Linux内核分析 - 网络[八]:IP协议
副标题[/!--empirenews.page--]
内核版本:2.6.34 这篇是关于IP层协议接收报文时的处理,重点说明了路由表的查找,以及IP分片重组。 ip_rcv 进入IP层报文接收函数 丢弃掉不是发往本机的报文,skb->pkt_type在网卡接收报文处理以太网头时会根据dst mac设置, 协议栈的书会讲不是发往本机的广播报文会在二层被丢弃,实际上丢弃是发生在进入上层之初。 if (skb- >pkt_type == PACKET_OTHERHOST) goto drop; 在取IP报头时要注意可能带有选项,因此报文长度应当以iph->ihl * 4为准。这里就需要尝试两次, 第一次尝试sizeof(struct iphdr),只是为了确保skb还可以容纳标准的报头(即20字节),然后可以ip_hdr(skb)得到报头;第二 次尝试ihl * 4,这才是报文的真正长度,然后重新调用ip_hdr(skb)来得到报头。两次尝试pull后要重新调用ip_hdr()的原因是 pskb_may_pull()可能会调用__pskb_pull_tail()来改现现有的skb结构。 if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto inhdr_error; iph = ip_hdr(skb); …… if (!pskb_may_pull(skb, iph->ihl*4)) goto inhdr_error; iph = ip_hdr(skb); 获取到IP报头后经过一些检查,获取到报文的总长度len = iph->tot_len,此时调用 pskb_trim_rcsum()去除多余的字节,即大于len的。 if (pskb_trim_rcsum(skb, len)) { IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS); goto drop; } 然后调用ip_rcv_finish()继续IP层的处理,ip_rcv()可以看成是查找路由前的IP层处理,接下来的ip_rcv_finish() 会查找路由表,两者间调用插入的netfilter(关于NetFilter,参考前篇 http://blog.csdn.net/qy532846454/article/details/6605592)。 return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); 进入ip_rcv_finish函数 ip_rcv_finish()主要工作是完成路由表的查询,决定报 文经过IP层处理后,是继续向上传递,还是进行转发,还是丢弃。 刚开始没有进行路由表查询,所以还没有相应的路由表项 :skb_dst(skb) == NULL。则在路由表中查找ip_route_input(),关于内核的路由表,可以参见前篇 http://blog.csdn.net/qy532846454/article/details/6726171: if (skb_dst(skb) == NULL) { int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev); 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; } } 通过路由表查找,我们知道: - 如果是丢弃的报文,则直接drop; - 如果是不能接收或转发的报文,则 input = ip_error - 如果是发往本机报文,则input = ip_local_deliver; - 如果是广播报文,则input = ip_local_deliver; - 如果是组播报文,则input = ip_local_deliver; - 如果是转发的报文,则input = ip_forward ; 在ip_rcv_finish()最后,会调用查找到的路由项_skb_dst->input()继续向上传递: return dst_input (skb); 具体看下各种情况下的报文传递,如果是丢弃的报文,则报文被释放,并从IP协议层返回,完成此次报文传递流 程。 drop: kfree_skb(skb); return NET_RX_DROP; 如果是不能处理的报文,则执行ip_error,根据error类型发送相应的ICMP错误报文。 static int ip_error(struct sk_buff *skb) { struct rtable *rt = skb_rtable(skb); unsigned long now; int code; switch (rt->u.dst.error) { case EINVAL: default: goto out; case EHOSTUNREACH: code = ICMP_HOST_UNREACH; break; case ENETUNREACH: code = ICMP_NET_UNREACH; IP_INC_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INNOROUTES); break; case EACCES: code = ICMP_PKT_FILTERED; break; } now = jiffies; rt->u.dst.rate_tokens += now - rt->u.dst.rate_last; if (rt->u.dst.rate_tokens > ip_rt_error_burst) rt->u.dst.rate_tokens = ip_rt_error_burst; rt->u.dst.rate_last = now; if (rt->u.dst.rate_tokens >= ip_rt_error_cost) { rt->u.dst.rate_tokens -= ip_rt_error_cost; icmp_send(skb, ICMP_DEST_UNREACH, code, 0); } out: kfree_skb(skb); return 0; } (编辑:淮北站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |