Linux协议栈IP层的路由处理

时间:2022-08-16 11:05:13

http://blog.csdn.net/wangxing1018/article/details/4285489

写在前面: 其实,这篇文章也不知道取什么名字好,感觉什么都没讲。唉。。。其实我的意图是说明Linux协议栈IP层,对数据包的路由处理,比如说,如果有两个网卡设备,数据包该从哪里走,等  。。。

首先,了解一下路由缓存的概念: 虽然路由表项数据结构提供了相对快速的查询程序,但是对于每一个IP数据包都运行这样一个查询程序的成本还是太高。基于这个原因,Linux内核还有一个路由缓存,用于存储最近转发查询的结果,以使访问更加快速。每一个转发操作都会首先询问路由缓存。只有在缓存没有的,才会查询路由表项,而此查询的结果立即被创建一条新的路由缓存。
路由缓存被用于减少路由表查找的时间。路由缓存的核心是与协议无关的目的缓存(Protocol Independent Destination Cache),简称为DST。缓存中与协议无关的(DST)部分是一组dst_entry数据结构。在sk_buff结构体中具体是struct dst_entry *dst,指向路由高速缓存中的一条记录,这说明它包含着有关报文进一步处理的信息(比如,报文通过哪个网卡发送)或MAC报头,即MAC源和目的地址。

 

Linux协议栈IP层的路由处理

上面的图是 The Linux® Networking Architecture: Design and Implementation of Network Protocols in the Linux Kernel   Prentice Hall   August 01, 2004 的,我仔细有看来一下,发现有点不合适的地方,见我画的红色的部分。

下面结合这个图,来说明。

IP包可以从三个方面进入Linux协议栈的IP层:
1 通过网卡接收数据分组,入口处是ip_rcv()函数 。
这部分可以看作是接收。
    首先,ip_rcv()函数进行一些IP协议工作:比如检查包是否本地、包的尺寸大小、包的检验和等一些常规性的检查;
    然后Netfilter钩子启用ip_rcv_finish()来进一步处理IP分组,比如分组交给谁。调用ip_route_input()函数,(根据IP地址、TOS、物理网络设备等)查询路由缓存(如果没有,这继续查找路由表。如果还没有,就丢弃该分组),然后设置sk_buff的dst。根据dst,来决定数据包处理:转发ip_forward()、本地递交ip_local_deliver()、出错ip_error();
2 上层协议接口,比如TCP/UDP等 。
这部分可以看作是发送。
    拿TCP来讲,首先,ip_queue_xmit(skb)会检查skb->dst路由信息。如果没有,比如套接字的第一个包,就使用ip_route_output()选择一个路由。接着,填充IP包的各个字段,比如版本、包头长度、TOS等。
    中间的一些分片等,参阅相关文档;
    接下来就用ip_finish_ouput2设置链路层报文头了。如果,链路层报头缓存有(即hh不为空),那就拷贝到skb里。如果没,那么就调用neigh_resolve_output,使用ARP获取。
3 IP层可以自己主动地生成IP包,比如ICMP或IGMP等。

总之,看来,无论是数据包的发生还是接受,占据主导地位的是Linux的路由表,可以看到还包括硬件网卡地址(在路由表的最后一项)。

下面是OReilly.Understanding.Linux.Network.Internals.Dec.2005的一个图,大家可以参考一下的。

Linux协议栈IP层的路由处理