Linux邻居协议 学习笔记 之六 arp协议处理初始化
上面分析完了通用邻居层的架构以及代码处理,下面分析ipv4的邻居协议arp。对于linux邻居协议层,我认为通用邻居层是最重要的实现,arp协议层的处理,主要是涉及三个方面:1、处理arp请求、应答,并创建相应的邻居项 2、发送arp请求,并创建相应的邻居项 3、处理应用层通过ioctl创建或者删除邻居项的请求。它们最终都好调用通用邻居层的函数。 1、arp协议的格式
上面分析完了通用邻居层的架构以及代码处理,下面分析ipv4的邻居协议arp。对于linux邻居协议层,我认为通用邻居层是最重要的实现,arp协议层的处理,主要是涉及三个方面:1、处理arp请求、应答,并创建相应的邻居项 2、发送arp请求,并创建相应的邻居项 3、处理应用层通过ioctl创建或者删除邻居项的请求。它们最终都好调用通用邻居层的函数。
1、arp协议的格式
Linux内核中arp相关的数据结构:
struct arphdr {
__be16 ar_hrd; /* format of hardware address */
__be16 ar_pro; /* format of protocol address */
unsignedchar ar_hln; /* length of hardware address */
unsignedchar ar_pln; /* length of protocol address */
__be16 ar_op; /* ARP opcode (command) */
#if 0
/*
* Ethernet looks like this : This bit is variable sized however...
*/
unsignedchar ar_sha[ETH_ALEN]; /* sender hardware address */
unsignedchar ar_sip[4]; /* sender IP address */
unsignedchar ar_tha[ETH_ALEN]; /* target hardware address */
unsignedchar ar_tip[4]; /* target IP address */
#endif
};
其中ar_op对应的操作码有:
/* ARP protocol opcodes. */
#define ARPOP_REQUEST 1 /*ARP request */
#define ARPOP_REPLY 2 /*ARP reply */
#define ARPOP_RREQUEST 3 /*RARP request */
#define ARPOP_RREPLY 4 /*RARP reply */
#define ARPOP_InREQUEST 8 /*InARP request */
#define ARPOP_InREPLY 9 /*InARP reply */
#define ARPOP_NAK 10 /*(ATM)ARP NAK */
因为我们目前只使用arp进行地址解析,所以在arp_rcv中只处理arp_request与arp_reply这两种操作码。
2、arp协议的初始化
a) 使用neigh_table_init初始化arp协议对应的邻居表arp_tbl
b) 为arp协议注册协议处理函数arp_rcv对于网络设备驱动处理完的数据,会由函数netif_receive_skb继续二、三层协议的处理。对于进入netif_receive_skb的数据包,如果桥接数据没有进行处理,则会遍历ptype_basehash数组中的每一个hash表中的所有已注册的协议处理函数,查找与skb数据包相同的协议处理函数,对于arp数据包来说,就会通过deliver_skb,调用到函数arp_rcv进行arp数据包的处理
c) 向proc文件系统中注册arp相关的proc文件
d) 向netdev_chain通知链中注册arp的事件通知函数,主要是处理二层地址改变的事件
void __init arp_init(void)
{
neigh_table_init(&arp_tbl);
dev_add_pack(&arp_packet_type);
/**/
arp_proc_init();
#ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL,&arp_tbl.parms, "ipv4", NULL);
#endif
register_netdevice_notifier(&arp_netdev_notifier);
}
由于上次没有分析邻居项初始化的函数,这次继续分析下函数neigh_table_init
/*
功能:初始化邻居表
1、调用neigh_table_init_no_netlink初始化邻居表项的成员值
2、将该邻居表添加到邻居表链表neigh_tables中
*/
void neigh_table_init(struct neigh_table*tbl)
{
structneigh_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;
}
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();
}
}
我们接着分析neigh_table_init_no_netlink
/*
1、设置邻居表的reachable_time
2、为该邻居表申请slab缓存,用于创建邻居项
3、为邻居表的邻居项hash数组申请缓存
4、创建一个带有延迟功能的工作队列,用于进行邻居项的垃圾回收
*/
void neigh_table_init_no_netlink(structneigh_table *tbl)
{
unsignedlong now = jiffies;
unsignedlong phsize;
write_pnet(&tbl->parms.net,&init_net);
atomic_set(&tbl->parms.refcnt,1);
tbl->parms.reachable_time=
neigh_rand_reach_time(tbl->parms.base_reachable_time);
if(!tbl->kmem_cachep)
tbl->kmem_cachep=
kmem_cache_create(tbl->id,tbl->entry_size, 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
tbl->stats= alloc_percpu(struct neigh_statistics);
if(!tbl->stats)
panic("cannotcreate neighbour cache statistics");
#ifdef CONFIG_PROC_FS
if(!proc_create_data(tbl->id, 0, init_net.proc_net_stat,
&neigh_stat_seq_fops, tbl))
panic("cannotcreate neighbour proc dir entry");
#endif
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("cannotallocate neighbour cache hashes");
get_random_bytes(&tbl->hash_rnd,sizeof(tbl->hash_rnd));
rwlock_init(&tbl->lock);
/*创建带有延迟功能的工作队列,并将创建的工作添加
到工作队列keventd_wq中去,并开启一个定时器延迟tbl->parms.reachable_time
后,调用queue_work执行函数neigh_periodic_work
*/
INIT_DELAYED_WORK_DEFERRABLE(&tbl->gc_work,neigh_periodic_work);
schedule_delayed_work(&tbl->gc_work,tbl->parms.reachable_time);
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;
}
3、arp_table表项的初始化
struct neigh_table arp_tbl = {
.family= AF_INET,
.entry_size= sizeof(struct neighbour) + 4,//这里的大小为什么要加4呢?因为neighbour中的最后一个零数组成员是指向ipv4地址的
.key_len= 4,//ipv4 地址长度
.hash= arp_hash,//arp 协议的hash函数
.constructor= arp_constructor,//邻居表现初始化函数,初始化neighbour项中与协议相关的成员
.proxy_redo= parp_redo,
.id= "arp_cache",
.parms= {
.tbl= &arp_tbl,
.base_reachable_time = 30 * HZ,
.retrans_time= 1 * HZ,
.gc_staletime= 60 * HZ,
.reachable_time= 30 * HZ,
.delay_probe_time= 5 * HZ,
.queue_len= 3,
.ucast_probes= 3,
.mcast_probes= 3,
.anycast_delay= 1 * HZ,
.proxy_delay= (8 * HZ) / 10,
.proxy_qlen= 64,
.locktime= 1 * HZ,
},
.gc_interval= 30 * HZ,
.gc_thresh1= 128,
.gc_thresh2= 512,
.gc_thresh3= 1024,
};
这个邻居表项设置了arp邻居表项的初始化处理函数的设置函数arp_constructor;邻居项异步垃圾回收启动相关的阀值设置gc_thresh1、gc_thresh2、gc_thresh3;一个邻居项发送arp request数据包的最大值;重传时间;邻居项状态转换相关的时间间隔值base_reachable_time、reachable_time、delay_probe_time;arp协议相关的hash函数。
4、arp协议相关的邻居项的初始化函数arp_constructor
/*
该函数用于arp协议中,初始化neighbour项中与arp协议相关的项
1、设置邻居项的状态
2、设置邻居项的ops指针
3、设置邻居项的output函数指针
*/
static int arp_constructor(struct neighbour*neigh)
{
__be32addr = *(__be32*)neigh->primary_key;
structnet_device *dev = neigh->dev;
structin_device *in_dev;
structneigh_parms *parms;
rcu_read_lock();
in_dev= __in_dev_get_rcu(dev);
if(in_dev == NULL) {
rcu_read_unlock();
return-EINVAL;
}
neigh->type= inet_addr_type(dev_net(dev), addr);
parms= in_dev->arp_parms;
__neigh_parms_put(neigh->parms);
neigh->parms= neigh_parms_clone(parms);
rcu_read_unlock();
/*对于以太网设备,其dev->header_ops为eth_header_ops*/
if(!dev->header_ops) {
neigh->nud_state= NUD_NOARP;
neigh->ops= &arp_direct_ops;
neigh->output= neigh->ops->queue_xmit;
}else {
/*Good devices (checked by reading texts, but only Ethernet is
tested)
ARPHRD_ETHER: (ethernet, apfddi)
ARPHRD_FDDI: (fddi)
ARPHRD_IEEE802: (tr)
ARPHRD_METRICOM: (strip)
ARPHRD_ARCNET:
etc. etc. etc.
ARPHRD_IPDDP will also work, if authorrepairs it.
I did not it, because this driver does notwork even
in old paradigm.
*/
#if 1
/*So... these "amateur" devices are hopeless.
The only thing, that I can say now:
It is very sad that we need to keep uglyobsolete
code to make them happy.
They should be moved to more reasonablestate, now
they use rebuild_header INSTEAD OFhard_start_xmit!!!
Besides that, they are sort of out of date
(a lot of redundant clones/copies, uselessin 2.1),
I wonder why people believe that they work.
*/
switch(dev->type) {
default:
break;
caseARPHRD_ROSE:
#if defined(CONFIG_AX25) ||defined(CONFIG_AX25_MODULE)
caseARPHRD_AX25:
#if defined(CONFIG_NETROM) ||defined(CONFIG_NETROM_MODULE)
caseARPHRD_NETROM:
#endif
neigh->ops= &arp_broken_ops;
neigh->output= neigh->ops->output;
return0;
#endif
;}
#endif
/*
1、对于组播类型的neighbour项,则将该邻居项的状态设置为NUD_NOARP
2、对于不需要arp的设备或者回环设备,将nud_state 设置为NUD_NOARP
3、对于广播类型或者点对点设备的邻居项,不需要arp
*/
if(neigh->type == RTN_MULTICAST) {
neigh->nud_state= NUD_NOARP;
arp_mc_map(addr,neigh->ha, dev, 1);
}else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
neigh->nud_state= NUD_NOARP;
memcpy(neigh->ha,dev->dev_addr, dev->addr_len);
}else if (neigh->type == RTN_BROADCAST || dev->flags&IFF_POINTOPOINT){
neigh->nud_state= NUD_NOARP;
memcpy(neigh->ha,dev->broadcast, dev->addr_len);
}
/*
如果设备的header_ops->cache存在,则将邻居项的ops设置为arp_hh_ops,
对于以太网设备,其header_ops->cache为eth_header_cache,所以对于以太网设备
其neighbour->ops为arp_hh_ops
*/
if(dev->header_ops->cache)
neigh->ops= &arp_hh_ops;
else
neigh->ops= &arp_generic_ops;
/*对于邻居项状态为有效状态时,则将neigh->output设置为neigh->ops->connected_output*/
if(neigh->nud_state&NUD_VALID)
neigh->output= neigh->ops->connected_output;
else
neigh->output= neigh->ops->output;
}
return0;
}
至此分析完了arp协议的初始化相关的处理流程,分析了arp协议函数与通用处理函数之间的调用关系
更多推荐
所有评论(0)