网络协议栈13:Connect函数分解之链路层

Skb_buff数据包从IP层下传到链路层后,链路层开始对数据包进行处理

 

首先,判断skb_buff数据包是不是不在skb_buff链表中,如果还在(即skb_buff->next!=NULL),则说明上面的处理有问题,代码要避开(人为的避开,代码还是有问题),即不能发送这个数据包,处理方式是,指定发送数据包的设备被指定为NULL,即数据包没有设备真实发送。

 

第二步,判断是否已知下一跳的MAC地址,即skb->arp=1,如果不是,则需要调用arp_find来查找IP地址对应的MAC地址,如果找不到,则直接返回,不再进行发送。

 

第三步,前面两步都正常了,则说明数据包正常,此时判断数据是否是刚刚下传的本地数据包,是则把数据包按照优先级的级别再次进行排队,这里分三种优先级,每个优先级都是一个队列,数据包按照相应的优先级被插入对应优先级的队列的尾部。

 

第四步,从最高优先级队列头部获得一个数据包,这个数据包有可能不是刚刚下传的数据包,如果下传的数据包大于发送的数据包,或者有重传的数据包,则此时获得的数据包就是先前的数据包。

 

第五步,把刚获得数据包进行发送(调用网卡驱动中的发送函数进行发送,到这里才是网卡驱动工作的开始),如果发送成功,就直接返回了,如果发送不成功,则把这个数据包插入到相应优先级的队列的头部,后面发送时它是相应对了中最先得到发送的数据包。

 由于网卡各式各样,没有办法进行抽象,因此,网卡的驱动被开发出来,只需要调用相应的上层函数,再把网卡的功能集合到网络协议栈中,行程完整的协议栈。

 

 

其中,第二步中,如果目的端IP对应的MAC地址还没有(或者下一跳),会使用arp_find来查找对应IP地址对应的MAC地址,其大致过程是:

ARP会在本地有一个ARP缓存,所谓的ARP缓存,在实现上采用数组加链表(或者称为队列)的方式,每个 ARP缓存表项由一个 arp_table 结构表示,所以数组中每个元素就指向一个 arp_table 结构类型的链表。对于具体数组元素的寻址由被解析的 IP 地址通过 Hash 算法而得。所以查询 ARP 缓存的过程就可表述为:首先根据被解析 IP地址索引数组中对应元素指向的 arp_table 结构链表,然后遍历该链表,对 IP地址进行匹配,如果查找一个 IP地址精确匹配的表项,则返回该表项,如果没有寻找到,则表示当前 ARP 缓存中没有对应表项,系统此时将创建一个新的 ARP表项,并发出 ARP请求报文,而当前发送的数据包就被缓存到该表项设置的数据包缓存队列中,当接收到 ARP 应答报文时,一方面完成原先表项的创建(硬件地址字段的初始化) ,另一方面将该表项中之前缓存的所有待发送数据包发送出去。结构图如下:

 

 

 

Arp_table结构体如下:

 

struct arp_table

{

       struct arp_table             *next;     /*next 字段用于 arp_table结构之间的相互串接,构成一个链表。*/

 

       unsigned long               last_used;              /*last_used 字段表示该表项上次被使用的时间*/

 

       unsigned int                  flags;             /*flags 字段用于维护 ARP 表项的一些标志位,如表项当前所处状态,可以为以后的 ARP 缓存表项的功能扩展做好准备。 */

 

       unsigned long               ip;          /*ip 字段表示表项所表示映射关系中的 IP地址*/

       unsigned long               mask;      /*mask 字段为对应 IP地址的网络掩码。*/

 

       unsigned char                ha[MAX_ADDR_LEN];       /* ha 字段表示映射关系中的硬件地址*/

       unsigned char                hlen;                     /* hlen 为硬件地址长度*/

       unsigned short               htype;                   /* htype 为硬件地址类型*/

 

       struct device                 *dev;             /*dev 字段表示该 ARP 表项绑定的网络设备,对于只有一个网络接口的主机,所有数据包的发送当然只有一个发送通道,但对于具有两个或者更多网络接口的主机,数据包就有多种可能的选择,如果每次发送时都进行发送接口的查询会不必要的增加系统开销,由于数据帧创建中链路层首部的创建都需要进行硬件地址解析,即查询 ARP缓存,所以在 ARP表项中维护一个对应发送接口的指针, 就可以在进行数据帧创建时一并解决通过哪个网络接口发送数据包的问题。另外对于多个网络接口的主机,每个网口都接在不同的网络上,而远端主机只可能属于一个网络,所以我们在进行 ARP 表项创建时可以知道这个主机属于哪个网络,从而初始化接入该网络的网口设备指针。由此可见,ARP 缓存表项中 dev 字段值将来自于路由表项。而路由表项要么是手工配置,要么根据运行路由协议创建,而根据接收路由数据包的网络接口我们可以进行路由表项中网口设备字段的初始化。*/

 

       struct timer_list             timer;      /*timer 字段用于定时重发 ARP 请求数据包,以防止 ARP 请求未得到响应。*/

       int                        retries;           /* 重发次数,当前设置的最大次数为 3,由 ARP_MAX_TRIES 变量表示*/

 

       struct sk_buff_head              skb;               /*最后一个字段 skb 表示暂时由于 IP 地址到硬件地址的解析未完成而被缓存的数据包队列该字段的具体使用在下文中介绍到相关函数时进行说明。*/   

 

};

 

而arp缓存数组定义如下:

#define ARP_TABLE_SIZE  16

#define FULL_ARP_TABLE_SIZE (ARP_TABLE_SIZE+1)

struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =

{

       NULL,

};

 

 

系统就是通过上述的结构体和数组来管理ARP缓存的

posted on 2012-01-12 16:15  image eye  阅读(1022)  评论(0编辑  收藏  举报