IP层接到一个数据包以后,如果需要进行路由,就调用函数myip_route_input, myip_route_input先在缓存里寻找路由,如果失败则调用myip_route_input_slow, myip_route_input_slow调用myfib_lookup在路由表里寻找路由,如果命中,首先在缓存里添加这个路由,然后返回结果。
路由缓存的数据结构是一个全局哈希表myrt_hash_table,前面文章讲过,该哈希表在模块初始化时被分配了4096个入口。哈希表的每一项都是 一个rtable结构的链表。每一个rtable数据结构都存放向一个IP主机发送报文的路由信息。下面是结构体struct rtable的定义:
struct rtable
{
union{
struct dst_entry dst;
struct rtable *rt_next;
}u;
struct in_device *idev;
unsigned rt_flags;
__u16 rt_type;
__u16 rt_multipath_alg;
__u32 rt_dst;
__u32 rt_src;
int rt_iif;
__u32 rt_gateway;
struct flowi fl;
__u32 rt_spec_dst;
struct inet_peer *peer;
};
接收到的数据包,可能是组播包,也可能是广播包,情况比较复杂,对此myip_route_input_slow都有对应的处理,为了简单起见,我们以一个ARP请求包为例来说明myip_route_input的“一部分”工作流程。
给我的网络设备接口eth0配置一个secondary地址172.16.48.11(其主地址是172.16.48.2),这样在路由表的local表里就多了一项:
: the key ip: 172.16.48.11
: the alias:
: fa tos: 0
: fa type: RTN_LOCAL
: fa scope: RT_SCOPE_HOST
: fa state: 0
: the fa info:
: tree ref: 2
: clnt ref: 1
: fib_dead: 0
: fib flags: 0
: protocol: 2
: pref src: 172.16.48.2.
: priority: 0
: fib_mtu: 0
: fib_window: 0
: fib_power: 0
: fib_nhs: 1
: all the nhs:
: 0.16.48.2
当我们收到来自172.16.48.1的一个对172.16.48.11的ARP请求包时。我们需要对接收到的数据包进行路由查询,这样调用myip_route_input:
myip_route_input(skb, dst=172.16.48.11, src=172.16.48.1, tos=0, dev)
因为是第一次,路由缓存中并没有我们要找的路由,所以该函数以同样的参数列表调用myip_route_input_slow接口。 myip_route_input_slow先进行一系例判断,当确定该数据包是单播包时,调用myfib_lookup进行路由查找。 myfib_lookup使用内置的路由规则,先查找local表,查找成功。
然后,对全局变量myrt_cache_stat的in_slow_tot加1,记录本次进入myip_route_input_slow查找路由。后面还会有很多操作,会相应的更新这个状态结构,不再进行描述。
路由表查询的结果是由结构struct fib_result表示的,此次查询结果的类型是RTN_LOCAL,表示这个数据包是要本地接收的,而不是转发的。接下来,我们要做的一件事情就是确 认这个数据包的源地址的有效性,方法是把源地址和目的地址反向,再去查找路由表,即查找172.16.48.11->172.16.48.1的路 由。显然,这次我们在main表中查到了type为RTN_UNICAST的路由,即源地址是有效的。
接下来,分配一个结构struct rtable,并进行初始化,主要是其成员fl,这是一个结构体struct flowi。里面填充数据包的源和目的地址的详细信息,还有输入网络设备接口,tos等详细信息。
最后我们要做的一件事情就是把这个新创建出来的rtable放到路由缓存中,先确定在myrt_hash_table中的具体哪一项,然后在链表中搜索, 如果发现这个rtable已经存在,则把它提到链表头,同时,销毁创建出来的rtable。关于路由缓存的垃圾回收算法等细节,我们再进行专门分析。
前面我们看到在调用myip_route_input时,第一个参数是skb,最后,我们需要把创建出来的rtable赋给skb->dst。
接下来,我们再看一个例子,如果我在172.46.48.1 ping 172.16.48.10。这是一个不存在的ip地址,当172.16.48.1发出ARP请求包时,会发生什么事情?
显然,在查找路由时,我们可以在main表中查到一个RTN_UNICAST的路由,表示不是本地接收,可能本地只是一个网关,或者需要转发。但这个包显然是不需要转发的,所以,我们得到的返回值是:无可达路由。
路由缓存的数据结构是一个全局哈希表myrt_hash_table,前面文章讲过,该哈希表在模块初始化时被分配了4096个入口。哈希表的每一项都是 一个rtable结构的链表。每一个rtable数据结构都存放向一个IP主机发送报文的路由信息。下面是结构体struct rtable的定义:
struct rtable
{
union{
struct dst_entry dst;
struct rtable *rt_next;
}u;
struct in_device *idev;
unsigned rt_flags;
__u16 rt_type;
__u16 rt_multipath_alg;
__u32 rt_dst;
__u32 rt_src;
int rt_iif;
__u32 rt_gateway;
struct flowi fl;
__u32 rt_spec_dst;
struct inet_peer *peer;
};
接收到的数据包,可能是组播包,也可能是广播包,情况比较复杂,对此myip_route_input_slow都有对应的处理,为了简单起见,我们以一个ARP请求包为例来说明myip_route_input的“一部分”工作流程。
给我的网络设备接口eth0配置一个secondary地址172.16.48.11(其主地址是172.16.48.2),这样在路由表的local表里就多了一项:
: the key ip: 172.16.48.11
: the alias:
: fa tos: 0
: fa type: RTN_LOCAL
: fa scope: RT_SCOPE_HOST
: fa state: 0
: the fa info:
: tree ref: 2
: clnt ref: 1
: fib_dead: 0
: fib flags: 0
: protocol: 2
: pref src: 172.16.48.2.
: priority: 0
: fib_mtu: 0
: fib_window: 0
: fib_power: 0
: fib_nhs: 1
: all the nhs:
: 0.16.48.2
当我们收到来自172.16.48.1的一个对172.16.48.11的ARP请求包时。我们需要对接收到的数据包进行路由查询,这样调用myip_route_input:
myip_route_input(skb, dst=172.16.48.11, src=172.16.48.1, tos=0, dev)
因为是第一次,路由缓存中并没有我们要找的路由,所以该函数以同样的参数列表调用myip_route_input_slow接口。 myip_route_input_slow先进行一系例判断,当确定该数据包是单播包时,调用myfib_lookup进行路由查找。 myfib_lookup使用内置的路由规则,先查找local表,查找成功。
然后,对全局变量myrt_cache_stat的in_slow_tot加1,记录本次进入myip_route_input_slow查找路由。后面还会有很多操作,会相应的更新这个状态结构,不再进行描述。
路由表查询的结果是由结构struct fib_result表示的,此次查询结果的类型是RTN_LOCAL,表示这个数据包是要本地接收的,而不是转发的。接下来,我们要做的一件事情就是确 认这个数据包的源地址的有效性,方法是把源地址和目的地址反向,再去查找路由表,即查找172.16.48.11->172.16.48.1的路 由。显然,这次我们在main表中查到了type为RTN_UNICAST的路由,即源地址是有效的。
接下来,分配一个结构struct rtable,并进行初始化,主要是其成员fl,这是一个结构体struct flowi。里面填充数据包的源和目的地址的详细信息,还有输入网络设备接口,tos等详细信息。
最后我们要做的一件事情就是把这个新创建出来的rtable放到路由缓存中,先确定在myrt_hash_table中的具体哪一项,然后在链表中搜索, 如果发现这个rtable已经存在,则把它提到链表头,同时,销毁创建出来的rtable。关于路由缓存的垃圾回收算法等细节,我们再进行专门分析。
前面我们看到在调用myip_route_input时,第一个参数是skb,最后,我们需要把创建出来的rtable赋给skb->dst。
接下来,我们再看一个例子,如果我在172.46.48.1 ping 172.16.48.10。这是一个不存在的ip地址,当172.16.48.1发出ARP请求包时,会发生什么事情?
显然,在查找路由时,我们可以在main表中查到一个RTN_UNICAST的路由,表示不是本地接收,可能本地只是一个网关,或者需要转发。但这个包显然是不需要转发的,所以,我们得到的返回值是:无可达路由。