对于linux的数据包流向,大家应该是比较了解,如果还不是很了解,可以参考《OReilly.Understanding.Linux.Network.Internals.Dec.2005》,其中有一个图非常清楚的描述了数据包的流向。
ip的数据包接收函数是ip_rcv()==>ip_rcv_finish() 在ip_rcv_finish()中: if (skb->dst == NULL) { int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev);
刚接收到的数据包,skb->dst项是空的,因此会调用ip_route_input()函数,我们来追踪ip_route_input函数: ip_route_input()==>ip_route_input_slow()
在ip_route_input_slow()中: if ((err = fib_lookup(&fl, &res)) != 0) { if (!IN_DEV_FORWARD(in_dev)) goto e_hostunreach; goto no_route; } 调用fib_lookup()函数用来在fib中查询路由信息,将路由查询结果保存在fib_result结构的res中。
接下来: if (res.type == RTN_LOCAL) { int result; result = fib_validate_source(saddr, daddr, tos, loopback_dev.ifindex, dev, &spec_dst, &itag); if (result < 0) goto martian_source; if (result) flags |= RTCF_DIRECTSRC; spec_dst = daddr; /*设置skb->dst->input=ip_local_deliver*/ goto local_input; } 如果查询的结果显示,路由类型RTN_LOCAL的话,跳转到local_input段,设置skb->dst->input = ip_local_deliver
接下来,路由类型是RTN_LOCAL的已经跳转到下面去了,剩下的就是非LCOAL的,也就是Forward的: if (!IN_DEV_FORWARD(in_dev)) goto e_hostunreach; if (res.type != RTN_UNICAST) goto martian_destination; /*设置skb->dst->input=ip_forward*/ err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos); 调用IN_DEV_FORWARD宏来判断网络设备是否处于FORWARD状态;调用ip_mkroute_input()函数来设置skb->dst->input=ip_forward
我们来分析一下IN_DEV_FORWARD这个宏: #define IN_DEV_FORWARD(in_dev) ((in_dev)->cnf.forwarding)
在ipv4_sysctl_forward()函数中调用inet_forward_change()函数: 在inet_forward_change()函数中: int on = ipv4_devconf.forwarding; in_dev->cnf.forwarding = on;
在sysctl_net_ipv4.c中 { .ctl_name = NET_IPV4_FORWARD, .procname = "ip_forward", .data = &ipv4_devconf.forwarding, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &ipv4_sysctl_forward, .strategy = &ipv4_sysctl_forward_strategy },
这样,我们通过 echo 1 > /proc/sys/net/ipv4/ip_forward 来打开forward功能,实际上就调用了 ipv4_sysctl_forward()==>inet_forward_change() 设置了in_dev->cnf.forwarding = 1;
这样IN_DEV_FORWARD(in_dev)返回为1,当forward的数据包到来的时候,能够调用ip_mkroute_input()函数,设置skb->dst->input=ip_forward。
========== 再回到ip_rcv_finish()中,最后函数调用了dst_input函数,我们来追踪一下dst_input()函数: dst_input()==>skb->dst->input
对于LOCAL的数据包来说调用的是:ip_local_deliver()函数 对于FORWARD的数据包来说调用的是:ip_forward()函数
对于fib_lookup的细节,可以参考: http://blog.chinaunix.net/u/28366/showart_215922.html
|