一 linux ARP cache状态图
各个状态的含义如下:
NUD_INCOMPLETE: ARP包已经发出,还没有收到回复
NUD_REACHABLE: ARP缓存有效,可达。
NUD_FAILED: 不可达,请求返回失败
NUD_STALE: 缓存存在,not confirmed for a certain amount of time
NUD_DELAY: 当NUD_STALE状态进行用户发包,进入此状态
NUD_PROBE: NUD_DELAY 查询包无包回应的时候,进入PROBE
二 ARP CACHE 状态机流程
最简单的就是表的从NONE到有,如果有回应就进入REACHABLE,如果无回应就FAILED,被回收掉。
比较复杂的是当REACHABLE 经过”not used for more than
reachable_time“后,就进入了STALE,这个状态比较复杂。下面重点介绍一下STALE状态。具体的linux
代码如下:
在一个arp表创建的时候启动如下的定时器:
static void neigh_timer_handler(unsigned long arg)
{
if (state & NUD_REACHABLE) {
if (time_before_eq(now,
neigh->confirmed +
neigh->parms->reachable_time)) {
NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
next = neigh->confirmed +
neigh->parms->reachable_time;
} else if (time_before_eq(now,
neigh->used +
neigh->parms->delay_probe_time)) {
NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
neigh->nud_state = NUD_DELAY;
neigh->updated = jiffies;
neigh_suspect(neigh);
next = now + neigh->parms->delay_probe_time;
} else {
NEIGH_PRINTK2("neigh %p is suspected.\n", neigh);
neigh->nud_state = NUD_STALE;
neigh->updated = jiffies;
neigh_suspect(neigh);
notify = 1;
}
在特殊的状态转换的时候,定时器被定时的开启。
#define NUD_IN_TIMER
(NUD_INCOMPLETE|NUD_REACHABLE|NUD_DELAY|NUD_PROBE)
if (neigh->nud_state & NUD_IN_TIMER) {
if (time_before(next, jiffies + HZ/2))
next = jiffies + HZ/2;
if (!mod_timer(&neigh->timer, next))
neigh_hold(neigh);
}
}
三 STALE状态机的转换
除了neigh_timer_handler
定时器,还有一个定时器neigh_periodic_timer,这个timer就是对于缓存gc的回收处理。
当STALE状态有发送的时候:
int __neigh_event_send(struct neighbour *neigh, struct sk_buff
*skb)
{
if (neigh->nud_state & NUD_STALE) {
NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
neigh_hold(neigh);
neigh->nud_state = NUD_DELAY;
neigh->updated = jiffies;
neigh_add_timer(neigh,
jiffies + neigh->parms->delay_probe_time);
}
}
static void neigh_periodic_timer(unsigned long arg)
{
if (time_after(now, tbl->last_rand + 300 * HZ)) {
struct neigh_parms *p;
tbl->last_rand = now;
for (p = &tbl->parms; p; p = p->next)
p->reachable_time =
neigh_rand_reach_time(p->base_reachable_time);
}
if (atomic_read(&n->refcnt) == 1 &&
(state ==
NUD_FAILED ||
time_after(now, n->used +
n->parms->gc_staletime))) {
*np = n->next;
n->dead = 1;
write_unlock(&n->lock);
if (n->parms->neigh_cleanup)
n->parms->neigh_cleanup(n);
neigh_release(n);
continue;
}
过期的时间,expire
根据base_reachable_time,当时间过期之后,就会执行上面的代码,将cache进行回收。
expire = tbl->parms.base_reachable_time >> 1;
expire /= (tbl->hash_mask + 1);
if (!expire)
expire = 1;
if (expire>HZ)
mod_timer(&tbl->gc_timer, round_jiffies(now + expire));
else
mod_timer(&tbl->gc_timer, now + expire);
}
四 ARP缓存老化时间
实际使用中,ARP的老化缓存时间。默认情况下reachable的持续时间的base_reachable_time,一般默认值是30s,等30s过后在发现没有包的交互后,相应的表状态转化为STALE,注意有一个gc_stale_time,这个就是STALE维持的时间,在gc_stale_time时间没有得到使用的话,就会被删除掉。具体在要经过多少时间删除此表呢。那就从neigh_periodic_timer在经过base_reachable_time时间的timer后,发现gc_stale_time的时间已经超过,那么就会将此表删除掉。