您的位置:首页 > 运维架构 > Linux

Linux邻居协议 学习笔记 之六 arp协议处理初始化

2014-04-02 23:43 549 查看
上面分析完了通用邻居层的架构以及代码处理,下面分析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协议函数与通用处理函数之间的调用关系
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: