Linux内核IP层的报文处理流程--从网卡接收的报文处理流程

本文主要讲解了Linux内核IP层的整体架构和对从网卡接受的报文处理流程。

使用的内核的版本是2.6.32.27


为了方便理解,本文采用整体流程图加伪代码的方式

对Linxu内核中IP整体实现架构和对网卡报文的处理流程进行了讲解,希望可以对大家有所帮助。

阅读本文章假设大家对C语言有了一定的了解


IP层的整体实现架构




IP层接受底层数据报文的处理流程

[cpp]  view plain copy
  1. /* 
  2.  * 在NET_RX_SOFTIRQ软中后,由ETH_P_IP触发的ipv4协议入口函数 
  3.  */  
  4. int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)  
  5. {  
  6.     /* 
  7.      * 过滤掉送往其他主机的数据包(这时网卡正在处于混杂模式) 
  8.      */  
  9.     if (skb->pkt_type == PACKET_OTHERHOST)  
  10.         goto drop;  
  11.   
  12.     iph = ip_hdr(skb);  
  13.   
  14.     /*头的长度是否至少是IP头长度(5); 是否是IPV4报文*/  
  15.     if (iph->ihl < 5 || iph->version != 4)  
  16.         goto inhdr_error;  
  17.   
  18.     /*IP头长度是否正确,不是伪造的长度*/  
  19.     if (!pskb_may_pull(skb, iph->ihl*4))  
  20.         goto inhdr_error;  
  21.   
  22.     iph = ip_hdr(skb);  
  23.     /*检查校验和*/  
  24.     if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))  
  25.         goto inhdr_error;  
  26.   
  27.     len = ntohs(iph->tot_len);  
  28.     if (skb->len < len) {  
  29.         IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);  
  30.         goto drop;  
  31.     } else if (len < (iph->ihl*4))  
  32.         goto inhdr_error;  
  33.   
  34.     /*实际尺寸不匹配套接字缓冲(skb->len)中维护的信息,则调用skb_trim调整数据包的长度*/  
  35.     if (pskb_trim_rcsum(skb, len)) {  
  36.         IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);  
  37.         goto drop;  
  38.     }  
  39.   
  40.     /*调用IP_PRE_ROUTING(NF_INET_PRE_ROUTING)上注册的钩子, 
  41.       *在调用钩子处理完之后,调用钩子处理完成之后,调用ip_rcv_finish 
  42.       * 后面讲防火墙的时候,我们会仔细梳理*/  
  43.     return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish);  
  44. }  
  45.   
  46.   
  47. /* NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish)*/  
  48. #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \  
  49.     NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)  
  50. {  
  51.     int __ret;                                     \  
  52.     if ((__ret=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, 1)) == 1)\  
  53.         __ret = (okfn)(skb);                               \  
  54.     __ret;  
  55. }         
  56.   
  57. static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,  
  58.                  struct sk_buff *skb,  
  59.                  struct net_device *indev,  
  60.                  struct net_device *outdev,  
  61.                  int (*okfn)(struct sk_buff *), int thresh,  
  62.                  int cond)  
  63. {  
  64.     /*逐个调用注册的防火墙钩子*/  
  65.     return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);  
  66. }  
  67.   
  68.   
  69. /* 
  70.  * 接收完数据包后的后续处理函数 
  71.  */  
  72. static int ip_rcv_finish(struct sk_buff *skb)  
  73. {  
  74.     const struct iphdr *iph = ip_hdr(skb);  
  75.     struct rtable *rt;  
  76.   
  77.      /* 
  78.       * 激活ip_route_input,确定报文的路由,如果ip_route_input无法从FIB中找到路由 
  79.       * 则丢弃数据报文,ip_route_input将在IP路由中的专题中进行讲解 
  80.       */  
  81.     if (skb_dst(skb) == NULL) {  
  82.         int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev);  
  83.         if (unlikely(err)) {  
  84.             goto drop;  
  85.         }  
  86.     }  
  87.   
  88.     /*检查IP报头里面是否含有选项,如果含有建立ip_options*/  
  89.     if (iph->ihl > 5 && ip_rcv_options(skb))  
  90.         goto drop;  
  91.   
  92.     /*根据dst_entry的结果,使用skb_dst(skb)->input(skb)进行IP的路由选择 
  93.      *传递给本地计算机的单播或多播,进入 ip_local_deliver(); 
  94.      *单播转发的报文进入ip_forward() 
  95.      *多播转发进入ip_mr_input() 
  96.      */  
  97.     return dst_input(skb);  
  98.     {  
  99.         skb_dst(skb)->input(skb)  
  100.     }  
  101.   
  102. drop:  
  103.     kfree_skb(skb);  
  104.     return NET_RX_DROP;  
  105. }  
  106.   
  107. /*目的地分发策略的注册*/  
  108. static int __mkroute_input(struct sk_buff *skb,  
  109.                struct fib_result *res,  
  110.                struct in_device *in_dev,  
  111.                __be32 daddr, __be32 saddr, u32 tos,  
  112.                struct rtable **result)  
  113. {  
  114.     //.......  
  115.     rth->u.dst.input = ip_forward;  
  116.     rth->u.dst.output = ip_output;  
  117.     //......      
  118. }  
  119.   
  120.   
  121. static int __mkroute_output(struct rtable **result,  
  122.                 struct fib_result *res,  
  123.                 const struct flowi *fl,  
  124.                 const struct flowi *oldflp,  
  125.                 struct net_device *dev_out,  
  126.                 unsigned flags)  
  127. {  
  128.     //......  
  129.     if (flags & RTCF_LOCAL) {  
  130.         rth->u.dst.input = ip_local_deliver;  
  131.         rth->rt_spec_dst = fl->fl4_dst;  
  132.     }  
  133.     if (flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {  
  134.         rth->rt_spec_dst = fl->fl4_src;  
  135.         if (flags & RTCF_LOCAL &&  
  136.             !(dev_out->flags & IFF_LOOPBACK)) {  
  137.             rth->u.dst.output = ip_mc_output;  
  138.             RT_CACHE_STAT_INC(out_slow_mc);  
  139.         }  
  140. #ifdef CONFIG_IP_MROUTE  
  141.         if (res->type == RTN_MULTICAST) {  
  142.             if (IN_DEV_MFORWARD(in_dev) &&  
  143.                 !ipv4_is_local_multicast(oldflp->fl4_dst)) {  
  144.                 rth->u.dst.input = ip_mr_input;  
  145.                 rth->u.dst.output = ip_mc_output;  
  146.             }  
  147.         }  
  148. #endif  
  149.   
  150.     }     
  151.     //......  
  152. }  

如果IP报文需要转发,那么分析流程如下

[cpp]  view plain copy
  1. //-----------------------------------------------------------------------------------------------------------------------------------------------------------------------  
  2. /*单播转发处理,负责处理转发相关的所有动作*/  
  3. int ip_forward(struct sk_buff *skb)  
  4. {  
  5.     /*删除不是PACKET_HOST的数据包*/  
  6.     if (skb->pkt_type != PACKET_HOST)  
  7.         goto drop;  
  8.   
  9.     /*TTL递减为1之间,丢弃该报,并返回ICMP_TIME_EXCEEDED*/  
  10.     if (ip_hdr(skb)->ttl <= 1)  
  11.         goto too_many_hops;  
  12.   
  13.     /*如果skb->len大于MTU值,且Dont-Fragment被职位,则丢弃此报文, 
  14.      *并返回ICMP_FRAG_NEEDED*/  
  15.     if (unlikely(skb->len > dst_mtu(&rt->u.dst) &&  (ip_hdr(skb)->frag_off & htons(IP_DF)))) {  
  16.         icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,  htonl(dst_mtu(&rt->u.dst)));  
  17.         goto drop;  
  18.     }  
  19.   
  20.     /*检查是否有足够的空间用于输出网络设备中的MAC报头dst.header_len() 
  21.      *调用skb_cow来创建一个新的足够长的skb,并且拷贝原来的所有数据 
  22.      */  
  23.     if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))  
  24.         goto drop;  
  25.     iph = ip_hdr(skb);  
  26.   
  27.     /*TTL减少1*/  
  28.     ip_decrease_ttl(iph);  
  29.   
  30.     /*使用IP_FORWARD中注册的钩子函数,当防火墙中的钩子都与运行完成后, 
  31.       *进入ip_forward_finish*/  
  32.     return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,  ip_forward_finish);  
  33.   
  34. sr_failed:  
  35.     /* 
  36.      *  Strict routing permits no gatewaying 
  37.      */  
  38.      icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);  
  39.      goto drop;  
  40.   
  41. too_many_hops:  
  42.     /* Tell the sender its packet died... */  
  43.     IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_INHDRERRORS);  
  44.     icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);  
  45. drop:  
  46.     kfree_skb(skb);  
  47.     return NET_RX_DROP;  
  48. }  
  49.   
  50. /* 
  51.  * 该函数没有什么用途,除非启用了FASTROUTE, 
  52.  * 将处理后的函数报文送入output阶段 
  53.  */  
  54. static int ip_forward_finish(struct sk_buff *skb)  
  55. {  
  56.     struct ip_options * opt = &(IPCB(skb)->opt);  
  57.   
  58.     /*使用ip_forward_options处理IP选项*/  
  59.     if (unlikely(opt->optlen))  
  60.         ip_forward_options(skb);  
  61.       
  62.     /*送入到输出阶段*/  
  63.     return dst_output(skb);  
  64.     {  
  65.         skb_dst(skb)->output(skb);  
  66.     }  
  67. }  
  68.   
  69.   
  70. /*目的地分发策略的注册*/  
  71. static int __mkroute_input(struct sk_buff *skb,  
  72.                struct fib_result *res,  
  73.                struct in_device *in_dev,  
  74.                __be32 daddr, __be32 saddr, u32 tos,  
  75.                struct rtable **result)  
  76. {  
  77.     //.......  
  78.     rth->u.dst.input = ip_forward;  
  79.     rth->u.dst.output = ip_output;  
  80.     //......      
  81. }  
  82.   
  83.   
  84. int ip_output(struct sk_buff *skb)  
  85. {  
  86.     struct net_device *dev = skb_dst(skb)->dev;  
  87.   
  88.     /*将skb->dev指向输出设备的dev*/  
  89.     skb->dev = dev;  
  90.     /*设置2层包类型为ETH_P_IP*/  
  91.     skb->protocol = htons(ETH_P_IP);  
  92.   
  93.     /*使用防火墙中的NF_IP_POST_ROUTING中注册的钩子函数进行处理, 
  94.       *处理完成之后进入ip_finish_output处理*/  
  95.     return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,  
  96.                 ip_finish_output,  
  97.                 !(IPCB(skb)->flags & IPSKB_REROUTED));  
  98. }  
  99.   
  100. /*判定是否进行IP分片*/  
  101. static int ip_finish_output(struct sk_buff *skb)  
  102. {  
  103.     /*如果报文尺寸大于MTU,则进行IP分片后送入ip_finish_output2 
  104.      *否则直接送入ip_finish_output2 
  105.      */  
  106.     if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))  
  107.         return ip_fragment(skb, ip_finish_output2);  
  108.     else  
  109.         return ip_finish_output2(skb);  
  110. }  
  111.   
  112. static const struct neigh_ops arp_generic_ops = {  
  113.     .family =       AF_INET,  
  114.     .output =       neigh_resolve_output,  
  115.     .hh_output =        dev_queue_xmit,  
  116. };  
  117.   
  118. static const struct neigh_ops arp_hh_ops = {  
  119.     .family =       AF_INET,  
  120.     .output =       neigh_resolve_output,  
  121.     .hh_output =        dev_queue_xmit,  
  122. };  
  123.   
  124.   
  125. static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,   __be16 protocol)  
  126. {  
  127.     struct hh_cache *hh;  
  128.   
  129.     //......  
  130.       
  131.     if (n->nud_state & NUD_CONNECTED)  
  132.         hh->hh_output = n->ops->hh_output; /*也就是dev_queue_xmit*/  
  133.     else  
  134.         hh->hh_output = n->ops->output;  
  135.   
  136.     //......  
  137. }  
  138.   
  139. static void neigh_suspect(struct neighbour *neigh)  
  140. {  
  141.     //.....  
  142.     neigh->output = neigh->ops->output; /*也就是neigh_resolve_output*/  
  143.   
  144. }  
  145.   
  146.   
  147. static inline int ip_finish_output2(struct sk_buff *skb)  
  148. {     
  149.     /*如果2层头数据空间不够,则重新分配足够长度的SKB,并将数据复制到新的SKB后释放原来SKB*/  
  150.     if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {  
  151.         struct sk_buff *skb2;  
  152.           
  153.         skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));  
  154.   
  155.         if (skb->sk)  
  156.             skb_set_owner_w(skb2, skb->sk);  
  157.         kfree_skb(skb);  
  158.         skb = skb2;  
  159.     }  
  160.   
  161.     /*如果路由出口项中已经含有2层包头缓存的引用(dst->hh),进入neigh_hh_output*/  
  162.     if (dst->hh)  
  163.         return neigh_hh_output(dst->hh, skb);  
  164.     /*如果没有dst->hh,有dst->neighbour,则启动地址解析协议,也就是neigh_resolve_output*/  
  165.     else if (dst->neighbour)  
  166.         return dst->neighbour->output(skb);  
  167.   
  168. }  
  169.   
  170. static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb)  
  171. {  
  172.     /*直接复制2层包头到套接字的包数据空间中*/  
  173.     memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);  
  174.     skb_push(skb, hh_len);  
  175.       
  176.     /*调用hh->hh_output(skb),也就是dev_queue_xmit进行硬件发送*/  
  177.     return hh->hh_output(skb);  
  178. }  

如果IP是上送本地CPU的报文,处理流程如下

[cpp]  view plain copy
  1. //-----------------------------------------------------------------------------------------------------------------------------------------------------------------------  
  2. /*包的本地投递*/  
  3. int ip_local_deliver(struct sk_buff *skb)  
  4. {  
  5.     /*收集并组装IP分片,如果还没有收集完成,那么就等待IP分片组装完成*/  
  6.     if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {  
  7.         if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))  
  8.             return 0;  
  9.     }  
  10.   
  11.     /*进入NF_IP_LOCAL_IN的过滤器处理,处理完成后进入ip_local_deliver_finish*/  
  12.     return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL, ip_local_deliver_finish);  
  13. }  
  14.   
  15.   
  16. /*IP层处理完成后的协议分发函数*/  
  17. static int ip_local_deliver_finish(struct sk_buff *skb)  
  18. {  
  19.     resubmit:  
  20.         /*如果是RAW-IP报文,送往RAW-IP对应的处理???*/  
  21.         raw = raw_local_deliver(skb, protocol);  
  22.   
  23.         /*MAX_INET_PROTOS-1 为IP报头中协议的模, 
  24.          *这里计算对应协议在ipprot中被散列的位置*/  
  25.         hash = protocol & (MAX_INET_PROTOS - 1);  
  26.         /*IP层上的ipprot负责管理所有的传输协议*/  
  27.         ipprot = rcu_dereference(inet_protos[hash]);  
  28.           
  29.         /*如果找到相应的协议,那么调用对应的处理例程*/  
  30.         if (ipprot != NULL) {             
  31.             ret = ipprot->handler(skb);  
  32.             if (ret < 0) {  
  33.                 protocol = -ret;  
  34.                 goto resubmit;  
  35.             }             
  36.         }  
  37.         /*找不到相应的处理例程*/   
  38.         else {  
  39.             /*又是RAW-IP报文,会在RAW-IP处理例程??? 
  40.              * 就丢弃,并想对端发送ICMP_DEST_UNREACH,ICMP_PROT_UNREACH*/  
  41.             if (!raw)  
  42.              {  
  43.                 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);  
  44.             }   
  45.   
  46.             kfree_skb(skb);  
  47.         }  
  48.   
  49.     return 0;  
  50. }  
  51.   
  52. static const struct net_protocol tcp_protocol = {  
  53.     .handler =  tcp_v4_rcv, /*TCP*/  
  54. };  
  55.   
  56. static const struct net_protocol udp_protocol = {  
  57.     .handler =  udp_rcv, /*UDP*/  
  58. };  
  59.   
  60. static const struct net_protocol icmp_protocol = {  
  61.     .handler =  icmp_rcv, /*ICMP*/  
  62. };  
  63.   
  64. static const struct net_protocol igmp_protocol = {  
  65.     .handler =  igmp_rcv, /*IGMP*/  
  66. };  

通过上面的分析讲解,我们就可以很清楚的了解到IP是如何接受一个来自于网卡的数据包,并如何进行三层报文


关于二层数据报文的处理流程和是如何送到三层进行处理,请参考我前面的博客

《Linux内核二层数据包接收流程》


希望大家批评指正


original link:http://blog.csdn.net/eric_liufeng/article/details/10789811

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值