邻居子系统(一)

邻居子系统负责将L3地址翻译成L2地址,设计上分为协议无关部分和协议相关部分,后续分析中,协议无关部分以IPv4协议族的ARP为例进行代码分析。

文件路径 描述
include/net/neighbour.h 协议无关部分的头文件
net/core/neighbour.c 协议无关部分实现文件
include/linux/arp.h arp协议头文件
net/ipv4/arp.c arp协议实现文件
include/linux/neighbour.h Netlink配置邻居子系统需要的数据结构

邻居协议的管理

邻居子系统框架将所有特定协议族的邻居协议组织成一个链表来维护。

// 系统中所有的邻居协议实例组织到该链表中
static struct neigh_table *neigh_tables;
// 该读写锁只保护neigh_tables的增加和删除,并不保护其中某个邻居协议实例的内容
static DEFINE_RWLOCK(neigh_tbl_lock);

注册邻居协议:neigh_table_init()

neigh_table_init()从名字上看仅仅像是对邻居协议进行初始化,实际上它还完成了邻居协议的注册。对该接口的使用示例参考arp_init()。

void neigh_table_init(struct neigh_table *tbl)
{
	struct neigh_table *tmp;
	// 初始化协议无关内容
	neigh_table_init_no_netlink(tbl);
	write_lock(&neigh_tbl_lock);
	// 检查该协议族是否已经注册了邻居协议
	for (tmp = neigh_tables; tmp; tmp = tmp->next) {
		if (tmp->family == tbl->family)
			break;
	}
	// 将新的邻居协议加入到全局链表neigh_tables的首部
	tbl->next = neigh_tables;
	neigh_tables = tbl;
	write_unlock(&neigh_tbl_lock);
	// 显然,每个协议族应该只注册一个邻居协议
	if (unlikely(tmp)) {
		printk(KERN_ERR "NEIGH: Registering multiple tables for " "family %d\n", tbl->family);
		dump_stack();
	}
}

邻居协议协议无关初始化

void neigh_table_init_no_netlink(struct neigh_table *tbl)
{
	unsigned long now = jiffies;
	unsigned long phsize;

	tbl->parms.net = &init_net;
	atomic_set(&tbl->parms.refcnt, 1);
	INIT_RCU_HEAD(&tbl->parms.rcu_head);
	tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time);

	// 创建高速缓存用于分配邻居项,每个邻居项的大小为entry_size,该值由各个邻居协议基于
	// struct neighboru扩展而来,对于arp协议,大小为sizeof(struct neighbour) + 4
	if (!tbl->kmem_cachep)
		tbl->kmem_cachep = kmem_cache_create(tbl->id, tbl->entry_size, 0,
			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
	// 邻居协议的统计信息,每个cpu一份
	tbl->stats = alloc_percpu(struct neigh_statistics);
	if (!tbl->stats)
		panic("cannot create neighbour cache statistics");

#ifdef CONFIG_PROC_FS
	// 创建/proc/net/stat/xxx_cache(对于IPv4是arp_cache,IPv6是ndisc_cache)文件,
	// 通过读该文件可以dump对应邻居协议的统计信息
	tbl->pde = proc_create(tbl->id, 0, init_net.proc_net_stat, &neigh_stat_seq_fops);
	if (!tbl->pde)
		panic("cannot create neighbour proc dir entry");
	tbl->pde->data = tbl;
#endif
	// 创建邻居项哈希表,初始大小为2,后面会根据需要进行扩充
	tbl->hash_mask = 1;
	tbl->hash_buckets = neigh_hash_alloc(tbl->hash_mask + 1);
	// 创建存储代理项的哈希表
	phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
	tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);

	if (!tbl->hash_buckets || !tbl->phash_buckets)
		panic("cannot allocate neighbour cache hashes");
	// 初始化邻居项哈希表的哈希算法参数
	get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd));

	rwlock_init(&tbl->lock);
	// 启动垃圾回收定时器
	setup_timer(&tbl->gc_timer, neigh_periodic_timer, (unsigned long)tbl);
	tbl->gc_timer.expires  = now + 1;
	add_timer(&tbl->gc_timer);
	// 启动处理代理的定时器
	setup_timer(&tbl->proxy_timer, neigh_proxy_process, (unsigned long)tbl);
	skb_queue_head_init_class(&tbl->proxy_queue, &neigh_table_proxy_queue_class);

	tbl->last_flush = now;
	tbl->last_rand	= now + tbl->parms.reachable_time * 20;
}

你可能感兴趣的:(邻居子系统)