首页 > 代码库 > Linux内核--网络栈实现分析(四)--网络层之IP协议(上)

Linux内核--网络栈实现分析(四)--网络层之IP协议(上)

本文分析基于Linux Kernel 1.2.13

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7514017

更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明

注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析、”(下)“表示分析是从上向下分析。

 

简单分析了链路层之后,上升到网络层来分析,看看链路层是如何为其上层--网络层服务的。其实在驱动程序层和网络层直接还有一层是接口层,叫做驱动程序接口层,用来整合不同的网络设备。接口层的内容会在上下层中提及。这里我们分析网络IP协议的实现原理。

其实现的文件主要是net/inet/ip.c文件中

我们首先分析下ip_init()初始化函数

这个函数是如何被调用的呢?

下面是调用的过程:

首先是在系统启动过程main.c中调用了sock_init()函数

 

[cpp] view plaincopy
  1. void sock_init(void)//网络栈初始化  
  2. {  
  3.     int i;  
  4.   
  5.     printk("Swansea University Computer Society NET3.019\n");  
  6.   
  7.     /* 
  8.      *  Initialize all address (protocol) families.  
  9.      */  
  10.        
  11.     for (i = 0; i < NPROTO; ++i) pops[i] = NULL;  
  12.   
  13.     /* 
  14.      *  Initialize the protocols module.  
  15.      */  
  16.   
  17.     proto_init();  
  18.   
  19. #ifdef CONFIG_NET  
  20.     /*  
  21.      *  Initialize the DEV module.  
  22.      */  
  23.   
  24.     dev_init();  
  25.     
  26.     /* 
  27.      *  And the bottom half handler  
  28.      */  
  29.   
  30.     bh_base[NET_BH].routine= net_bh;//设置NET 下半部分的处理函数为net_bh  
  31.     enable_bh(NET_BH);  
  32. #endif    
  33. }  

然后调用了proto_init()函数

 

 

[cpp] view plaincopy
  1. void proto_init(void)  
  2. {  
  3.     extern struct net_proto protocols[];    /* Network protocols 全局变量,定义在protocols.c中*/  
  4.     struct net_proto *pro;  
  5.   
  6.     /* Kick all configured protocols. */  
  7.     pro = protocols;  
  8.     while (pro->name != NULL) //对所有的定义的域进行初始化  
  9.     {  
  10.         (*pro->init_func)(pro);  
  11.         pro++;  
  12.     }  
  13.     /* We‘re all done... */  
  14. }  

而protocols全局变量协议向量表的定义中对INET域中协议的初始化函数设置为inet_proto_init()

 

 

[cpp] view plaincopy
  1. /* 
  2.  *  Protocol Table 
  3.  */  
  4.    
  5. struct net_proto protocols[] = {  
  6. #ifdef  CONFIG_UNIX  
  7.   { "UNIX", unix_proto_init },  
  8. #endif  
  9. #if defined(CONFIG_IPX)||defined(CONFIG_ATALK)    
  10.   { "802.2",    p8022_proto_init },  
  11.   { "SNAP", snap_proto_init },  
  12. #endif  
  13. #ifdef CONFIG_AX25    
  14.   { "AX.25",    ax25_proto_init },  
  15. #endif    
  16. #ifdef  CONFIG_INET  
  17.   { "INET", inet_proto_init },  
  18. #endif  
  19. #ifdef  CONFIG_IPX  
  20.   { "IPX",  ipx_proto_init },  
  21. #endif  
  22. #ifdef CONFIG_ATALK  
  23.   { "DDP",  atalk_proto_init },  
  24. #endif  
  25.   { NULL,   NULL        }  
  26. };  

看到在inet_proto_init()函数中调用了ip_init()对IP层进行了初始化。

 

 

[cpp] view plaincopy
  1. void inet_proto_init(struct net_proto *pro)//INET域协议初始化函数  
  2. {  
  3.     struct inet_protocol *p;  
  4.     int i;  
  5.   
  6.   
  7.     printk("Swansea University Computer Society TCP/IP for NET3.019\n");  
  8.   
  9.     /* 
  10.      *  Tell SOCKET that we are alive...  
  11.      */  
  12.      
  13.     (void) sock_register(inet_proto_ops.family, &inet_proto_ops);  
  14.   
  15.     ...........................................  
  16.   
  17.     printk("IP Protocols: ");  
  18.     for(p = inet_protocol_base; p != NULL;) //将inet_protocol_base指向的一个inet_protocol结构体加入数组inet_protos中  
  19.     {  
  20.         struct inet_protocol *tmp = (struct inet_protocol *) p->next;  
  21.         inet_add_protocol(p);  
  22.         printk("%s%s",p->name,tmp?", ":"\n");  
  23.         p = tmp;  
  24.     }  
  25.     /* 
  26.      *  Set the ARP module up 
  27.      */  
  28.     arp_init();//对地址解析层进行初始化  
  29.     /* 
  30.      *  Set the IP module up 
  31.      */  
  32.     ip_init();//对IP层进行初始化  
  33. }  
代码中inet_protocol_base指向的链表为&igmp_protocol-->&icmp_protocol-->&udp_protocol-->&tcp_protocol-->NULL(定义在protocol.c中)

 

分析ip_init()函数需要先要知道packet_type结构,这个结构体是网络层协议的结构体,网络层协议与该结构体一一对应。

 

[cpp] view plaincopy
  1. /*该结构用于表示网络层协议,网络层协议与packt_type一一对应*/  
  2. struct packet_type {  
  3.   unsigned short    type;   /* This is really htons(ether_type). ,对应的网络层协议编号*/  
  4.   struct device *   dev;  
  5.   int           (*func) (struct sk_buff *, struct device *,  
  6.                  struct packet_type *);  
  7.   void          *data;  
  8.   struct packet_type    *next;  
  9. };  
第一个字段的网络层协议编号定义在include/linux/if_ether.h中

 

 

[cpp] view plaincopy
  1. /* These are the defined Ethernet Protocol ID‘s. */  
  2. #define ETH_P_LOOP  0x0060      /* Ethernet Loopback packet */  
  3. #define ETH_P_ECHO  0x0200      /* Ethernet Echo packet     */  
  4. #define ETH_P_PUP   0x0400      /* Xerox PUP packet     */  
  5. #define ETH_P_IP    0x0800      /* Internet Protocol packet */  
  6. #define ETH_P_ARP   0x0806      /* Address Resolution packet    */  
  7. #define ETH_P_RARP      0x8035      /* Reverse Addr Res packet  */  
  8. #define ETH_P_X25   0x0805      /* CCITT X.25           */  
  9. #define ETH_P_ATALK 0x809B      /* Appletalk DDP        */  
  10. #define ETH_P_IPX   0x8137      /* IPX over DIX         */  
  11. #define ETH_P_802_3 0x0001      /* Dummy type for 802.3 frames  */  
  12. #define ETH_P_AX25  0x0002      /* Dummy protocol id for AX.25  */  
  13. #define ETH_P_ALL   0x0003      /* Every packet (be careful!!!) */  
  14. #define ETH_P_802_2 0x0004      /* 802.2 frames         */  
  15. #define ETH_P_SNAP  0x0005      /* Internal only        */  

第二个字段表示处理包的网络接口设备,一般初始化为NULL。

 

第三个字段为相应网络协议的处理函数。

第四个字段是一个void指针。

第五个字段是next指针域,用于将该结构连接成链表。

下面是ip_init()函数

 

[cpp] view plaincopy
  1. /* 
  2.  *  IP registers the packet type and then calls the subprotocol initialisers 
  3.  */  
  4.   
  5. void ip_init(void)//该函数在af_inet.c文件中  
  6. {  
  7.     ip_packet_type.type=htons(ETH_P_IP);  
  8.     dev_add_pack(&ip_packet_type);//将网络协议插入IP协议链表,头插法  
  9.   
  10.     /* So we flush routes when a device is downed */      
  11.     register_netdevice_notifier(&ip_rt_notifier);//将其插入通知链表  
  12. /*  ip_raw_init(); 
  13.     ip_packet_init(); 
  14.     ip_tcp_init(); 
  15.     ip_udp_init();*/  
  16. }  

这里需要说明的是系统采用主动通知的方式,其实现是有赖于notifier_block结构,其定义在notifier.h中

 

 

[cpp] view plaincopy
  1. struct notifier_block  
  2. {  
  3.     int (*notifier_call)(unsigned long, void *);  
  4.     struct notifier_block *next;  
  5.     int priority;  
  6. };  
对于网卡设备而言,网卡设备的启动和关闭是事件,内核需要得到通知从而采取相应的措施。其原理是:当事件发生时,事件通知者便利某个队列,对队列中感兴趣(符合条件)的被通知者调用被通知者注册是定义的通知处理函数,从而达到让内核做出相应的操作。

 

当硬件缓冲区数据填满后,会执行中断处理程序,以NE 8390网卡为例,由于在ne.c文件中注册中断时的中断处理函数设置如下:

 

[cpp] view plaincopy
  1. request_irq (dev->irq, ei_interrupt, 0, wordlength==2 ? "ne2000":"ne1000");  
中断处理函数为ei_interrupt()。执行ei_interrupt()函数时会调用函数ei_recieve(),而ei_recieve()函数会调用netif_rx()函数将以skb_buf的形式发送给上层。

 

当然netif_rx()函数的特点前面分析过,即Bottom Half技术,使得中断处理过程有效的缩短,提高系统的效率。在下半段该函数会调用dev_transmit()函数,而它会调用函数dev_tint()函数,dev_tinit()会调用函数dev_queue_xmit(),这个函数会调用dev->hard_start_xmit函数,该函数指针在ethdev_init()函数中赋值了:

 

[cpp] view plaincopy
  1. dev->hard_start_xmit = &ei_start_xmit;//设备的发送函数,定义在8390.c中  
最后调用ei_start_xmit()函数将数据包从硬件设备中读出放在skb中,即存放到内核空间中。

 

中断返回后系统会执行下半段,即执行net_bh()函数,该函数会扫描网络协议队列,调用相应的协议的接收函数,IP协议就会调用ip_rcv()

 

[cpp] view plaincopy
  1. /* 
  2.  *  When we are called the queue is ready to grab, the interrupts are 
  3.  *  on and hardware can interrupt and queue to the receive queue a we 
  4.  *  run with no problems. 
  5.  *  This is run as a bottom half after an interrupt handler that does 
  6.  *  mark_bh(NET_BH); 
  7.  */  
  8.    
  9. void net_bh(void *tmp)  
  10. {  
  11. ...................................  
  12.        
  13.     while((skb=skb_dequeue(&backlog))!=NULL)//出队直到队列为空  
  14.     {  
  15.         ...............................  
  16.            /* 
  17.         *   Fetch the packet protocol ID.  This is also quite ugly, as 
  18.         *   it depends on the protocol driver (the interface itself) to 
  19.         *   know what the type is, or where to get it from.  The Ethernet 
  20.         *   interfaces fetch the ID from the two bytes in the Ethernet MAC 
  21.         *   header (the h_proto field in struct ethhdr), but other drivers 
  22.         *   may either use the ethernet ID‘s or extra ones that do not 
  23.         *   clash (eg ETH_P_AX25). We could set this before we queue the 
  24.         *   frame. In fact I may change this when I have time. 
  25.         */  
  26.           
  27.         type = skb->dev->type_trans(skb, skb->dev);//取出该数据包所属的协议类型  
  28.   
  29.         /* 
  30.          *  We got a packet ID.  Now loop over the "known protocols" 
  31.          *  table (which is actually a linked list, but this will 
  32.          *  change soon if I get my way- FvK), and forward the packet 
  33.          *  to anyone who wants it. 
  34.          * 
  35.          *  [FvK didn‘t get his way but he is right this ought to be 
  36.          *  hashed so we typically get a single hit. The speed cost 
  37.          *  here is minimal but no doubt adds up at the 4,000+ pkts/second 
  38.          *  rate we can hit flat out] 
  39.          */  
  40.         pt_prev = NULL;  
  41.         for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) //遍历ptype_base所指向的网络协议队列  
  42.         {  
  43.             //判断协议号是否匹配  
  44.             if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev))  
  45.             {  
  46.                 /* 
  47.                  *  We already have a match queued. Deliver 
  48.                  *  to it and then remember the new match 
  49.                  */  
  50.                 if(pt_prev)  
  51.                 {  
  52.                     struct sk_buff *skb2;  
  53.   
  54.                     skb2=skb_clone(skb, GFP_ATOMIC);//复制数据包结构  
  55.   
  56.                     /* 
  57.                      *  Kick the protocol handler. This should be fast 
  58.                      *  and efficient code. 
  59.                      */  
  60.   
  61.                     if(skb2)  
  62.                         pt_prev->func(skb2, skb->dev, pt_prev);//调用相应协议的处理函数,  
  63.                                                                 //这里和网络协议的种类有关系  
  64.                                                                 //如IP 协议的处理函数就是ip_rcv  
  65.                 }  
  66.                 /* Remember the current last to do */  
  67.                 pt_prev=ptype;  
  68.             }  
  69.         } /* End of protocol list loop */  
  70.           
  71.     ...........................................  
  72. }  
IP数据包类型的初始化设置在ip.c中
[cpp] view plaincopy
  1. /* 
  2.  *  IP protocol layer initialiser 
  3.  */  
  4.   
  5. static struct packet_type ip_packet_type =  
  6. {  
  7.     0,  /* MUTTER ntohs(ETH_P_IP),*/  
  8.     NULL,   /* All devices */  
  9.     ip_rcv,  
  10.     NULL,  
  11.     NULL,  
  12. };  


 

接下来分析ip_rcv()函数,这是IP层的接收函数,接收来自链路层的数据。


这里首先了解一下IP数据包的首部结构,结构示意图如下:

标志位三位,分别是:保留,DF(可以分片),MF(还有后续分片)。

内核中对应的结构体定义如下(include/linux/ip.h):

 

[cpp] view plaincopy
  1. /*IP数据包首部结构体*/  
  2. struct iphdr {  
  3. #if defined(LITTLE_ENDIAN_BITFIELD)  
  4.     __u8    ihl:4,  
  5.         version:4;  
  6. #elif defined (BIG_ENDIAN_BITFIELD)  
  7.     __u8    version:4,  
  8.         ihl:4;  
  9. #else  
  10. #error  "Please fix <asm/byteorder.h>"  
  11. #endif  
  12.     __u8    tos;//服务类型  
  13.     __u16   tot_len;//总长度  
  14.     __u16   id;//标示  
  15.     __u16   frag_off;//标志和片偏移  
  16.     __u8    ttl;//生存时间  
  17.     __u8    protocol;//协议  
  18.     __u16   check;//头部校验和  
  19.     __u32   saddr;//源地址  
  20.     __u32   daddr;//目的地址  
  21.     /*The options start here. */  
  22. };  


 

内核中用于封装网络数据的最重要的数据结构sk_buff定义在include/linux/skbuff.h中:

 

 

[cpp] view plaincopy
  1. struct sk_buff {  
  2.   struct sk_buff        * volatile next;//指针域,指向后继  
  3.   struct sk_buff        * volatile prev;//指针域,指向前驱  
  4. #if CONFIG_SKB_CHECK  
  5.   int               magic_debug_cookie;  
  6. #endif  
  7.   struct sk_buff        * volatile link3;//该指针域用于TCP协议,指向数据包重发队列  
  8.   struct sock           *sk;//该数据指向的套接字  
  9.   volatile unsigned long    when;   /* used to compute rtt‘s    用于计算往返时间*/  
  10.   struct timeval        stamp;  
  11.   struct device         *dev;//标示发送或接收该数据包的接口设备  
  12.   struct sk_buff        *mem_addr;//指向该sk_buff结构指向的内存基地址,用于该数据结构的内存释放  
  13.   union {//该联合结构用于实现通用  
  14.     struct tcphdr   *th;  
  15.     struct ethhdr   *eth;//链路层有效  
  16.     struct iphdr    *iph;//网络层有效  
  17.     struct udphdr   *uh;  
  18.     unsigned char   *raw;  
  19.     unsigned long   seq;//TCP协议有效,表示该数据包的ACK值  
  20.   } h;  
  21.   struct iphdr      *ip_hdr;        /* For IPPROTO_RAW ,指向IP首部指针,用于RAW套接字*/  
  22.   unsigned long         mem_len;//表示该结构的大小和数据帧的总大小  
  23.   unsigned long         len;//表示数据帧大小  
  24.   unsigned long         fraglen;//表示分片个数  
  25.   struct sk_buff        *fraglist;  /* Fragment list ,分片数据包队列*/  
  26.   unsigned long         truesize;//==mem_len  
  27.   unsigned long         saddr;//源地址  
  28.   unsigned long         daddr;//目的地址  
  29.   unsigned long         raddr;//数据包的下一站地址  
  30.   volatile char         acked,//==1表示该数据包已经得到确认  
  31.                 used,//==1表示该数据包已经被数据包用完,可以释放  
  32.                 free,//==1表示该数据包用完后直接释放,不用缓存  
  33.                 arp;//==1表示MAC数据帧首部完成,否则表示MAC首部目的硬件地址尚不知晓,需使用ARP协议询问  
  34.   unsigned char         tries,//表示数据包已得到试发送  
  35.                         lock,//表示是否被其他程序使用  
  36.                         localroute,//表示是局域网路由还是广域网路由  
  37.                         pkt_type;//表示数据包的类型,分为,发往本机、广播、多播、发往其他主机  
  38. #define PACKET_HOST     0       /* To us */  
  39. #define PACKET_BROADCAST    1  
  40. #define PACKET_MULTICAST    2  
  41. #define PACKET_OTHERHOST    3       /* Unmatched promiscuous */  
  42.   unsigned short        users;      /* User count - see datagram.c (and soon seqpacket.c/stream.c) 使用该数据包的程序数目*/  
  43.   unsigned short        pkt_class;  /* For drivers that need to cache the packet type with the skbuff (new PPP) */  
  44. #ifdef CONFIG_SLAVE_BALANCING  
  45.   unsigned short        in_dev_queue;//表示数据包是否在设备缓冲队列  
  46. #endif    
  47.   unsigned long         padding[0];//填充  
  48.   unsigned char         data[0];//指向数据部分  
  49. };  

ip_rcv()函数流程图:

 

[cpp] view plaincopy
  1. /* 
  2.  *  This function receives all incoming IP datagrams. 
  3.  */  
  4.   
  5. int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)  
  6. {  
  7.     struct iphdr *iph = skb->h.iph;  
  8.     struct sock *raw_sk=NULL;  
  9.     unsigned char hash;  
  10.     unsigned char flag = 0;  
  11.     unsigned char opts_p = 0;   /* Set iff the packet has options. */  
  12.     struct inet_protocol *ipprot;//每个传输层协议对应一个inet_protocol,用于调用传输层的服务函数  
  13.     static struct options opt; /* since we don‘t use these yet, and they 
  14.                 take up stack space. */  
  15.     int brd=IS_MYADDR;  
  16.     int is_frag=0;  
  17. #ifdef CONFIG_IP_FIREWALL  
  18.     int err;  
  19. #endif    
  20.   
  21.     ip_statistics.IpInReceives++;  
  22.   
  23.     /* 
  24.      *  Tag the ip header of this packet so we can find it 
  25.      */  
  26.   
  27.     skb->ip_hdr = iph;//设置IP首部指针  
  28.   
  29.     /* 
  30.      *  Is the datagram acceptable? 
  31.      * 
  32.      *  1.  Length at least the size of an ip header 
  33.      *  2.  Version of 4 
  34.      *  3.  Checksums correctly. [Speed optimisation for later, skip loopback checksums] 
  35.      *  (4. We ought to check for IP multicast addresses and undefined types.. does this matter ?) 
  36.      */  
  37.     //IP数据包合法性检查  
  38.     if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 ||  
  39.         skb->len<ntohs(iph->tot_len) || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0)  
  40.     {  
  41.         ip_statistics.IpInHdrErrors++;  
  42.         kfree_skb(skb, FREE_WRITE);  
  43.         return(0);  
  44.     }  
  45.       
  46.     /* 
  47.      *  See if the firewall wants to dispose of the packet.  
  48.      */  
  49.   
  50. #ifdef  CONFIG_IP_FIREWALL  
  51.     //检查防火墙是否阻止该数据包,过滤数据包  
  52.     if ((err=ip_fw_chk(iph,dev,ip_fw_blk_chain,ip_fw_blk_policy, 0))!=1)  
  53.     {  
  54.         if(err==-1)  
  55.             icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);  
  56.         kfree_skb(skb, FREE_WRITE);  
  57.         return 0;     
  58.     }  
  59.   
  60. #endif  
  61.       
  62.     /* 
  63.      *  Our transport medium may have padded the buffer out. Now we know it 
  64.      *  is IP we can trim to the true length of the frame. 
  65.      */  
  66.   
  67.     skb->len=ntohs(iph->tot_len);  
  68.   
  69.     /* 
  70.      *  Next analyse the packet for options. Studies show under one packet in 
  71.      *  a thousand have options.... 
  72.      */  
  73.   
  74.     if (iph->ihl != 5)//IP数据报首部存在选项字段  
  75.     {   /* Fast path for the typical optionless IP packet. */  
  76.         memset((char *) &opt, 0, sizeof(opt));  
  77.         if (do_options(iph, &opt) != 0)  
  78.             return 0;  
  79.         opts_p = 1;  
  80.     }  
  81.   
  82.     /* 
  83.      *  Remember if the frame is fragmented. 
  84.      */  
  85.      //看该数据包是否含有分片,MF和偏移同时为0,则表示无分片,否则是分片,此处有BUG  
  86.      /* 
  87.      分片的条件 
  88.      第一个分片MF=1,offset=0 
  89.      中间分片MF=1,offset!=0 
  90.      最后分片MF=1,offset!=0 
  91.      */  
  92.     if(iph->frag_off)  
  93.     {  
  94.         if (iph->frag_off & 0x0020)  
  95.             is_frag|=1;  
  96.         /* 
  97.          *  Last fragment ? 
  98.          */  
  99.       
  100.         if (ntohs(iph->frag_off) & 0x1fff)  
  101.             is_frag|=2;  
  102.     }  
  103.       
  104.     /* 
  105.      *  Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday. 
  106.      * 
  107.      *  This is inefficient. While finding out if it is for us we could also compute 
  108.      *  the routing table entry. This is where the great unified cache theory comes 
  109.      *  in as and when someone implements it 
  110.      * 
  111.      *  For most hosts over 99% of packets match the first conditional 
  112.      *  and don‘t go via ip_chk_addr. Note: brd is set to IS_MYADDR at 
  113.      *  function entry. 
  114.      */  
  115.   
  116.     if ( iph->daddr != skb->dev->pa_addr && (brd = ip_chk_addr(iph->daddr)) == 0)  
  117.     {  
  118.         /* 
  119.          *  Don‘t forward multicast or broadcast frames.广播的数据报不转发 
  120.          */  
  121.   
  122.         if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)  
  123.         {  
  124.             kfree_skb(skb,FREE_WRITE);  
  125.             return 0;  
  126.         }  
  127.   
  128.         /* 
  129.          *  The packet is for another target. Forward the frame 
  130.          */  
  131.   
  132. #ifdef CONFIG_IP_FORWARD  
  133.         ip_forward(skb, dev, is_frag);//转发数据报  
  134. #else  
  135. /*      printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n", 
  136.             iph->saddr,iph->daddr);*/  
  137.         ip_statistics.IpInAddrErrors++;  
  138. #endif  
  139.         /* 
  140.          *  The forwarder is inefficient and copies the packet. We 
  141.          *  free the original now. 
  142.          */  
  143.   
  144.         kfree_skb(skb, FREE_WRITE);  
  145.         return(0);  
  146.     }  
  147.       
  148. #ifdef CONFIG_IP_MULTICAST    
  149.     //多播  
  150.     if(brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))  
  151.     {  
  152.         /* 
  153.          *  Check it is for one of our groups 
  154.          */  
  155.         struct ip_mc_list *ip_mc=dev->ip_mc_list;  
  156.         do  
  157.         {  
  158.             if(ip_mc==NULL)  
  159.             {     
  160.                 kfree_skb(skb, FREE_WRITE);  
  161.                 return 0;  
  162.             }  
  163.             if(ip_mc->multiaddr==iph->daddr)  
  164.                 break;  
  165.             ip_mc=ip_mc->next;  
  166.         }  
  167.         while(1);  
  168.     }  
  169. #endif  
  170.     /* 
  171.      *  Account for the packet 
  172.      */  
  173.        
  174. #ifdef CONFIG_IP_ACCT  
  175.     ip_acct_cnt(iph,dev, ip_acct_chain);  
  176. #endif    
  177.   
  178.     /* 
  179.      * Reassemble IP fragments. 
  180.      */  
  181.   
  182.     if(is_frag)//该数据报是一个分片,进行合并  
  183.     {  
  184.         /* Defragment. Obtain the complete packet if there is one */  
  185.         /*该函数的作用是梳理分片的数据报,如果接收当前分片后,所有分片均已到达 
  186.         *该函数会调用ip_glue()函数进行IP数据报的重组,否则将该IP数据报放到ipq中fragment 
  187.         *字段指向的队列中 
  188.         * 
  189.         */  
  190.         skb=ip_defrag(iph,skb,dev);  
  191.         if(skb==NULL)  
  192.             return 0;  
  193.         skb->dev = dev;  
  194.         iph=skb->h.iph;  
  195.     }  
  196.       
  197.            
  198.   
  199.     /* 
  200.      *  Point into the IP datagram, just past the header. 
  201.      */  
  202.   
  203.     skb->ip_hdr = iph;  
  204.     skb->h.raw += iph->ihl*4;//sk_buff中union类型的h字段永远指向当前正在处理的协议的首部,这里使其指向传输层的首部,用于传输层的处理  
  205.       
  206.     /* 
  207.      *  Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies. 
  208.      */  
  209.        
  210.     hash = iph->protocol & (SOCK_ARRAY_SIZE-1);  
  211.       
  212.     /* If there maybe a raw socket we must check - if not we don‘t care less */  
  213.     //处理RAW类型的套接字  
  214.     if((raw_sk=raw_prot.sock_array[hash])!=NULL)  
  215.     {  
  216.         struct sock *sknext=NULL;  
  217.         struct sk_buff *skb1;  
  218.         raw_sk=get_sock_raw(raw_sk, hash,  iph->saddr, iph->daddr);  
  219.         if(raw_sk)  /* Any raw sockets */  
  220.         {  
  221.             do  
  222.             {  
  223.                 /* Find the next */  
  224.                 sknext=get_sock_raw(raw_sk->next, hash, iph->saddr, iph->daddr);  
  225.                 if(sknext)  
  226.                     skb1=skb_clone(skb, GFP_ATOMIC);  
  227.                 else  
  228.                     break;  /* One pending raw socket left */  
  229.                 if(skb1)  
  230.                     raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr);//RAW类型套接字的接收函数  
  231.                 raw_sk=sknext;  
  232.             }  
  233.             while(raw_sk!=NULL);  
  234.             /* Here either raw_sk is the last raw socket, or NULL if none */  
  235.             /* We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy */  
  236.         }  
  237.     }  
  238.       
  239.     /* 
  240.      *  skb->h.raw now points at the protocol beyond the IP header. 
  241.      */  
  242.   
  243.     hash = iph->protocol & (MAX_INET_PROTOS -1);  
  244.     //对所有使用IP协议的上层协议套接字处理  
  245.     for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)  
  246.     {  
  247.         struct sk_buff *skb2;  
  248.   
  249.         if (ipprot->protocol != iph->protocol)  
  250.             continue;  
  251.        /* 
  252.     *   See if we need to make a copy of it.  This will 
  253.     *   only be set if more than one protocol wants it. 
  254.     *   and then not for the last one. If there is a pending 
  255.     *   raw delivery wait for that 
  256.     */  
  257.         if (ipprot->copy || raw_sk)  
  258.         {  
  259.             skb2 = skb_clone(skb, GFP_ATOMIC);  
  260.             if(skb2==NULL)  
  261.                 continue;  
  262.         }  
  263.         else  
  264.         {  
  265.             skb2 = skb;  
  266.         }  
  267.         flag = 1;  
  268.   
  269.            /* 
  270.         * Pass on the datagram to each protocol that wants it, 
  271.         * based on the datagram protocol.  We should really 
  272.         * check the protocol handler‘s return values here... 
  273.         */  
  274.         ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,  
  275.                 (ntohs(iph->tot_len) - (iph->ihl * 4)),  
  276.                 iph->saddr, 0, ipprot);  
  277.   
  278.     }  
  279.   
  280.     /* 
  281.      * All protocols checked. 
  282.      * If this packet was a broadcast, we may *not* reply to it, since that 
  283.      * causes (proven, grin) ARP storms and a leakage of memory (i.e. all 
  284.      * ICMP reply messages get queued up for transmission...) 
  285.      */  
  286.   
  287.     if(raw_sk!=NULL)    /* Shift to last raw user */  
  288.         raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr);  
  289.     else if (!flag)     /* Free and report errors */  
  290.     {  
  291.         if (brd != IS_BROADCAST && brd!=IS_MULTICAST)  
  292.             icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);  
  293.         kfree_skb(skb, FREE_WRITE);  
  294.     }  
  295.   
  296.     return(0);  
  297. }  

这里会进一步调用raw_rcv()或者相应协议的ipprot->handler来调用传输层服务函数。下篇会进行简单分析。

Linux内核--网络栈实现分析(四)--网络层之IP协议(上)