Linux邻居协议 学习笔记 之二 通用邻居处理函数对应的数据结构的分析

来源:互联网 发布:qq群排名优化助手软件 编辑:程序博客网 时间:2024/04/29 16:57


 

       在linux代码中,对于不同的邻居项,抽象出了一个通用的模型,通用邻居层,主要是用来进行邻居项的创建、添加、删除、查找、更新等操作。

       对于通用邻居层,最主要的就是邻居项的状态机的设计,本部分先介绍相应的数据结构,在分析通用邻居处理函数时,会仔细分析邻居状态机。本部分会简要介绍状态机。

 

 

首先是邻居状态的定义,在通用邻居项中定义了以下邻居状态:

 

#defineNUD_INCOMPLETE      0x01

#defineNUD_REACHABLE 0x02

#defineNUD_STALE    0x04

#defineNUD_DELAY   0x08

#defineNUD_PROBE   0x10

#defineNUD_FAILED  0x20

 

/* Dummy states*/

#defineNUD_NOARP   0x40

#defineNUD_PERMANENT       0x80

#defineNUD_NONE     0x00

 

其中NUD_NOARP、NUD_PERMANENT、NUD_NONE表示该邻居项不需要邻居地址的解析。

 

 

下面我们介绍NUD_IN_TIMER、NUD_VALID、NUD_CONNECTED

 #define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_REACHABLE|NUD_DELAY|NUD_PROBE)
#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
#define NUD_CONNECTED (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)

 

对于NUD_IN_TIMER,通过名称我们就知道,当邻居项处于该状态时,则会启动定时器。下面我们一一分析这几个邻居项状态,通过分析完这几个状态,我们就基本上会理解邻居项状态机中定时器处理函数neigh_timer_handler的设计逻辑了。

1、对于NUD_INCOMPLETE,当本机发送完arp 请求包后,还未收到应答时,即会进入该状态。进入该状态,即会启动定时器,如果在定时器到期后,还没有收到应答时:如果没有到达最大发包上限时,即会重新进行发送请求报文;如果超过最大发包上限还没有收到应答,则会将状态设置为failed

2、对于收到可到达性确认后,即会进入NUD_REACHABLE,当进入NUD_REACHABLE状态。当进入NUD_REACHABLE后,即会启动一个定时器,当定时器到时前,该邻居协议没有

被使用过,就会将邻居项的状态转换为NUD_STALE

3、对于进入NUD_STALE状态的邻居项,即会启动一个定时器。如果在定时器到时前,有数据需要发送,则直接将数据包发送出去,并将状态设置为NUD_DELAY;如果在定时器到时,没有数据需要发送,且该邻居项的引用计数为1,则会通过垃圾回收机制,释放该邻居项对应的缓存

4、处于NUD_DELAY状态的邻居项,如果在定时器到时后,没有收到可到达性确认,则会进入NUD_PROBE状态;如果在定时器到达之前,收到可到达性确认,则会进入NUD_REACHABLE (在该状态下的邻居项不会发送solicit请求,而只是等待可到达性应答。主要包括对以前的solicit请求的应答或者收到一个对于本设备以前发送的一个数据包的应答)

5、处于NUD_PROBE状态的邻居项,会发送arp solicit请求,并启动一个定时器。如果在定时器到时前,收到可到达性确认,则进入NUD_REACHABLE;如果在定时器到时后,没有收到可到达性确认:

       a)没有超过最大发包次数时,则继续发送solicit请求,并启动定时器

       b)如果超过最大发包次数,则将邻居项状态设置为failed

 

 

在上面5个状态中,在NUD_REACHABLE、NUD_PROBE、NUD_STALE、NUD_DELAY状态时,数据包是可以正常发送的,只是发送的函数不同。这样就不难理解NUD_VALID包含NUD_PERMANENT、NUD_NOARP、NUD_REACHABLE、NUD_PROBE、NUD_STALE、NUD_DELAY了

 

 

对于#defineNUD_CONNECTED       (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)

主要是表示邻居是可达的状态,对于NUD_PERMANENT、NUD_NOARP状态的邻居项,其邻居状态是不会改变的,一直是有效的,除非删除该邻居项。对于NUD_REACHABLE我们在上面已经介绍过了。

 

 

下面介绍neigh_parms、neighbour、neigh_ops、neigh_table

 

struct neigh_parms {

#ifdef CONFIG_NET_NS

       structnet *net;

#endif

       structnet_device *dev;

       structneigh_parms *next;

       int   (*neigh_setup)(struct neighbour *);

       void (*neigh_cleanup)(struct neighbour *);

       structneigh_table *tbl;

 

       void *sysctl_table;

 

       intdead;

       atomic_trefcnt;

       structrcu_head rcu_head;

 

       int   base_reachable_time;//基本有效时间 ,对于arp默认为30s

       int   retrans_time;//solicit请求报文重发间隔时间

       int   gc_staletime;//闲置时间

       int   reachable_time;//确认有效时间超时长度,这个值每隔300s会更新一次

       int   delay_probe_time;//在nud_delay时,为delay的超时时间;在nud_reach状态时,用于判断是否需要进入delay状态的一个时间判断点

        

 

       int   queue_len;//缓存数据包的队列长度

       int   ucast_probes;//发送单播solicit请求的最大次数

       int   app_probes;// ?

       int   mcast_probes;//发送广播solicit请求的最大次数

       int   anycast_delay;

       int   proxy_delay;

       int   proxy_qlen;

       int   locktime;

};

 

 

 

struct neighbour {

       structneighbour     *next;

       structneigh_table   *tbl;//指向该邻居项所属的邻居表

       structneigh_parms *parms;

       structnet_device           *dev;

       unsignedlong        used;//邻居项使用时间

       unsignedlong        confirmed;//connected状态确认时间

       unsignedlong        updated;//邻居项更新时间

       __u8                     flags;

       __u8                     nud_state;//邻居项状态值

       __u8                     type;//邻居项地址的类型

       __u8                     dead;

       atomic_t         probes;//记录邻居项发送的solicit请求的次数

       rwlock_t         lock;

       unsignedchar         ha[ALIGN(MAX_ADDR_LEN,sizeof(unsigned long))];//

       structhh_cache             *hh;//二层缓存头部指针,指向一个二层缓存头部

       atomic_t         refcnt;

       int                 (*output)(struct sk_buff *skb);

       structsk_buff_head       arp_queue;//数据包缓存队列,当有数据包要发送,但又没有目的ip地址对应的目的mac时,则会将数据包缓存在该队列中

       structtimer_list      timer;

       conststruct neigh_ops    *ops; //neighbour项的函数指针表,包含发送solicit请求函数,以及不同状态下对应的输出函数

       u8                  primary_key[0];

};

 

 

 

 

struct neigh_ops {

       int                 family;//所属的地址簇,对于arp,则为AF_INET

       void               (*solicit)(struct neighbour *,struct sk_buff*);//发送邻居请求的函数指针

       void               (*error_report)(struct neighbour*, struct sk_buff*);//当有数据要传送,且邻居项不可达时,则调用该函数向三层发送错误信息

       int                 (*output)(struct sk_buff*);//通用输出函数

       int                 (*connected_output)(structsk_buff*);//当邻居项可达时,使用该函数发送数据包

       int                 (*hh_output)(structsk_buff*);//在缓存了二层头部时,调用该函数发送数据包

       int                 (*queue_xmit)(structsk_buff*);//真正的数据传输函数,

};

 

 

struct neigh_table {

       structneigh_table   *next; //指向下一个邻居协议对应的邻居表

       int                 family;//该邻居协议对应的地址簇

       int                 entry_size;//该邻接表所能包含的邻居项的最大值

       int                 key_len;//关键字的大小,对于arp协议,是4个字节大小

       __u32                   (*hash)(const void *pkey,const struct net_device *);//hash函数

       int                 (*constructor)(struct neighbour*);//该邻居协议所对应的邻居项的邻居初始化 函数,初始化与该邻居协议相关的成员值

       int                 (*pconstructor)(structpneigh_entry *);

       void               (*pdestructor)(structpneigh_entry *);

       void               (*proxy_redo)(struct sk_buff*skb);

       char               *id;

       structneigh_parms parms;

       /*HACK. gc_* shoul follow parms without a gap! */

       int                 gc_interval;//垃圾回收处理邻居项的时间间隔

       int                 gc_thresh1;//当邻居项的数量少于该值时,不会进行垃圾回收

       int                 gc_thresh2;//如果邻居项的数目超过这个值,则在新建邻居项时,若超过5秒未进行刷新,则刷新并强制垃圾回收

       int                 gc_thresh3;//当超过这个值时,则在创建新邻居项时,强制进行垃圾回收

       unsignedlong        last_flush;//记录最新一次刷新邻居表项的时间

       structdelayed_work       gc_work;

       structtimer_list     proxy_timer;

       structsk_buff_head       proxy_queue;

       atomic_t         entries;//已创建邻居表项的数据

       rwlock_t         lock;

       unsignedlong        last_rand;//记录neigh_parms中reach_time成员的最近更新时间

       structkmem_cache        *kmem_cachep;//用来分配邻居项的slab缓存

       structneigh_statistics     __percpu *stats;

       structneighbour     **hash_buckets;//存储邻居项的hash bucket

       unsignedint           hash_mask;

       __u32                   hash_rnd;//hash 散列表扩容时的关键字

       structpneigh_entry **phash_buckets;

};

 

至此,将通用邻居项相关的主要数据结构即介绍完了。

 

0 0
原创粉丝点击