上面分析完了通用邻居层的架构以及代码处理,下面分析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协议函数与通用处理函数之间的调用关系

Logo

更多推荐