Chinaunix首页 | 论坛 | 博客
  • 博客访问: 795020
  • 博文数量: 264
  • 博客积分: 592
  • 博客等级: 中士
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-24 22:02
文章分类

全部博文(264)

文章存档

2019年(2)

2018年(1)

2017年(1)

2016年(4)

2015年(14)

2014年(57)

2013年(88)

2012年(97)

分类: LINUX

2014-02-12 15:36:27

转:http://blog.csdn.net/nerdx/article/details/12450055
  1. //参考 深入理解linux网络技术内幕  

                                    


  1. //  注册l3协议  
  2. //      ptype_all链表,链接所有ETH_P_ALL类型的l3协议  
  3. //      ptype_base哈希表,非ETH_P_ALL类型的l3协议  
  4. //  注:l3协议可以使用相同的协议号  
  5.   
  6. 1.1 void dev_add_pack(struct packet_type *pt)  
  7. {  
  8.     int hash;  
  9.     //ptype_all ptype_base共用一把锁 ptype_lock  
  10.     spin_lock_bh(&ptype_lock);  
  11.     //ETH_P_ALL类型的l3协议,从外接收到的数据帧,和从本地发送的数据帧,都会向ptype_all链表中的l3协议,传递一份  
  12.     if (pt->type == htons(ETH_P_ALL)) {  
  13.         netdev_nit++;  
  14.         list_add_rcu(&pt->list, &ptype_all);  
  15.     } else {  
  16.         hash = ntohs(pt->type) & 15;//ptype_base有16个bucket  
  17.         list_add_rcu(&pt->list, &ptype_base[hash]);//链接到bucket的链表中  
  18.     }  
  19.     spin_unlock_bh(&ptype_lock);  
  20. }  
  21.   
  22.   
  23. //  ip协议控制块  
  24. //      每个l3协议,通过struct packet_type描述自己  
  25. 1.2 static struct packet_type ip_packet_type = {  
  26.     .type = __constant_htons(ETH_P_IP),//.type使用网络字节序,大端模式  
  27.     .func = ip_rcv,//ip接收函数  
  28. };  
  29.   
  30.   
  31. //  ip协议数据接收  
  32. //  调用路径:netif_receive_skb->ip_rcv  
  33.   
  34. //  函数主要任务:  
  35. //      1.使处理程序拥有独立的skb  
  36. //      2.检验ip数据包的有效性  
  37. //      3.处理l2填充  
  38.   
  39. //  ip数据包有效性:  
  40. //      1.报头长度  
  41. //      2.协议号  
  42. //      3.ip校验和  
  43. //      4.ip报文总长度  
  44.   
  45. //  处理l2填充:  
  46. //      1.当skb中指示的报文总长度大于ip报头指示的数据包总长度时,说明发生了l2填充  
  47. //      2.以ip报头指示的数据包总长度为正确,去掉末尾多余的填充数据,同时使l4校验和失效    
  48.   
  49. 1.3 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)  
  50. {  
  51.     struct iphdr *iph;  
  52.     //skb->pkt_type由接收设备的驱动程序,调用eth_type_trans设定  
  53.     //当设备处于混杂模式下,对于非本机,非广播,非多播的数据帧,设置其pkt_type=PACKET_OTHERHOST  
  54.     //ip协议不处理l2地址非本机的封包,此类型的封包应该在ip协议之前被bonding,或者网桥来处理  
  55.     if (skb->pkt_type == PACKET_OTHERHOST)  
  56.         goto drop;  
  57.   
  58.     //更新snmp统计信息  
  59.     IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);  
  60.     //如果skb引用计数不为1,则拷贝一份  
  61.     if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {  
  62.         IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);  
  63.         goto out;  
  64.     }  
  65.     //使skb->data到skb->tail之间足够20字节的ip头(此时不包括选项字段)  
  66.     if (!pskb_may_pull(skb, sizeof(struct iphdr)))  
  67.         goto inhdr_error;  
  68.   
  69.     iph = skb->nh.iph;//ip头  
  70.   
  71.     //报文有效性检查  
  72.     //  1.报头长度  
  73.     //  2.协议号  
  74.     //  3.ip校验和  
  75.     //  4.ip报文总长度  
  76.     if (iph->ihl < 5 || iph->version != 4)  
  77.         goto inhdr_error;   
  78.   
  79.     //ihl以4字节为单位,表示ip头的长度  
  80.     if (!pskb_may_pull(skb, iph->ihl*4))  
  81.         goto inhdr_error;  
  82.   
  83.     //对ip头和选项(如果存在的话),计算校验和  
  84.     if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)  
  85.         goto inhdr_error;   
  86.   
  87.     {  
  88.         __u32 len = ntohs(iph->tot_len); //ip报文总长(包括ip报头,选项,ip有效载荷)  
  89.         if (skb->len < len || len < (iph->ihl<<2))//skb中l3数据总长度小于接收到的长度,或者接收到的长度不足ip报头和选项的长度  
  90.             goto inhdr_error;  
  91.   
  92.         //处理l2填充:  
  93.         //  1.当skb中指示的报文总长度大于ip报头指示的数据包总长度时,说明发生了l2填充  
  94.         //  2.以ip报头指示的数据包总长度为正确,去掉末尾多余的填充数据,同时使l4校验和失效    
  95.         if (skb->len > len) {  
  96.             __pskb_trim(skb, len);  
  97.             if (skb->ip_summed == CHECKSUM_HW)//如果校验和已经由硬件求得,但是由于对数据进行了切割操作  
  98.                 skb->ip_summed = CHECKSUM_NONE;//则指示需要网络层通过软件再进行校验和计算  
  99.         }  
  100.     }  
  101.   
  102.     return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,  
  103.                ip_rcv_finish);//经过netfilter钩子函数的处理,最终由ip_rcv_finish完成处理  
  104.   
  105.   
  106. inhdr_error:  
  107.     IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);  
  108. drop:  
  109.         kfree_skb(skb);  
  110. out:  
  111.         return NET_RX_DROP;  
  112. }  
  113.   
  114.   
  115. //  ip协议数据接收  
  116.   
  117. //  函数主要任务:  
  118. //      1.路由数据包  
  119. //      2.如果ip报文存在选项,分析ip选项  
  120. //      3.处理源路由选项  
  121. //      4.根据路由子系统对skb->dst的设置,继续处理skb  
  122.   
  123. 1.4 static inline int ip_rcv_finish(struct sk_buff *skb)  
  124. {  
  125.     struct net_device *dev = skb->dev;  
  126.     struct iphdr *iph = skb->nh.iph;  
  127.   
  128.   
  129.     if (skb->dst == NULL) {//目标路由未设置  
  130.         if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))//输入路径上的路由,设置skb->dst  
  131.             goto drop;   
  132.     }  
  133.     ...  
  134.   
  135.   
  136.     if (iph->ihl > 5) {//需要处理ip选项  
  137.         struct ip_options *opt;  
  138.         //当ip报头的长度大于20字节,说明有选项需要处理,此时通过skb_cow,如果缓存区和别人共享,就会做出该缓冲区的副本,因为对缓存区具有排他拥有权  
  139.         if (skb_cow(skb, skb_headroom(skb))) {//是必要的,因为需要处理选项,就可能会修改ip报头,复制缓存区时,保证副本预留16字节的l2头  
  140.             IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);  
  141.             goto drop;  
  142.         }  
  143.         iph = skb->nh.iph;  
  144.         //在报文接收路径的多处,由不同函数对各个选项进行处理  
  145.         if (ip_options_compile(NULL, skb))//此处分析ip选项,初始化skb->cb中ip_options结构,为后边处理ip选项做准备。  
  146.             goto inhdr_error;  
  147.   
  148.         opt = &(IPCB(skb)->opt);  
  149.         if (opt->srr) {//ip_options_compile分析ip报文中的选项,设置opt->srr为源路由选项相对于ip头的起始位置  
  150.             struct in_device *in_dev = in_dev_get(dev);  
  151.             if (in_dev) {  
  152.                 if (!IN_DEV_SOURCE_ROUTE(in_dev)) {//入口设备的inet配置不支持源路由选项  
  153.                       
  154.                     in_dev_put(in_dev);  
  155.                     goto drop;//直接丢弃封包  
  156.                 }  
  157.                 in_dev_put(in_dev);  
  158.             }  
  159.             if (ip_options_rcv_srr(skb))//处理源路由选项  
  160.                 goto drop;  
  161.         }  
  162.     }  
  163.     //skb->dst->input处理,input函数指针由ip_route_input,根据目标地址,进行设置。  
  164.     return dst_input(skb);  
  165.   
  166.   
  167. inhdr_error:  
  168.     IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);  
  169. drop:  
  170.         kfree_skb(skb);  
  171.         return NET_RX_DROP;  
  172. }  
阅读(410) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~