1.3.1邻居系统状态图(老外给的解释)
NUD_VALID : An entry is considered to be in the NUD_VALID state if its state is any one of the following, which
represent neighbors believed to have an available address:
NUD_PERMANENT NUD_NOARP NUD_REACHABLE NUD_PROBE NUD_STALE NUD_DELAY
NUD_CONNECTED : This is used for the subset of NUD_VALID states that do not have a confirmation process pending:
NUD_PERMANENT NUD_NOARP NUD_REACHABLE
NUD_IN_TIMER : The neighboring subsystem is running a timer for this entry, which happens when the status is
unclear. The basic states that correspond to this are:
NUD_INCOMPLETE NUD_DELAY NUD_PROBE
1.3.2 邻居表项的查找
邻居项查找是通过neigh_lookup相关函数来进行的;该函数根据输出设备和主键值(IPv4为目的ip地址)在邻居项hash表中查找,
并且在找到邻居项之后,进行引用计数的递增,然后返回该项;
struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
struct net_device *dev)
{
struct neighbour *n;
int key_len = tbl->key_len;
u32 hash_val;
struct neigh_hash_table *nht;
NEIGH_CACHE_STAT_INC(tbl, lookups);
rcu_read_lock_bh();
nht = rcu_dereference_bh(tbl->nht);/* 获取hash */
/* 计算hash值 */
hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift);/* 计算hash值 */
/* 遍历hash表项 */
for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
n != NULL;
n = rcu_dereference_bh(n->next)) { /* 找到则返回该项 */
if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
if (!atomic_inc_not_zero(&n->refcnt))
n = NULL;
NEIGH_CACHE_STAT_INC(tbl, hits);
break;
}
}
rcu_read_unlock_bh();
return n;
}
1.3.3 邻居状态更新
下面我们分析一下函数neigh_update:
该函数的功能:邻居项的更新,主要是更新二层地址与邻居项的状态,并会 根据邻居项的状态,选择相对应的输出函数
1、判断输入二层地址,判断是否需要覆盖邻居项的二层地址
2、判断邻居项状态的改变是否合法
3、根据不同的邻居项状态设置不同的邻居项输出函数,并设置与该邻居项关联的所有二层缓存头部
该函数被调用的情形有:
1、当接收到邻居项的应答报文后,则会调用该函数更新二层地址和状态为CONNECT
2、当接收到邻居项的请求报文后,则会调用该函数将邻居项的状态设置为STALE
3、处理通过ioctl或者netlink执行的邻居项的添加、删除邻居项时,也会调用该函数
更新邻居项的状态与二层地址
/* Generic update routine.
-- lladdr is new lladdr or NULL, if it is not supplied.
-- new is new state.
-- flags
NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr,
if it is different.
NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected"
lladdr instead of overriding it
if it is different.
It also allows to retain current state
if lladdr is unchanged.
NEIGH_UPDATE_F_ADMIN means that the change is administrative.
NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing
NTF_ROUTER flag.
NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as
a router.
Caller MUST hold reference count on the entry.
*/
/* 更新指定的邻居项,更新内容为硬件地址和状态,如果新状态有效,并且有缓存包,则发送 */
int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
u32 flags)
{
u8 old;
int err;
int notify = 0;
struct net_device *dev;
int update_isrouter = 0;
static unsigned long log_timeout = 0;
int i;
write_lock_bh(&neigh->lock);
dev = neigh->dev;
old = neigh->nud_state;
err = -EPERM;
/* 原状态是NOARP或者PERMANENT,必须要求是用户管理员发生的更新 */
if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
(old & (NUD_NOARP | NUD_PERMANENT)))
goto out;
/* 新状态不是有效状态 */
if (!(new & NUD_VALID)) {
neigh_del_timer(neigh);/* 删除定时器 */
if (old & NUD_CONNECTED) /* 原状态是已连接状态,更新输出函数 */
neigh_suspect(neigh);
neigh->nud_state = new;/* 设置状态 */
err = 0;
notify = old & NUD_VALID;
NEIGH_PRINTK2("%s,%d old 0x%x, new 0x%x flags 0x%x\n", __FUNCTION__, __LINE__, old, new, flags);
/* 原状态为INCOMPLETE或者PROBE,新状态为失败状态 */ 或者old为admin用户下的等xx状态 则清空缓存队列
if ((((old & (NUD_INCOMPLETE | NUD_PROBE | NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_FAILED)) && (flags & NEIGH_UPDATE_F_ADMIN))
|| (old & (NUD_INCOMPLETE | NUD_PROBE)))&&
(new & NUD_FAILED)) { /* 清空缓存包队列 */
neigh_invalidate(neigh);// neigh_invalidate发送错误报告,并发送通知信息,函数返回
notify = 1;
}
goto out;
}
/*
1、对于设备二层地址长度为0的情形,则不需要更新二层地址,直接使用neigh->ha
2、原状态为有效的,且要更改的地址与邻居项存储的地址相同,则无需更改
3、原状态为无效,且要更改的地址也是无效,则是逻辑错误,函数直接 返回
4、原状态有效,且要更改的地址无效时,则先将地址设置为邻居项的地址=
5、其他情况下不更改传进来的二层地址。
即:
原状态有效,且修改的地址与原邻居项地址不同
原状态无效,且修改的地址有效时
*/
/* Compare new lladdr with cached one */
if (!dev->addr_len) {
/* First case: device needs no address. */
lladdr = neigh->ha;
} else if (lladdr) {
/* The second case: if something is already cached
and a new address is proposed:
- compare new & old
- if they are different, check override flag
*/
if ((old & NUD_VALID) &&
!memcmp(lladdr, neigh->ha, dev->addr_len))
lladdr = neigh->ha;
} else {
/* No address is supplied; if we know something,
use it, otherwise discard the request.
*/
err = -EINVAL;
if (!(old & NUD_VALID))
goto out;
lladdr = neigh->ha;
}
if (new & NUD_CONNECTED)
neigh->confirmed = jiffies;
neigh->updated = jiffies;
/* If entry was valid and address is not changed,
do not change entry state, if new one is STALE.
*/
err = 0;
update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
if (old & NUD_VALID) { /* 原状态有效 */ /* 地址不同 && 无UPDATE_F_OVERRIDE标记 */
if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) {
update_isrouter = 0;
if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) &&
(old & NUD_CONNECTED)) { /* 有UPDATE_F_WEAK_OVERRIDE状态 && 原状态是连接状态 */
lladdr = neigh->ha; /* 更新硬件地址为邻居项地址 */
new = NUD_STALE;
} else
goto out;
} else { /* 地址相同或者有UPDATE_F_OVERRIDE标记 */
if (lladdr == neigh->ha && new == NUD_STALE &&
((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) ||
(old & NUD_CONNECTED))
)
new = old;
}
}
if (new != old) {
if (new & NUD_REACHABLE)
{
notify = 1;
}
neigh_del_timer(neigh); /* 删除定时器 */
if (new & NUD_IN_TIMER) /* 新状态需要定时器,则添加 */
neigh_add_timer(neigh, (jiffies +
((new & NUD_REACHABLE) ?
neigh->parms->reachable_time :
0)));
neigh->nud_state = new; /* 设置新状态 */
}
/*
如果邻居项的二层地址不同,则更新邻居项里的二层地址,并
调用neigh_update_hhs,更新与该邻居项相关联的所有二层头部缓存。
如果新状态不是CONNECT状态,则将confirm时间设置为比当前时间早2*base_reachable_time.根据邻居项的不同更新邻居项的输出函数:
当为NUD_CONNECTED,则调用neigh_connect将邻居项的输出函数设置为快速输出函数
当为非NUD_CONNECTED,则调用neigh_suspect将邻居项的输出函数设置为通用输出函数
*/
if (lladdr != neigh->ha) { /* 新旧状态不同或新旧地址不同 */
write_seqlock(&neigh->ha_lock);
memcpy(&neigh->ha, lladdr, dev->addr_len); /* 拷贝新地址 */
write_sequnlock(&neigh->ha_lock);
neigh_update_hhs(neigh);
if (!(new & NUD_CONNECTED)) /* 新状态不是连接状态,更新确认时间 */
neigh->confirmed = jiffies -
(neigh->parms->base_reachable_time << 1);
notify = 1;
}
if (new == old)
goto out;
if (new & NUD_CONNECTED) /* 新状态为CONNECTED,更新输出函数为connected_out */
neigh_connect(neigh);
else
neigh_suspect(neigh); /* 否则,输出函数为output */
if (!(old & NUD_VALID)) { /* 原状态无效,新状态有效 */
struct sk_buff *skb;
/* Again: avoid dead loop if something went wrong */
while (neigh->nud_state & NUD_VALID && /* 新状态有效,缓存队列不为空 */
(skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
struct dst_entry *dst = skb_dst(skb);
struct neighbour *n2, *n1 = neigh;
write_unlock_bh(&neigh->lock);
rcu_read_lock();
/* Why not just use 'neigh' as-is? The problem is that
* things such as shaper, eql, and sch_teql can end up
* using alternative, different, neigh objects to output
* the packet in the output path. So what we need to do
* here is re-lookup the top-level neigh in the path so
* we can reinject the packet there.
*/
n2 = NULL;
if (dst) { /* 有路由缓存,则根据路由缓存获取邻居项,有则替换 */
n2 = dst_neigh_lookup_skb(dst, skb);
if (n2)
n1 = n2;
}
n1->output(n1, skb); /* 输出数据包 */
if (n2) /* 是否引用的邻居项 */
neigh_release(n2);
rcu_read_unlock();
write_lock_bh(&neigh->lock);
}
skb_queue_purge(&neigh->arp_queue); /* 清空数据包缓存队列 */
neigh->arp_queue_len_bytes = 0;
}
out:
if (update_isrouter) {
neigh->flags = (flags & NEIGH_UPDATE_F_ISROUTER) ?
(neigh->flags | NTF_ROUTER) :
(neigh->flags & ~NTF_ROUTER);
}
write_unlock_bh(&neigh->lock);
if (notify) /* 通知其他关心的模块 */
neigh_update_notify(neigh);
return err;
}
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子