邻居子系统 之 邻居项创建__neigh_create
概述
IP层输出数据包会根据路由的下一跳查询邻居项,如果不存在则会调用__neigh_create创建邻居项,然后调用邻居项的output函数进行输出;
__neigh_create完成邻居项的创建,进行初始化之后,加入到邻居项hash表,然后返回,其中,如果hash表中有与新建邻居项相同的项会复用该项,新建项会被释放;
neigh_alloc完成邻居项的分配,分配成功后会设定定时器来检查和更新邻居项的状态;
源码分析
struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
struct net_device *dev, bool want_ref)
{
u32 hash_val;
int key_len = tbl->key_len;
int error;
/* 分配邻居项n */
struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev);
struct neigh_hash_table *nht; if (!n) {
rc = ERR_PTR(-ENOBUFS);
goto out;
} /* 拷贝主键值,ipv4为下一跳ip地址 */
memcpy(n->primary_key, pkey, key_len);
/* 设置输出设备 */
n->dev = dev;
dev_hold(dev); /* Protocol specific setup. */
/* 执行邻居表的邻居项初始化函数,ARP为arp_constructor */
if (tbl->constructor && (error = tbl->constructor(n)) < ) {
rc = ERR_PTR(error);
goto out_neigh_release;
} /* 执行设备的邻居项初始化 */
if (dev->netdev_ops->ndo_neigh_construct) {
error = dev->netdev_ops->ndo_neigh_construct(dev, n);
if (error < ) {
rc = ERR_PTR(error);
goto out_neigh_release;
}
} /* Device specific setup. */
/* 老式设备的邻居项初始化 */
if (n->parms->neigh_setup &&
(error = n->parms->neigh_setup(n)) < ) {
rc = ERR_PTR(error);
goto out_neigh_release;
} /* 设置邻居项的确认时间 */
n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << ); write_lock_bh(&tbl->lock); /* 获取hash */
nht = rcu_dereference_protected(tbl->nht,
lockdep_is_held(&tbl->lock)); /* hash扩容 */
if (atomic_read(&tbl->entries) > ( << nht->hash_shift))
nht = neigh_hash_grow(tbl, nht->hash_shift + ); /* 计算hash值 */
hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> ( - nht->hash_shift); /* 邻居项的配置参数正在被删除,不能继续初始化 */
if (n->parms->dead) {
rc = ERR_PTR(-EINVAL);
goto out_tbl_unlock;
} /* 遍历hash */
for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val],
lockdep_is_held(&tbl->lock));
n1 != NULL;
n1 = rcu_dereference_protected(n1->next,
lockdep_is_held(&tbl->lock))) {
/* 找到相同的邻居项,则使用之 */
if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
if (want_ref)
neigh_hold(n1);
rc = n1;
/* 解锁,释放新的邻居项 */
goto out_tbl_unlock;
}
} /* 不存在,则添加新邻居项到hash */
n->dead = ;
if (want_ref)
neigh_hold(n);
rcu_assign_pointer(n->next,
rcu_dereference_protected(nht->hash_buckets[hash_val],
lockdep_is_held(&tbl->lock)));
rcu_assign_pointer(nht->hash_buckets[hash_val], n);
write_unlock_bh(&tbl->lock);
neigh_dbg(, "neigh %p is created\n", n); /* 返回新的邻居项 */
rc = n;
out:
return rc;
out_tbl_unlock:
write_unlock_bh(&tbl->lock);
out_neigh_release:
neigh_release(n);
goto out;
}
static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev)
{
struct neighbour *n = NULL;
unsigned long now = jiffies;
int entries; /* 增加并返回邻居项数量 */
entries = atomic_inc_return(&tbl->entries) - ; /* 超过限额,则进行回收 */
if (entries >= tbl->gc_thresh3 ||
(entries >= tbl->gc_thresh2 &&
time_after(now, tbl->last_flush + * HZ))) {
if (!neigh_forced_gc(tbl) &&
entries >= tbl->gc_thresh3) {
net_info_ratelimited("%s: neighbor table overflow!\n",
tbl->id);
NEIGH_CACHE_STAT_INC(tbl, table_fulls);
goto out_entries;
}
} /* 分配邻居项 */
n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC);
if (!n)
goto out_entries; /* 成员初始化 */
__skb_queue_head_init(&n->arp_queue);
rwlock_init(&n->lock);
seqlock_init(&n->ha_lock);
n->updated = n->used = now;
n->nud_state = NUD_NONE;
n->output = neigh_blackhole;
seqlock_init(&n->hh.hh_lock);
n->parms = neigh_parms_clone(&tbl->parms); /* 设置定时器,检查和调整邻居项状态 */
setup_timer(&n->timer, neigh_timer_handler, (unsigned long)n); NEIGH_CACHE_STAT_INC(tbl, allocs); /* 关联邻居表 */
n->tbl = tbl;
atomic_set(&n->refcnt, );
n->dead = ;
out:
return n; out_entries:
atomic_dec(&tbl->entries);
goto out;
}
邻居子系统 之 邻居项创建__neigh_create的更多相关文章
- 邻居子系统 之 邻居表的初始化neigh_table_init
概述 邻居子系统支持多种实现,例如ARP,ND等,这些实现需要在其初始化的时候,调用neigh_table_init将邻居表项添加到全局邻居子系统数组中,并对实例中的字段(如hash,定时器等)进行相 ...
- 邻居子系统 之 邻居项查找neigh_lookup、___neigh_lookup_noref
概述 邻居项查找是通过neigh_lookup相关函数来进行的: ___neigh_lookup_noref,该函数根据输出设备和主键值(IPv4为下一跳ip地址)在邻居项hash表中查找,找到则返回 ...
- 邻居子系统 之 状态定时器回调neigh_timer_handler
概述 在分配邻居子系统之后,会设置定时器来处理那些需要定时器处理的状态,定时器回调函数为neigh_timer_handler:函数会根据状态机变换规则对状态进行切换,切换状态后,如果需要更新输出函数 ...
- 邻居子系统 之 更新neigh_update
概述 neigh_update函数用来更新指定的邻居项,更新内容是硬件地址和状态,更新之后,会根据新状态设置其输出函数,CONNECTED状态则使用快速输出,否则使用慢速输出:如果是由原来的无效状态变 ...
- 邻居子系统输出 之 neigh_output、neigh_hh_output
概述 ip层在构造好ip头,检查完分片之后,会调用邻居子系统的输出函数neigh_output进行输出,输出分为有二层头缓存和没有两种情况,有缓存时调用neigh_hh_output进行快速输出,没有 ...
- ubuntu 启动项创建器 选择不了CD镜像,IOS镜像的解决方法
自己系统是ubuntu14.04 , 想使用 ubuntu自带的启动项创建器(usb-creator-gtk)做一个CDLinux的U盘启动项, 打开程序后发现U盘识别了, 在添加镜像的时候,发现怎么 ...
- 自学Zabbix4.2 web监控项创建+item详解
自学Zabbix4.2 web监控项创建+item详解 1. web监控项创建 1.1 Scenario 选项卡 Name: 监控项的名称 Application: 放到哪个应用中 Authenti ...
- 《深入理解Linux网络技术内幕》阅读笔记 --- 邻居子系统
1.封包从L3至L2的传送过程如下所示: 本地主机的路由子系统选择L3目的地址(下一个跃点). 根据路由表,如果下一个跃点在同一个网络中,邻居层就把目的L3地址解析为跃点的L2地址.这个关联会被放入缓 ...
- 邻居子系统1.5 neigh output
1.5.1 当邻居项不处于NUD_CONNECTD状态时,不允许快速路径发送报文,函数neigh_resolve_output 用于慢而安全的输出,通常用初始化neigh_ops结构 来实例outpu ...
随机推荐
- Java 实现 海康摄像头抓拍图像 Windows、Linux
先抱怨一下,打死都想不到,海康的摄像头SDK居然是一个Java类,还有必须的两个jar包(jna.jar,examples.jar).鬼能想得到会这么命名. 下面开始吧. Windows 把从官网下载 ...
- Unknown column '字段名' in 'field list' 错误解决方案
当后台报这个错误的时候,说明实体类中的字段名出错了,解决方案: 1,检查数据库中的字段名与实体类中的字段名是否一致,特别要注意单词字母, 2,检查数据库中的字段是否与实体类中的字段一致.比如数据库中没 ...
- mysql中取出的时间日期多个.0
字段名称为 create_time 字段类型为 datetime 存储的内容为 2019-03-26 09:42:05 但是 通过mybatis取出来放到实体类里,数值就变成了 2019-03-26 ...
- python 语言
认识程序 程序时根据语言提供的指令按照一定的逻辑顺序对获得的数据进行运算,并最终返回给我们的指令和数据的组合. 低级语言有时叫机器语言或者汇编语言,在我们看来是一串0和1组成的二进制数字,这些数字代表 ...
- 怎么处理Win7电脑打开软件速度慢的情况?
很多使用Win7系统的用户都会发现这么一个问题,就是电脑在使用过一段时间后,打开一个应用软件的速度就会变慢,非常耽误时间.下面就和大家分享一个解决Win7系统应用软件打开速度慢的小技巧. Win7系统 ...
- 注解【Annotation】、反射
注解:Annotation是从JDK5.0开始引入的新技术.Annotation的作用:如果没有注解信息处理流程,则注解毫无意义)- 不是程序本身,可以对程序作出解释.(这一点,跟注释没什么区别)- ...
- Linux系统组成和获取命令帮助2
基于cobbler进行网络安装: https://cobbler.github.io/ 终端:terminal 无论是系统的图形界面还是文字界面,都可以叫做控制台,终端 ...
- oracle学习1 基于oracle数据库的PLSQL编程以及存储过程的创建和使用视频
https://www.bilibili.com/video/av46777605 plsql中选择testWindow中可以进行测试 1.编写函数在plsql的testwindow中 begin d ...
- Android异常与性能优化相关面试问题-冷启动优化面试问题详解
什么是冷启动: 冷启动的定义:冷启动就是在启动应用前,系统中没有该应用的任何进程信息.实际也就是要执行Application.onCreate()方法的那次启动. 冷启动 / 热启动的区别:热启动:用 ...
- top命令经常用来监控linux的系统状况,比如cpu、内存的使用,程序员基本都知道这个命令。 按 q 退出
top命令经常用来监控linux的系统状况,比如cpu.内存的使用,程序员基本都知道这个命令. 按 q 退出