上次研究到netif_receive_skb(),这是网络层的入口。这个函数根据skb的protocol字段,调用不同的函数。
//linux-3.0.8/net/core/dev.c
type = skb->protocol;
list_for_each_entry_rcu(ptype,
&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
if (ptype->type == type &&
(ptype->dev == null_or_dev || ptype->dev == skb->dev ||
ptype->dev == orig_dev)) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
deliver_skb()是一个内联函数,它的作用就是调用不同协议的处理函数。这是很好理解的,在数据报文在两层之间传递时,报文需要提供一些字段,让下一层根据这些字段做出不同的处理。对于一个网络系统,L3(网络层)有很多不同的协议,比如IP,IPX,appletalk等,当然也可能有L2的LCC协议。这些协议在内核协议栈中都是有明确定义的,对应于skb的protocol字段。如果是IPv4,就会调用非常重要的ip_rcv()函数了。ip_rcv()函数在最后时候,会通过NetFilter调用ip_rcv_finish()。
ip_rcv_finish()函数中会间接调用ip_route_input_slow(),这个函数中会设置skb的_skb_refdst->input函数指针。这个指针可能被设置为ip_local_deliver()或者是ip_forward()。这次先看看ip_local_deliver(),即接收本地的数据报文。
int ip_local_deliver(struct sk_buff *skb)
{
/*
* Reassemble IP fragments.
*/
if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
return 0;
}
return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
}
这个函数就是做了网络从L3到L4的最后工作。首先,这个函数会根据ip帧的头部信息尝试重组ip帧。如果没有重组成功,就返回0。这次返回就标志着新数据帧的处理基本完成了。当然,如果重组成功的话,还需要通过NetFilter调用ip_local_deliver_finish()。如果NetFilter觉得该数据包可以接收,就会调用ip_local_deliver_finish()。看到这个函数,我们就知道ip帧就要和网络层说byebye了,是的它进入了L4传输层。
这部分显得的有点简单,其实真正的重头戏在ip_forward()中,路由子系统还没开始看,所以先绕过了。