zoukankan      html  css  js  c++  java
  • Linux内核网络栈实现分析(十)网络层之IP协议(下)

    本文分析基于Linux Kernel 1.2.13

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

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

    作者:闫明

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


    上篇博文分析传输层最终会调用函数ip_queue_xmit()函数,将发送数据的任务交给网络层,下面就分析了下该函数:

    该函数的主要函数调用关系图如下:


    1. /* 
    2.  * Queues a packet to be sent, and starts the transmitter 
    3.  * if necessary.  if free = 1 then we free the block after 
    4.  * transmit, otherwise we don't. If free==2 we not only 
    5.  * free the block but also don't assign a new ip seq number. 
    6.  * This routine also needs to put in the total length, 
    7.  * and compute the checksum 
    8.  */  
    9.   
    10. void ip_queue_xmit(struct sock *sk, //发送数据的队列所对应的sock结构  
    11.                       struct device *dev,//发送该数据包的网卡设备  
    12.                       struct sk_buff *skb,//封装好的sk_buff结构,要发送的数据在该结构中  
    13.                       int free)//主要配合TCP协议使用,用于数据包的重发,UDP等协议调用是free=1  
    14. {  
    15.     struct iphdr *iph;//IP数据报首部指针  
    16.     unsigned char *ptr;  
    17.   
    18.     /* Sanity check */  
    19.     if (dev == NULL)  
    20.     {  
    21.         printk("IP: ip_queue_xmit dev = NULL\n");  
    22.         return;  
    23.     }  
    24.   
    25.     IS_SKB(skb);  
    26.   
    27.     /* 
    28.      *  Do some book-keeping in the packet for later 
    29.      */  
    30.   
    31.   
    32.     skb->dev = dev;//进一步完整sk_buff的相应字段  
    33.     skb->when = jiffies;//用于TCP协议的超时重传  
    34.   
    35.     /* 
    36.      *  Find the IP header and set the length. This is bad 
    37.      *  but once we get the skb data handling code in the 
    38.      *  hardware will push its header sensibly and we will 
    39.      *  set skb->ip_hdr to avoid this mess and the fixed 
    40.      *  header length problem 
    41.      */  
    42.   
    43.     ptr = skb->data;//指针指向sk_buff中的数据部分  
    44.     ptr += dev->hard_header_len;//hard_header_len为硬件首部长度,在net_init.c的函数eth_setup()函数中设置的,dev->hard_header_len = ETH_HLEN; 以太网首部长度为14  
    45.     iph = (struct iphdr *)ptr;//prt已经指向IP数据包的首部  
    46.     skb->ip_hdr = iph;  
    47.     iph->tot_len = ntohs(skb->len-dev->hard_header_len);//计算IP数据报的总长度  
    48.   
    49. #ifdef CONFIG_IP_FIREWALL  
    50.     if(ip_fw_chk(iph, dev, ip_fw_blk_chain, ip_fw_blk_policy, 0) != 1)  
    51.         /* just don't send this packet */  
    52.         return;  
    53. #endif    
    54.   
    55.     /* 
    56.      *  No reassigning numbers to fragments... 
    57.      */  
    58.   
    59.     if(free!=2)  
    60.         iph->id      = htons(ip_id_count++);  
    61.     else  
    62.         free=1;  
    63.   
    64.     /* All buffers without an owner socket get freed */  
    65.     if (sk == NULL)  
    66.         free = 1;  
    67.   
    68.     skb->free = free;//设置skb的free值,free=1,发送后立即释放;free=2,不但释放缓存,而且不分配新的序列号  
    69.   
    70.     /* 
    71.      *  Do we need to fragment. Again this is inefficient. 
    72.      *  We need to somehow lock the original buffer and use 
    73.      *  bits of it. 
    74.      */  
    75.     //数据帧中的数据部分必须小于等于MTU  
    76.     if(skb->len > dev->mtu + dev->hard_header_len)//发送的数据长度大于数据帧的数据部分和帧首部之和,则需要分片  
    77.     {  
    78.         ip_fragment(sk,skb,dev,0);//对数据报分片后继续调用ip _queue_xmit()函数发送数据  
    79.         IS_SKB(skb);  
    80.         kfree_skb(skb,FREE_WRITE);  
    81.         return;  
    82.     }  
    83.   
    84.     /* 
    85.      *  Add an IP checksum 
    86.      */  
    87.   
    88.     ip_send_check(iph);//IP数据报首部检查  
    89.   
    90.     /* 
    91.      *  Print the frame when debugging 
    92.      */  
    93.   
    94.     /* 
    95.      *  More debugging. You cannot queue a packet already on a list 
    96.      *  Spot this and moan loudly. 
    97.      */  
    98.     if (skb->next != NULL)//说明该数据包仍然存在于某个缓存队列  
    99.     {  
    100.         printk("ip_queue_xmit: next != NULL\n");  
    101.         skb_unlink(skb);//将其从缓存链表中删除,否则可能导致内核错误  
    102.     }  
    103.   
    104.     /* 
    105.      *  If a sender wishes the packet to remain unfreed 
    106.      *  we add it to his send queue. This arguably belongs 
    107.      *  in the TCP level since nobody else uses it. BUT 
    108.      *  remember IPng might change all the rules. 
    109.      */  
    110.   
    111.     if (!free)//free=0  
    112.     {  
    113.         unsigned long flags;  
    114.         /* The socket now has more outstanding blocks */  
    115.   
    116.         sk->packets_out++;  
    117.   
    118.         /* Protect the list for a moment */  
    119.         save_flags(flags);  
    120.         cli();  
    121.   
    122.         if (skb->link3 != NULL)//link3指向数据报道呃重发队列  
    123.         {  
    124.             printk("ip.c: link3 != NULL\n");  
    125.             skb->link3 = NULL;  
    126.         }  
    127.         //sk中send_tail和send_head是用户缓存的单向链表表尾和表头  
    128.         if (sk->send_head == NULL)  
    129.         {  
    130.             sk->send_tail = skb;  
    131.             sk->send_head = skb;  
    132.         }  
    133.         else  
    134.         {  
    135.             sk->send_tail->link3 = skb;//link3指针用于数据包的连接  
    136.             sk->send_tail = skb;  
    137.         }  
    138.         /* skb->link3 is NULL */  
    139.   
    140.         /* Interrupt restore */  
    141.         restore_flags(flags);  
    142.     }  
    143.     else  
    144.         /* Remember who owns the buffer */  
    145.         skb->sk = sk;  
    146.   
    147.     /* 
    148.      *  If the indicated interface is up and running, send the packet. 
    149.      */  
    150.        
    151.     ip_statistics.IpOutRequests++;  
    152. #ifdef CONFIG_IP_ACCT  
    153.     ip_acct_cnt(iph,dev, ip_acct_chain);  
    154. #endif    
    155.       
    156. #ifdef CONFIG_IP_MULTICAST  //这部分是IP数据报的多播处理  
    157.   
    158.     /* 
    159.      *  Multicasts are looped back for other local users 
    160.      */  
    161.        
    162.     .......................................  
    163. #endif  
    164.     if((dev->flags&IFF_BROADCAST) && iph->daddr==dev->pa_brdaddr && !(dev->flags&IFF_LOOPBACK))//广播数据包的处理  
    165.         ip_loopback(dev,skb);  
    166.           
    167.     if (dev->flags & IFF_UP)//设备状态正常  
    168.     {  
    169.         /* 
    170.          *  If we have an owner use its priority setting, 
    171.          *  otherwise use NORMAL 
    172.          */  
    173.         //调用设备接口层函数发送数据: dev_queue_xmit()函数  
    174.         if (sk != NULL)  
    175.         {  
    176.             dev_queue_xmit(skb, dev, sk->priority);  
    177.         }  
    178.         else  
    179.         {  
    180.             dev_queue_xmit(skb, dev, SOPRI_NORMAL);  
    181.         }  
    182.     }  
    183.     else//设备状态不正常  
    184.     {  
    185.         ip_statistics.IpOutDiscards++;  
    186.         if (free)  
    187.             kfree_skb(skb, FREE_WRITE);  
    188.     }  
    189. }  

    这个函数中对长度过长的数据包进行了分片,ip_fragment()函数,该函数没有详细分析。

    1. void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)  
    2. {  
    3.     struct iphdr *iph;  
    4.     unsigned char *raw;  
    5.     unsigned char *ptr;  
    6.     struct sk_buff *skb2;  
    7.     int left, mtu, hlen, len;  
    8.     int offset;  
    9.     unsigned long flags;  
    10.   
    11.     /* 
    12.      *  Point into the IP datagram header. 
    13.      */  
    14.   
    15.     raw = skb->data;  
    16.     iph = (struct iphdr *) (raw + dev->hard_header_len);  
    17.   
    18.     skb->ip_hdr = iph;  
    19.   
    20.     /* 
    21.      *  Setup starting values. 
    22.      */  
    23.   
    24.     hlen = (iph->ihl * sizeof(unsigned long));  
    25.     left = ntohs(iph->tot_len) - hlen;   /* Space per frame */  
    26.     hlen += dev->hard_header_len;        /* Total header size */  
    27.     mtu = (dev->mtu - hlen);     /* Size of data space */  
    28.     ptr = (raw + hlen);         /* Where to start from */  
    29.   
    30.     /* 
    31.      *  Check for any "DF" flag. [DF means do not fragment] 
    32.      */  
    33.   
    34.     if (ntohs(iph->frag_off) & IP_DF)  
    35.     {  
    36.         /* 
    37.          *  Reply giving the MTU of the failed hop. 
    38.          */  
    39.         ip_statistics.IpFragFails++;  
    40.         icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev->mtu, dev);  
    41.         return;  
    42.     }  
    43.   
    44.     /* 
    45.      *  The protocol doesn't seem to say what to do in the case that the 
    46.      *  frame + options doesn't fit the mtu. As it used to fall down dead 
    47.      *  in this case we were fortunate it didn't happen 
    48.      */  
    49.   
    50.     if(mtu<8)  
    51.     {  
    52.         /* It's wrong but it's better than nothing */  
    53.         icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev);  
    54.         ip_statistics.IpFragFails++;  
    55.         return;  
    56.     }  
    57.   
    58.     /* 
    59.      *  Fragment the datagram. 
    60.      */  
    61.   
    62.     /* 
    63.      *  The initial offset is 0 for a complete frame. When 
    64.      *  fragmenting fragments it's wherever this one starts. 
    65.      */  
    66.   
    67.     if (is_frag & 2)  
    68.         offset = (ntohs(iph->frag_off) & 0x1fff) << 3;  
    69.     else  
    70.         offset = 0;  
    71.   
    72.   
    73.     /* 
    74.      *  Keep copying data until we run out. 
    75.      */  
    76.   
    77.     while(left > 0)  
    78.     {  
    79.         len = left;  
    80.         /* IF: it doesn't fit, use 'mtu' - the data space left */  
    81.         if (len > mtu)  
    82.             len = mtu;  
    83.         /* IF: we are not sending upto and including the packet end 
    84.            then align the next start on an eight byte boundary */  
    85.         if (len < left)  
    86.         {  
    87.             len/=8;  
    88.             len*=8;  
    89.         }  
    90.         /* 
    91.          *  Allocate buffer. 
    92.          */  
    93.   
    94.         if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL)  
    95.         {  
    96.             printk("IP: frag: no memory for new fragment!\n");  
    97.             ip_statistics.IpFragFails++;  
    98.             return;  
    99.         }  
    100.   
    101.         /* 
    102.          *  Set up data on packet 
    103.          */  
    104.   
    105.         skb2->arp = skb->arp;  
    106.         if(skb->free==0)  
    107.             printk("IP fragmenter: BUG free!=1 in fragmenter\n");  
    108.         skb2->free = 1;  
    109.         skb2->len = len + hlen;  
    110.         skb2->h.raw=(char *) skb2->data;  
    111.         /* 
    112.          *  Charge the memory for the fragment to any owner 
    113.          *  it might possess 
    114.          */  
    115.   
    116.         save_flags(flags);  
    117.         if (sk)  
    118.         {  
    119.             cli();  
    120.             sk->wmem_alloc += skb2->mem_len;  
    121.             skb2->sk=sk;  
    122.         }  
    123.         restore_flags(flags);  
    124.         skb2->raddr = skb->raddr; /* For rebuild_header - must be here */  
    125.   
    126.         /* 
    127.          *  Copy the packet header into the new buffer. 
    128.          */  
    129.   
    130.         memcpy(skb2->h.raw, raw, hlen);  
    131.   
    132.         /* 
    133.          *  Copy a block of the IP datagram. 
    134.          */  
    135.         memcpy(skb2->h.raw + hlen, ptr, len);  
    136.         left -= len;  
    137.   
    138.         skb2->h.raw+=dev->hard_header_len;  
    139.   
    140.         /* 
    141.          *  Fill in the new header fields. 
    142.          */  
    143.         iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);  
    144.         iph->frag_off = htons((offset >> 3));  
    145.         /* 
    146.          *  Added AC : If we are fragmenting a fragment thats not the 
    147.          *         last fragment then keep MF on each bit 
    148.          */  
    149.         if (left > 0 || (is_frag & 1))  
    150.             iph->frag_off |= htons(IP_MF);  
    151.         ptr += len;  
    152.         offset += len;  
    153.   
    154.         /* 
    155.          *  Put this fragment into the sending queue. 
    156.          */  
    157.   
    158.         ip_statistics.IpFragCreates++;  
    159.   
    160.         ip_queue_xmit(sk, dev, skb2, 2);//还是调用ip_queue_xmit()函数来发送分片后的数据  
    161.     }  
    162.     ip_statistics.IpFragOKs++;  
    163. }  

    网络层的发送函数调用了设备接口层,相当于网络模型的链路层的发送函数dev_queue_xmit()

    该函数的调用关系如下:


    1. /* 
    2.  *  Send (or queue for sending) a packet.  
    3.  * 
    4.  *  IMPORTANT: When this is called to resend frames. The caller MUST 
    5.  *  already have locked the sk_buff. Apart from that we do the 
    6.  *  rest of the magic. 
    7.  */  
    8.   
    9. void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)  
    10. {  
    11.     unsigned long flags;  
    12.     int nitcount;  
    13.     struct packet_type *ptype;  
    14.     int where = 0;      /* used to say if the packet should go  */  
    15.                 /* at the front or the back of the  */  
    16.                 /* queue - front is a retransmit try    */  
    17.                 /* where=0 表示是刚从上层传递的新数据包;where=1 表示从硬件队列中取出的数据包*/  
    18.   
    19.     if (dev == NULL)   
    20.     {  
    21.         printk("dev.c: dev_queue_xmit: dev = NULL\n");  
    22.         return;  
    23.     }  
    24.       
    25.     if(pri>=0 && !skb_device_locked(skb))//锁定该skb再进行操作,避免造成内核的不一致情况  
    26.         skb_device_lock(skb);   /* Shove a lock on the frame */  
    27. #ifdef CONFIG_SLAVE_BALANCING  
    28.     save_flags(flags);  
    29.     cli();  
    30.     if(dev->slave!=NULL && dev->slave->pkt_queue < dev->pkt_queue &&  
    31.                 (dev->slave->flags & IFF_UP))  
    32.         dev=dev->slave;  
    33.     restore_flags(flags);  
    34. #endif        
    35. #ifdef CONFIG_SKB_CHECK   
    36.     IS_SKB(skb);  
    37. #endif      
    38.     skb->dev = dev;  
    39.   
    40.     /* 
    41.      *  This just eliminates some race conditions, but not all...  
    42.      */  
    43.   
    44.     if (skb->next != NULL) //这种条件似乎永远不能成立,因为发送数据包前,数据包已经从缓存队列摘下  
    45.     {//以防内核代码有BUG  
    46.         /* 
    47.          *  Make sure we haven't missed an interrupt.  
    48.          */  
    49.         printk("dev_queue_xmit: worked around a missed interrupt\n");  
    50.         start_bh_atomic();  
    51.         dev->hard_start_xmit(NULL, dev);  
    52.         end_bh_atomic();  
    53.         return;  
    54.     }  
    55.   
    56.     /* 
    57.      *  Negative priority is used to flag a frame that is being pulled from the 
    58.      *  queue front as a retransmit attempt. It therefore goes back on the queue 
    59.      *  start on a failure. 
    60.      */  
    61.        
    62.     if (pri < 0) //优先级小于0表示是从硬件队列中取出的数据包  
    63.     {  
    64.         pri = -pri-1;  
    65.         where = 1;  
    66.     }  
    67.   
    68.     if (pri >= DEV_NUMBUFFS)   
    69.     {  
    70.         printk("bad priority in dev_queue_xmit.\n");  
    71.         pri = 1;  
    72.     }  
    73.   
    74.     /* 
    75.      *  If the address has not been resolved. Call the device header rebuilder. 
    76.      *  This can cover all protocols and technically not just ARP either. 
    77.      */  
    78.        
    79.     if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {//用于ARP协议,并重建MAC帧首部  
    80.         return;  
    81.     }  
    82.   
    83.     save_flags(flags);  
    84.     cli();    
    85.     if (!where) {//表示是新数据包,需要将其加入设备队列中  
    86. #ifdef CONFIG_SLAVE_BALANCING     
    87.         skb->in_dev_queue=1;//该数据包在设备队列  
    88. #endif        
    89.         skb_queue_tail(dev->buffs + pri,skb);//将发送数据包加入硬件队列  
    90.         skb_device_unlock(skb);     /* Buffer is on the device queue and can be freed safely */  
    91.         skb = skb_dequeue(dev->buffs + pri);//从硬件队列中取出一个数据包  
    92.         skb_device_lock(skb);       /* New buffer needs locking down */  
    93. #ifdef CONFIG_SLAVE_BALANCING         
    94.         skb->in_dev_queue=0;  
    95. #endif        
    96.     }  
    97.     restore_flags(flags);  
    98.   
    99.     /* copy outgoing packets to any sniffer packet handlers */  
    100.     if(!where)//对于新的数据包,则遍历网络层协议队列,内核支持混杂模式  
    101.     {  
    102.         for (nitcount= dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next)   
    103.         {  
    104.             /* Never send packets back to the socket 
    105.              * they originated from - MvS (miquels@drinkel.ow.org) 
    106.              */  
    107.             if (ptype->type == htons(ETH_P_ALL) &&  
    108.                (ptype->dev == dev || !ptype->dev) &&  
    109.                ((struct sock *)ptype->data != skb->sk))  
    110.             {  
    111.                 struct sk_buff *skb2;  
    112.                 if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)  
    113.                     break;  
    114.                 /* 
    115.                  *  The protocol knows this has (for other paths) been taken off 
    116.                  *  and adds it back. 
    117.                  */  
    118.                 skb2->len-=skb->dev->hard_header_len;  
    119.                 ptype->func(skb2, skb->dev, ptype);//IP层函数对应func为ip_rcv(),将发送的数据回送一份给对应的网络层协议  
    120.                 nitcount--;//用于及时退出循环  
    121.             }  
    122.         }  
    123.     }  
    124.     start_bh_atomic();//开始原子操作  
    125.     if (dev->hard_start_xmit(skb, dev) == 0) {//调用硬件的发送函数发送数据  
    126.         end_bh_atomic();//结束原子操作  
    127.         /* 
    128.          *  Packet is now solely the responsibility of the driver 
    129.          */  
    130.         return;//到这里说明数据包成功发送  
    131.     }  
    132.     //数据包没有成功发送,进行处理,将数据包从新加入硬件队列  
    133.     end_bh_atomic();  
    134.   
    135.     /* 
    136.      *  Transmission failed, put skb back into a list. Once on the list it's safe and 
    137.      *  no longer device locked (it can be freed safely from the device queue) 
    138.      */  
    139.     cli();  
    140. #ifdef CONFIG_SLAVE_BALANCING  
    141.     skb->in_dev_queue=1;  
    142.     dev->pkt_queue++;  
    143. #endif        
    144.     skb_device_unlock(skb);//对SKB解锁  
    145.     skb_queue_head(dev->buffs + pri,skb);//这次采用头插法插入硬件发送队列  
    146.     restore_flags(flags);  
    147. }  

    具体的硬件发送函数dev->hard_start_xmit的实现将做下篇博文中分析。
  • 相关阅读:
    React生命周期, 兄弟组件之间通信
    React组件式编程Demo-用户的增删改查
    React之this.refs, 实现数据双向绑定
    CCF CSP 201812-4 数据中心
    CCF CSP 201812-4 数据中心
    PAT 顶级 1020 Delete At Most Two Characters (35 分)
    PAT 顶级 1020 Delete At Most Two Characters (35 分)
    Codeforces 1245C Constanze's Machine
    Codeforces 1245C Constanze's Machine
    CCF CSP 201712-4 行车路线
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6173197.html
Copyright © 2011-2022 走看看