当IP数据包上传给IP层时,有IP层的ip_rcv()函数进行接收,这个函数的主要作用就是检查这个数据包。这个函数不会处理不属于这个主机的数据包。也就是这个包的目标MAC地址不是本机的话,该函数就不会处理,而不是L3层的IP地址。
这里我们可以看到pkt_type的类型是PACKET_OTHERHOST,说明这里是其它主机的数据包,直接进行丢弃处理。在网卡获取数据包的中断处理过程(ei_interrupt)中调用了ei_receive,而ei_receive又调用eth_type_trans,在这个过程中将包的类型设置为PACKET_OTHEHOST。
然后在ip_rcv中判断包类型是不是PACKET_OTHERHOST,如果是则丢弃之。
if (skb->pkt_type == PACKET_OTHERHOST) goto drop;
|
接下来是一个共享的检查,如果是共享的数据包,需要修改skb中的信息,所以需要复制一个副本,再作进一步的处理。
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
goto out;
}
|
再下面的处理就是检查首部的长度是否够长,校检和,版本号等信息。 if (iph->ihl < 5 || iph->version != 4) goto inhdr_error;
if (!pskb_may_pull(skb, iph->ihl*4)) goto inhdr_error; 如果没有错误的话就会调ip_rcv_finiish()函数进行进一步的处理。 return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); 在这个函数中首先查找路由信息, if (likely(skb->dst == NULL)) { int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev); 然后,对IP头部中的选项信息进行了进行处理。 if (iph->ihl > 5 && ip_rcv_options(skb)) goto drop;
return dst_input(skb);
|
最后,返回调用dst_input()函数,这个函数主机是根据路由信息调用相应的input函数,
err = skb->dst->input(skb);
但是这个input函数可能是ip_local_deliver或者是ip_forward();
Ip_forward()函数主机是对数据包进行转发发送,ip_local_deliver()函数主要是在本机进行处理的过程。
首先,确定接收到的包是不是分片,如果是,则要将分片重新装成一个完整的IP数据包再上传给L4层。最后调用ip_local_deliver_finish()函数。
int ip_local_deliver(struct sk_buff *skb) if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER); if (!skb) return 0; } return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL, ip_local_deliver_finish); ip_local_deliver_finish
|
确定将这个数据包传送给L4层,L3层的头已经没有作用,先去掉L3的头部。
int ihl = skb->nh.iph->ihl*4;
__skb_pull(skb, ihl);//将skb->data指向L4的头部。
/* Point into the IP datagram, just past the header. */ skb->h.raw = skb->data;// 重新设置skb->h.raw
接下来就是要处理与L4层相关的协议了,这里首先处理的是Raw IP,先查看raw_v4_htable有没有注册这个L4协议的Raw IP。 int protocol = skb->nh.iph->protocol; int hash; struct sock *raw_sk; struct net_protocol *ipprot;
resubmit: hash = protocol & (MAX_INET_PROTOS - 1); raw_sk = sk_head(&raw_v4_htable[hash]); 如果有,则要执行raw_v4_input()对其进行处理。提交一个副本。 if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash)) raw_sk = NULL; 然后,对L4层的各层协议进行相应的处理,调用不同的处理函数。 if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) { int ret;
if (!ipprot->no_policy) { if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { kfree_skb(skb); goto out; } nf_reset(skb); } ret = ipprot->handler(skb);//这里是调用相应的处理函数。
if (ret < 0) { protocol = -ret; goto resubmit; }
|
调用相应的处理函数时要进行相就的注册,这里主要依据6to4隧道为例。我们可以在源码文件中sit.c,中的sit_init()函数中看到下面的注册添加函数。
if (inet_add_protocol(&sit_protocol, IPPROTO_IPV6) < 0) { printk(KERN_INFO "sit init: Can't add protocol\n"); return -EAGAIN; }
|