日期:2014-05-16 浏览次数:20819 次
本文主要讲解了Linux内核IP层的整体架构和对从网卡接受的报文处理流程,使用的内核的版本是2.6.32.27
为了方便理解,本文采用整体流程图加伪代码的方式对Linxu内核中IP整体实现架构和对网卡报文的处理流程进行了讲解,希望可以对大家有所帮助。阅读本文章假设大家对C语言有了一定的了解
IP层的整体实现架构
IP层接受底层数据报文的处理流程
/* * 在NET_RX_SOFTIRQ软中后,由ETH_P_IP触发的ipv4协议入口函数 */ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { /* * 过滤掉送往其他主机的数据包(这时网卡正在处于混杂模式) */ if (skb->pkt_type == PACKET_OTHERHOST) goto drop; iph = ip_hdr(skb); /*头的长度是否至少是IP头长度(5); 是否是IPV4报文*/ if (iph->ihl < 5 || iph->version != 4) goto inhdr_error; /*IP头长度是否正确,不是伪造的长度*/ if (!pskb_may_pull(skb, iph->ihl*4)) goto inhdr_error; iph = ip_hdr(skb); /*检查校验和*/ if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) goto inhdr_error; len = ntohs(iph->tot_len); if (skb->len < len) { IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; } else if (len < (iph->ihl*4)) goto inhdr_error; /*实际尺寸不匹配套接字缓冲(skb->len)中维护的信息,则调用skb_trim调整数据包的长度*/ if (pskb_trim_rcsum(skb, len)) { IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS); goto drop; } /*调用IP_PRE_ROUTING(NF_INET_PRE_ROUTING)上注册的钩子, *在调用钩子处理完之后,调用钩子处理完成之后,调用ip_rcv_finish * 后面讲防火墙的时候,我们会仔细梳理*/ return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); } /* NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish)*/ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN) { int __ret; \ if ((__ret=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, 1)) == 1)\ __ret = (okfn)(skb); \ __ret; } static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), int thresh, int cond) { /*逐个调用注册的防火墙钩子*/ return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh); } /* * 接收完数据包后的后续处理函数 */ static int ip_rcv_finish(struct sk_buff *skb) { const struct iphdr *iph = ip_hdr(skb); struct rtable *rt; /* * 激活ip_route_input,确定报文的路由,如果ip_route_input无法从FIB中找到路由 * 则丢弃数据报文,ip_route_input将在IP路由中的专题中进行讲解 */ if (skb_dst(skb) == NULL) { int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev); if (unlikely(err)) { goto drop; } } /*检查IP报头里面是否含有选项,如果含有建立ip_options*/ if (iph->ihl > 5 &am