我们知道,报文经过网卡驱动处理后,调用net_receive_skb传递给具体的协议处理函数,对于IPv4报文来说,其协议处理函数就是ip_rcv了,ip_rcv在进行一些健康检查等操作后,会调用ip_rcv_finish来处理报文。这也是IPv4协议对报文接收处理的开始。
我们先看下ip_rcv_finish源代码:
ip_rcv_finish:
//ip数据报文的主要处理程序(ip_rcv仅仅只是对ip数据报做一些健康性检查)
//ip_rcv_finish 其实是进行路由表查询,,决定报文经过IP层处理后,是继续向上传递,还是进行转发,还是丢弃。
//1.决定报文在本地传递或者转发,如果是转发还需要找到出口设备和下一跳节点
//2.分析和处理一些选项
static int ip_rcv_finish(struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
/*
* Initialise the virtual path cache for the packet. It describe
* how the packet travels inside Linux networking.
* 刚开始没有进行路由表查询,所以还没有相应的路由表项:skb_dst(skb) == NULL。
* 则在路由表中查找ip_route_input(),关于内核的路由表
*/
if (skb_dst(skb) == NULL) {
int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
skb->dev); //这里面进行了一些初始化操作,比较重要,与ip报文接下来的走向有关
if (unlikely(err)) {
if (err == -EHOSTUNREACH)
IP_INC_STATS_BH(dev_net(skb->dev),
IPSTATS_MIB_INADDRERRORS);
else if (err == -ENETUNREACH)
IP_INC_STATS_BH(dev_net(skb->dev),
IPSTATS_MIB_INNOROUTES);
goto drop;
}
}
#ifdef CONFIG_NET_CLS_ROUTE
//更新traffic cotrol(qos层)所使用的统计数据
if (unlikely(skb_dst(skb)->tclassid)) {
struct ip_rt_acct *st = per_cpu_ptr(ip_rt_acct, smp_processor_id());
u32 idx = skb_dst(skb)->tclassid;
st[idx&0xFF].o_packets++;
st[idx&0xFF].o_bytes += skb->len;
st[(idx>>16)&0xFF].i_packets++;
st[(idx>>16)&0xFF].i_bytes += skb->len;
}
#endif
if (iph->ihl > 5 && ip_rcv_options(skb))
goto drop;
rt = skb_rtable(skb); /* skb->dst包含路由信息。根据路由类型更新SNMP统计数据 */
if (rt->rt_type == RTN_MULTICAST) {
IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INMCAST,
skb->len);
} else if (rt->rt_type == RTN_BROADCAST)
IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INBCAST,
skb->len);
/*
* dst_input实际上会调用skb->dst->input(skb).input函数会根据路由信息设置为合适的
* 函数指针,如果是递交到本地的则为ip_local_deliver,若是转发则为ip_forward.
* 暂时仅先考虑ip_local_deliver。
*/
return dst_input(skb);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
ip_route_input会进行路由表查询,该函数直接或间接决定了报文之后要往何处传递。是进行本地传递还是转发。
我们可以看到如果报文没有被drop掉,那么报文最终会被dst_input(skb)处理。dst_input(skb)实际上执行的是skb->dst->input(skb)。而这里的input函数其实就是由ip_route_input决定的。
对于应该本地传递的报文,