概述

neigh_update函数用来更新指定的邻居项,更新内容是硬件地址和状态,更新之后,会根据新状态设置其输出函数,CONNECTED状态则使用快速输出,否则使用慢速输出;如果是由原来的无效状态变为现在的有效状态,则需要将数据包缓存队列中的数据包发送出去;

该函数在邻居子系统中被频繁调用;arp模块再收到邻居应答,收到邻居的情况,转发单播代理请求后,会调用该函数更新地址和状态;netlink或者ioctl模块添加或者删除邻居项,也会调用该函数更新地址和状态;

源码分析
 /* 更新指定的邻居项,更新内容为硬件地址和状态,新状态有效,并且有缓存包,则发送 */
int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
u32 flags, u32 nlmsg_pid)
{
u8 old;
int err;
int notify = ;
struct net_device *dev;
int update_isrouter = ; 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 (neigh->dead)
goto out; /* 新状态不是有效状态 */
if (!(new & NUD_VALID)) { /* 删除定时器 */
neigh_del_timer(neigh); /* 原状态是已连接状态,更新输出函数 */
if (old & NUD_CONNECTED)
neigh_suspect(neigh);
/* 设置状态 */
neigh->nud_state = new;
err = ;
notify = old & NUD_VALID; /* 原状态为INCOMPLETE或者PROBE,新状态为失败状态 */
if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
(new & NUD_FAILED)) {
/* 清空缓存包队列 */
neigh_invalidate(neigh);
notify = ;
}
goto out;
} /* 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 entry was valid and address is not changed,
do not change entry state, if new one is STALE.
*/
err = ;
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 = ;
/* 有UPDATE_F_WEAK_OVERRIDE状态 && 原状态是连接状态 */
if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) &&
(old & NUD_CONNECTED)) {
/* 更新硬件地址为邻居项地址 */
lladdr = neigh->ha;
/* 更新状态为STALE */
new = NUD_STALE;
} else
goto out;
}
/* 地址相同或者有UPDATE_F_OVERRIDE标记 */
else {
/* 地址相同&&新状态为STALE&&不是管理员更新,新状态设置为原状态 */
if (lladdr == neigh->ha && new == NUD_STALE &&
!(flags & NEIGH_UPDATE_F_ADMIN))
new = old;
}
} /* Update timestamps only once we know we will make a change to the
* neighbour entry. Otherwise we risk to move the locktime window with
* noop updates and ignore relevant ARP updates.
*/
/* 新旧状态不同或新旧地址不同 */
if (new != old || lladdr != neigh->ha) {
/* 新状态是连接状态,更新确认时间 */
if (new & NUD_CONNECTED)
neigh->confirmed = jiffies;
/* 更新更新时间 */
neigh->updated = jiffies;
} /* 新旧状态不同 */
if (new != old) {
/* 删除定时器 */
neigh_del_timer(neigh); /* 新状态为PROBE,设置未接受到应答计数为0 */
if (new & NUD_PROBE)
atomic_set(&neigh->probes, ); /* 新状态需要定时器,则添加 */
if (new & NUD_IN_TIMER)
neigh_add_timer(neigh, (jiffies +
((new & NUD_REACHABLE) ?
neigh->parms->reachable_time :
)));
/* 设置新状态 */
neigh->nud_state = new;
notify = ;
} /* 新旧地址不同 */
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_VAR(neigh->parms, BASE_REACHABLE_TIME) << );
notify = ;
} /* 新旧状态相同 */
if (new == old)
goto out; /* 新状态为CONNECTED,更新输出函数为connected_out */
if (new & NUD_CONNECTED)
neigh_connect(neigh);
/* 否则,输出函数为output */
else
neigh_suspect(neigh); /* 原状态无效,新状态有效 */
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 = ;
}
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, nlmsg_pid); return err;
}

邻居子系统 之 更新neigh_update的更多相关文章

  1. 邻居子系统 之 状态定时器回调neigh_timer_handler

    概述 在分配邻居子系统之后,会设置定时器来处理那些需要定时器处理的状态,定时器回调函数为neigh_timer_handler:函数会根据状态机变换规则对状态进行切换,切换状态后,如果需要更新输出函数 ...

  2. 邻居子系统 之 邻居表的初始化neigh_table_init

    概述 邻居子系统支持多种实现,例如ARP,ND等,这些实现需要在其初始化的时候,调用neigh_table_init将邻居表项添加到全局邻居子系统数组中,并对实例中的字段(如hash,定时器等)进行相 ...

  3. 邻居子系统输出 之 neigh_output、neigh_hh_output

    概述 ip层在构造好ip头,检查完分片之后,会调用邻居子系统的输出函数neigh_output进行输出,输出分为有二层头缓存和没有两种情况,有缓存时调用neigh_hh_output进行快速输出,没有 ...

  4. 《深入理解Linux网络技术内幕》阅读笔记 --- 邻居子系统

    1.封包从L3至L2的传送过程如下所示: 本地主机的路由子系统选择L3目的地址(下一个跃点). 根据路由表,如果下一个跃点在同一个网络中,邻居层就把目的L3地址解析为跃点的L2地址.这个关联会被放入缓 ...

  5. 邻居子系统 之 邻居项创建__neigh_create

    概述 IP层输出数据包会根据路由的下一跳查询邻居项,如果不存在则会调用__neigh_create创建邻居项,然后调用邻居项的output函数进行输出: __neigh_create完成邻居项的创建, ...

  6. 邻居子系统 之 邻居项查找neigh_lookup、___neigh_lookup_noref

    概述 邻居项查找是通过neigh_lookup相关函数来进行的: ___neigh_lookup_noref,该函数根据输出设备和主键值(IPv4为下一跳ip地址)在邻居项hash表中查找,找到则返回 ...

  7. 邻居子系统1.5 neigh output

    1.5.1 当邻居项不处于NUD_CONNECTD状态时,不允许快速路径发送报文,函数neigh_resolve_output 用于慢而安全的输出,通常用初始化neigh_ops结构 来实例outpu ...

  8. 邻居子系统 arp 状态图

  9. 网络子系统48_ip协议数据帧的发送

    //ip协议与l4协议接口,l4通过此接口向下l3传递数据帧 //函数主要任务: // 1.通过路由子系统路由封包 // 2.填充l3报头 // 3.ip分片 // 4.计算校验和 // 5.衔接邻居 ...

随机推荐

  1. MySQL学习笔记:count(1)、count(*)、count(字段)的区别

    关于数据库中行数统计,无论是MySQL还是Oracle,都有一个函数可以使用,那就是COUNT. 但是,就是这个常用的COUNT函数,却暗藏着很多玄机,尤其是在面试的时候,一不小心就会被虐.不信的话请 ...

  2. Linux--环境变量配置文件

    Linux系统中环境变量配置文件分为两类: 全局环境变量配置文件 /etc/profile 用户环境变量配置文件 ~/.bash_profile . ~/.bash_login ~/.profile ...

  3. js页面加载时候的调用函数的方法

    方法一:jquery 中:$(function(){}) 括号内写你的内容 方法二:html <body onload=''> <script type="text/jav ...

  4. css and canvas实现圆形进度条

    进度条效果:   话不多说,上代码 使用css动画实现,看到一篇博客的启发,稍微修改了下, css实现的原理是用两个半圆一开始隐藏,再分别旋转180度,最后成为一个整圆 半圆效果,一开始右边的半圆在盒 ...

  5. 第四篇.python的基础

    目录 第四篇.python基础01 1. 变量 2. 常量 3. python变量内存管理 4. 变量的三个特征 5. 花式赋值 6. 注释 7. 数据类型基础 8. 数字类型 9. 字符串类型 10 ...

  6. python之初入Python

    python优缺点: Python的优点很多,简单的可以总结为以下几点. 简单和明确,做一件事只有一种方法. 学习曲线低,跟其他很多语言相比,Python更容易上手. 开放源代码,拥有强大的社区和生态 ...

  7. NETGEAR 系列路由器命令执行漏洞简析

    NETGEAR 系列路由器命令执行漏洞简析 2016年12月7日,国外网站exploit-db上爆出一个关于NETGEAR R7000路由器的命令注入漏洞.一时间,各路人马开始忙碌起来.厂商忙于声明和 ...

  8. pip命令及虚拟环境的建立

    以下命令是pip命令,是帮助我们安装解决python所需要的环境包 列出已经安装的包 pip list 安装要安装的包 pip install 包名 安装特定版本 pip install django ...

  9. QTP(3)

    Test3001_Flight4a_手动启动录制购票 (VBS脚本) Window("Flight Reservation").WinObject("Date of Fl ...

  10. BZOJ 1005 prufer序列

    给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di ...