【Linux 内核网络协议栈源码剖析】数据包发送


http://blog.csdn.net/wenqian1991/article/details/46725105


由于在connect函数中涉及数据包的发送与接收问题,事实上,发送与接收函数不限于connect函数,所以这里单独剖析。

承前文继续剖析 connect 函数,数据包的发送和接收在 ip_queue_xmit 函数和 release_sock 函数中实现。本文着重分析 ip_queue_xmit 函数,下篇将补充分析 connect 函数剩下的部分。

值得注意的是:这些函数是数据包发送函数,在数据传输阶段,基本上都会调用该函数,因为connect涉及该函数,就放在这里介绍了,不意味着这个函数只属于connect下层函数。

5、网络层——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.  //sk:被发送数据包对应的套接字;dev:发送数据包的网络设备  
  11.  //skb:被发送的数据包         ;flags:是否对数据包进行缓存以便于此后的超时重发  
  12. void ip_queue_xmit(struct sock *sk, struct device *dev,  
  13.           struct sk_buff *skb, int free)  
  14. {  
  15.     struct iphdr *iph;  
  16.     unsigned char *ptr;  
  17.   
  18.     /* Sanity check */  
  19.     //发送设备检查  
  20.     if (dev == NULL)  
  21.     {  
  22.         printk("IP: ip_queue_xmit dev = NULL ");  
  23.         return;  
  24.     }  
  25.   
  26.     IS_SKB(skb);//数据包合法性检查  
  27.   
  28.     /* 
  29.      *  Do some book-keeping in the packet for later 
  30.      */  
  31.   
  32.   
  33.     skb->dev = dev;  
  34.     skb->when = jiffies;//重置数据包的发送时间,只有一个定时器,每次发数据包时,都要重新设置  
  35.   
  36.     /* 
  37.      *  Find the IP header and set the length. This is bad 
  38.      *  but once we get the skb data handling code in the 
  39.      *  hardware will push its header sensibly and we will 
  40.      *  set skb->ip_hdr to avoid this mess and the fixed 
  41.      *  header length problem 
  42.      */  
  43.     //skb->data指向的地址空间的布局: MAC首部 | IP首部 | TCP首部 | 有效负载  
  44.     ptr = skb->data;//获取数据部分首地址  
  45.     ptr += dev->hard_header_len;//后移硬件(MAC)首部长度个字节,定位到ip首部  
  46.     iph = (struct iphdr *)ptr;//获取ip首部  
  47.     skb->ip_hdr = iph;//skb对应字段建立关联  
  48.     //ip数据报的总长度(ip首部+数据部分) = skb的总长度 - 硬件首部长度  
  49.     iph->tot_len = ntohs(skb->len-dev->hard_header_len);  
  50.   
  51. #ifdef CONFIG_IP_FIREWALL  
  52.     //数据包过滤,用于防火墙安全性检查  
  53.     if(ip_fw_chk(iph, dev, ip_fw_blk_chain, ip_fw_blk_policy, 0) != 1)  
  54.         /* just don't send this packet */  
  55.         return;  
  56. #endif    
  57.   
  58.     /* 
  59.      *  No reassigning numbers to fragments... 
  60.      */  
  61.     //如果不是分片数据包,就需要递增id字段  
  62. //free==2,表示这是个分片数据包,所有分片数据包必须具有相同的id字段,方便以后分片数据包重组  
  63.     if(free!=2)  
  64.         iph->id      = htons(ip_id_count++);//ip数据报标识符  
  65.         //ip_id_count是全局变量,用于下一个数据包中ip首部id字段的赋值  
  66.     else  
  67.         free=1;  
  68.   
  69.     /* All buffers without an owner socket get freed */  
  70.     if (sk == NULL)//没有对应sock结构,则无法对数据包缓存  
  71.         free = 1;  
  72.   
  73.     skb->free = free;//用于标识数据包发送之后是缓存还是立即释放,=1表示无缓存  
  74.   
  75.     /* 
  76.      *  Do we need to fragment. Again this is inefficient. 
  77.      *  We need to somehow lock the original buffer and use 
  78.      *  bits of it. 
  79.      */  
  80.     //数据包拆分  
  81.     //如果ip层数据包的数据部分(各层首部+有效负载)长度大于网络设备的最大传输单元,就需要拆分发送  
  82.     //实际是skb->len - dev->hard_header_len > dev->mtu  
  83.     //因为MTU最大报文长度表示的仅仅是IP首部及其数据负载的长度,所以要考虑MAC首部长度  
  84.     if(skb->len > dev->mtu + dev->hard_header_len)  
  85.     {  
  86.     //拆分成分片数据包传输  
  87.         ip_fragment(sk,skb,dev,0);  
  88.         IS_SKB(skb);//检查数据包skb相关字段  
  89.         kfree_skb(skb,FREE_WRITE);  
  90.         return;  
  91.     }  
  92.   
  93.     /* 
  94.      *  Add an IP checksum 
  95.      */  
  96.     //ip首部校验和计算  
  97.     ip_send_check(iph);  
  98.   
  99.     /* 
  100.      *  Print the frame when debugging 
  101.      */  
  102.   
  103.     /* 
  104.      *  More debugging. You cannot queue a packet already on a list 
  105.      *  Spot this and moan loudly. 
  106.      */  
  107.     if (skb->next != NULL)  
  108.     {  
  109.         printk("ip_queue_xmit: next != NULL ");  
  110.         skb_unlink(skb);  
  111.     }  
  112.   
  113.     /* 
  114.      *  If a sender wishes the packet to remain unfreed 
  115.      *  we add it to his send queue. This arguably belongs 
  116.      *  in the TCP level since nobody else uses it. BUT 
  117.      *  remember IPng might change all the rules. 
  118.      */  
  119.     //free=0,表示对数据包进行缓存,一旦发生丢弃的情况,进行数据包重传(可靠性数据传输协议)  
  120.     if (!free)  
  121.     {  
  122.         unsigned long flags;  
  123.         /* The socket now has more outstanding blocks */  
  124.   
  125.         sk->packets_out++;//本地发送出去但未得到应答的数据包数目  
  126.   
  127.         /* Protect the list for a moment */  
  128.         save_flags(flags);  
  129.         cli();  
  130.   
  131.         //数据包重发队列  
  132.         if (skb->link3 != NULL)  
  133.         {  
  134.             printk("ip.c: link3 != NULL ");  
  135.             skb->link3 = NULL;  
  136.         }  
  137.         if (sk->send_head == NULL)  
  138.         {  
  139.         //数据包重传缓存队列则是由下列两个字段维护  
  140.             sk->send_tail = skb;  
  141.             sk->send_head = skb;  
  142.         }  
  143.         else  
  144.         {  
  145.             sk->send_tail->link3 = skb;  
  146.             sk->send_tail = skb;  
  147.         }  
  148.         /* skb->link3 is NULL */  
  149.   
  150.         /* Interrupt restore */  
  151.         restore_flags(flags);  
  152.     }  
  153.     else  
  154.         /* Remember who owns the buffer */  
  155.         skb->sk = sk;  
  156.   
  157.     /* 
  158.      *  If the indicated interface is up and running, send the packet. 
  159.      */  
  160.        
  161.     ip_statistics.IpOutRequests++;  
  162. #ifdef CONFIG_IP_ACCT  
  163. //下面函数内部调用ip_fw_chk,也是数据包过滤  
  164.     ip_acct_cnt(iph,dev, ip_acct_chain);  
  165. #endif    
  166.       
  167. #ifdef CONFIG_IP_MULTICAST    
  168.     //对于多播和广播数据包,其必须复制一份回送给本机  
  169.     /* 
  170.      *  Multicasts are looped back for other local users 
  171.      */  
  172.      /*对多播和广播数据包进行处理*/  
  173.      //检查目的地址是否为一个多播地址  
  174.     if (MULTICAST(iph->daddr) && !(dev->flags&IFF_LOOPBACK))  
  175.     {  
  176.     //检查发送设备是否为一个回路设备  
  177.         if(sk==NULL || sk->ip_mc_loop)  
  178.         {  
  179.             if(iph->daddr==IGMP_ALL_HOSTS)//如果是224.0.0.1(默认多播地址)  
  180.                 ip_loopback(dev,skb);//数据包回送给发送端  
  181.             else  
  182.             {  //检查多播地址列表,对数据包进行匹配  
  183.                 struct ip_mc_list *imc=dev->ip_mc_list;  
  184.                 while(imc!=NULL)  
  185.                 {  
  186.                     if(imc->multiaddr==iph->daddr)//如果存在匹配项,则回送数据包  
  187.                     {  
  188.                         ip_loopback(dev,skb);  
  189.                         break;  
  190.                     }  
  191.                     imc=imc->next;  
  192.                 }  
  193.             }  
  194.         }  
  195.         /* Multicasts with ttl 0 must not go beyond the host */  
  196.         //检查ip首部ttl字段,如果为0,则不可进行数据包发送(转发)  
  197.         if(skb->ip_hdr->ttl==0)  
  198.         {  
  199.             kfree_skb(skb, FREE_READ);  
  200.             return;  
  201.         }  
  202.     }  
  203. #endif  
  204.     //对广播数据包的判断  
  205.     if((dev->flags&IFF_BROADCAST) && iph->daddr==dev->pa_brdaddr && !(dev->flags&IFF_LOOPBACK))  
  206.         ip_loopback(dev,skb);  
  207.   
  208.     //对发送设备当前状态的检查,如果处于非工作状态,则无法发送数据包,此时进入else执行  
  209.     if (dev->flags & IFF_UP)  
  210.     {  
  211.         /* 
  212.          *  If we have an owner use its priority setting, 
  213.          *  otherwise use NORMAL 
  214.          */  
  215.         //调用下层接口函数dev_queue_xmit,将数据包交由链路层处理  
  216.         if (sk != NULL)  
  217.         {  
  218.             dev_queue_xmit(skb, dev, sk->priority);  
  219.         }  
  220.         else  
  221.         {  
  222.             dev_queue_xmit(skb, dev, SOPRI_NORMAL);  
  223.         }  
  224.     }  
  225.     else  
  226.     {  
  227.         ip_statistics.IpOutDiscards++;  
  228.         if (free)  
  229.             kfree_skb(skb, FREE_WRITE);//丢弃数据包,对tcp可靠传输而言,将造成数据包超时重传  
  230.     }  
  231. }  
上面函数功能可以总结为:

1. 相关合法性检查;

2. 防火墙过滤;

3. 对数据包是否需要分片发送进行检查;

4. 进行可能的数据包缓存处理;

5. 对多播和广播数据报是否需要回送本机进行检查;

6. 调用下层接口函数 dev_queue_xmit 将数据包送往链路层进行处理。

上面函数内部涉及到一个函数,把数据包分片,当数据包大小大于最大传输单元时,需要将数据包分片传送,这里则是通过函数 ip_fragment 实现的。

ip_fragment函数:将大数据包(大于最大传输单元(最大传输单元指的是ip报,不包含mac头部))分片发送。这个函数条理很清楚

  1. /* 
  2.  *  This IP datagram is too large to be sent in one piece.  Break it up into 
  3.  *  smaller pieces (each of size equal to the MAC header plus IP header plus 
  4.  *  a block of the data of the original IP data part) that will yet fit in a 
  5.  *  single device frame, and queue such a frame for sending by calling the 
  6.  *  ip_queue_xmit().  Note that this is recursion, and bad things will happen 
  7.  *  if this function causes a loop... 
  8.  * 
  9.  *  Yes this is inefficient, feel free to submit a quicker one. 
  10.  * 
  11.  *  **Protocol Violation** 
  12.  *  We copy all the options to each fragment. !FIXME! 
  13.  */  
  14. void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)  
  15. {  
  16.     struct iphdr *iph;  
  17.     unsigned char *raw;  
  18.     unsigned char *ptr;  
  19.     struct sk_buff *skb2;  
  20.     int left, mtu, hlen, len;  
  21.     int offset;  
  22.     unsigned long flags;  
  23.   
  24.     /* 
  25.      *  Point into the IP datagram header. 
  26.      */  
  27.   
  28.     raw = skb->data;//得到数据部分,如果你还没清楚skb与data表示什么的话,请自行面壁  
  29.     iph = (struct iphdr *) (raw + dev->hard_header_len);//偏移mac首部就到了ip首部位置  
  30.   
  31.     skb->ip_hdr = iph;//指定ip首部  
  32.   
  33.     /* 
  34.      *  Setup starting values. 
  35.      */  
  36.     //ip数据报由ip首部和数据负载部分组成,其中数据负载部分又有tcp首部和tcp有效负载组成  
  37.     hlen = (iph->ihl * sizeof(unsigned long));//ip首部长度  
  38.     left = ntohs(iph->tot_len) - hlen;//ip数据负载长度 /* Space per frame */  
  39.     hlen += dev->hard_header_len;//加上mac首部长度,得到这两者的长度和       /* Total header size */  
  40.     mtu = (dev->mtu - hlen);//mtu初始化为ip数据负载长度        /* Size of data space */  
  41.     ptr = (raw + hlen); //指向ip负载数据开始位置      /* Where to start from */  
  42.   
  43.     /* 
  44.      *  Check for any "DF" flag. [DF means do not fragment] 
  45.      */  
  46.     //检查发送端是否允许进行分片  
  47.     if (ntohs(iph->frag_off) & IP_DF)//如果不允许  
  48.     {  
  49.         /* 
  50.          *  Reply giving the MTU of the failed hop. 
  51.          */  
  52.          //返回一个ICMP错误  
  53.         ip_statistics.IpFragFails++;  
  54.         icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev->mtu, dev);  
  55.         return;  
  56.     }  
  57.   
  58.     /* 
  59.      *  The protocol doesn't seem to say what to do in the case that the 
  60.      *  frame + options doesn't fit the mtu. As it used to fall down dead 
  61.      *  in this case we were fortunate it didn't happen 
  62.      */  
  63.     //如果ip数据负载长度<8,则无法为其创建分片  
  64.     //规定,分片中数据长度必须是8的倍数  
  65.     if(mtu<8)  
  66.     {  
  67.         /* It's wrong but it's better than nothing */  
  68.         icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev);  
  69.         ip_statistics.IpFragFails++;  
  70.         return;  
  71.     }  
  72.   
  73.     /* 
  74.      *  Fragment the datagram. 
  75.      */  
  76.   
  77.     /* 
  78.      *  The initial offset is 0 for a complete frame. When 
  79.      *  fragmenting fragments it's wherever this one starts. 
  80.      */  
  81.   
  82.     if (is_frag & 2)//表示被分片数据包本身是一个分片  
  83.         offset = (ntohs(iph->frag_off) & 0x1fff) << 3;  
  84.     else  
  85.         offset = 0;  
  86.   
  87.   
  88.     /* 
  89.      *  Keep copying data until we run out. 
  90.      */  
  91.     //直到所有分片数据处理完  
  92.     while(left > 0)  
  93.     {  
  94.         len = left;  
  95.         /* IF: it doesn't fit, use 'mtu' - the data space left */  
  96.         if (len > mtu)//如果这个数据还是大于mtu,表示不是最后一个分片,还要继续分片处理  
  97.             len = mtu;  
  98.         /* IF: we are not sending upto and including the packet end 
  99.            then align the next start on an eight byte boundary */  
  100.         if (len < left)  
  101.         {  
  102. //得到小于mtu的最大的一个为8的倍数的数值,这个运算很有意思,值得借鉴一下  
  103. //可以看出分片的原则是每个分片在条件下尽量携带最多数据,这个条件就是不能大于mtu值,且必须是8的倍数  
  104.             len/=8;  
  105.             len*=8;  
  106.         }  
  107.         /* 
  108.          *  Allocate buffer. 
  109.          */  
  110. //分配一个skb_buff,注意这里分配的大小,hlen是ip首部大小,得加上ip首部  
  111. //这里可以看出,每个分片数据包还得加上ip首部,典型的1+1>2,相比不分片下降低了效率,  
  112. //但是这是不可避免的,必须增加火车头,不然不知道往哪开  
  113.         if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL)  
  114.         {  
  115.             printk("IP: frag: no memory for new fragment! ");  
  116.             ip_statistics.IpFragFails++;  
  117.             return;  
  118.         }  
  119.   
  120.         /* 
  121.          *  Set up data on packet 
  122.          */  
  123.   
  124.         skb2->arp = skb->arp;//表示mac首部是否创建成功,  
  125.         //参见ip_build_header函数,其内部调用了ip_send函数(eth_header)  
  126.         if(skb->free==0)  
  127.             printk("IP fragmenter: BUG free!=1 in fragmenter ");  
  128.         skb2->free = 1;//数据包无须缓存  
  129.         skb2->len = len + hlen;//数据部分长度(分片大小+ip首部)  
  130.         skb2->h.raw=(char *) skb2->data;//让skb2对应层的raw指向分片数据包的数据部分  
  131.         /* 
  132.          *  Charge the memory for the fragment to any owner 
  133.          *  it might possess 
  134.          */  
  135.   
  136.         save_flags(flags);  
  137.         if (sk)  
  138.         {  
  139.             cli();  
  140.             sk->wmem_alloc += skb2->mem_len;//设置sk当前写缓冲大小为分片数据包的大小  
  141.             skb2->sk=sk;//关联  
  142.         }  
  143.         restore_flags(flags);  
  144.         skb2->raddr = skb->raddr;//数据包的下一站地址,所有分片数据包自然是原数据包是一个地址  /* For rebuild_header - must be here */  
  145.   
  146.         /* 
  147.          *  Copy the packet header into the new buffer. 
  148.          */  
  149. //把skb数据部分拷贝到raw指向的位置,这里拷贝的是首部(mac首部+ip首部)  
  150. //实际上raw和skb2->data是指向同一个地址  
  151.         memcpy(skb2->h.raw, raw, hlen);  
  152.         //raw随着层次变化,链路层=eth,ip层=iph  
  153.   
  154.         /* 
  155.          *  Copy a block of the IP datagram. 
  156.          */  
  157.         //这里则是拷贝ip数据负载部分  
  158.         memcpy(skb2->h.raw + hlen, ptr, len);  
  159.         left -= len;//剩下未传送的数据大小  
  160.   
  161.         skb2->h.raw+=dev->hard_header_len;//raw位置定位到了ip首部  
  162.         //一定要清楚skb_buff->data到了某一层的数据布局  
  163.         /* 
  164.          *  Fill in the new header fields. 
  165.          */  
  166.          //获取ip首部  
  167.         iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);  
  168.         iph->frag_off = htons((offset >> 3));//片位移,offset在前面进行了设置  
  169.         /* 
  170.          *  Added AC : If we are fragmenting a fragment thats not the 
  171.          *         last fragment then keep MF on each bit 
  172.          */  
  173.         if (left > 0 || (is_frag & 1))//left>0,表示这不是最后一个分片,还有剩下数据包未发送  
  174.             iph->frag_off |= htons(IP_MF);  
  175.   
  176.     //ip数据负载已经发送了len各大小的分片数据包,那么就要更新下一个分片数据包的位置,以便发送  
  177.         ptr += len;//ip数据负载位置更新  
  178.         offset += len;//偏移量更新,  
  179.   
  180.         /* 
  181.          *  Put this fragment into the sending queue. 
  182.          */  
  183.   
  184.         ip_statistics.IpFragCreates++;  
  185.   
  186.         ip_queue_xmit(sk, dev, skb2, 2);//发送数据包  
  187.         //可以看出,发送一个数据包的过程就是,检查其大小是否小于mtu,否则需要进行分片,  
  188.         //然后对分片进行发送,分片数据包自然是小于mtu,直到原来的大于mtu的数据包全部分片发送完  
  189.     }  
  190.     ip_statistics.IpFragOKs++;  
  191. }  
ip_fragment 函数条理很清晰,就是将大的拆分为小的,其拆分过程为,新建指定大小(小于MTU的是8的倍数的最大值)的分片数据包,然后将原大数据包中的数据负载截取前分片大小,再加上ip首部,每个分片数据包都要加上ip首部,这样降低效率的措施不得不采用。然后就是发送这个分片数据包,直到大数据包分片发送完成。
ip_queue_xmit 最后通过调用 dev_queue_xmit 函数将数据包发往链路层进行处理。

6、链路层——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.  /* 
  10.  skb:被发送的数据包;dev:数据包发送网络接口设备;pri:网络接口设备忙时,缓存该数据包时使用的优先级 
  11.  */  
  12. void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)  
  13. {  
  14.     unsigned long flags;  
  15.     int nitcount;  
  16.     struct packet_type *ptype;//用于网络层协议  
  17.     int where = 0;      /* used to say if the packet should go  */  
  18.                 /* at the front or the back of the  */  
  19.                 /* queue - front is a retransmit try    */  
  20.   
  21.     if (dev == NULL)   
  22.     {  
  23.         printk("dev.c: dev_queue_xmit: dev = NULL ");  
  24.         return;  
  25.     }  
  26.   
  27.     //加锁  
  28.     if(pri>=0 && !skb_device_locked(skb))  
  29.         skb_device_lock(skb);   /* Shove a lock on the frame */  
  30. #ifdef CONFIG_SLAVE_BALANCING  
  31.     save_flags(flags);//保存状态  
  32.     cli();  
  33.     //检查是否使用了主从设备的连接方式  
  34.     //如果采用了这种方式,则发送数据包时,可在两个设备之间平均负载  
  35.     if(dev->slave!=NULL && dev->slave->pkt_queue < dev->pkt_queue &&  
  36.                 (dev->slave->flags & IFF_UP))  
  37.         dev=dev->slave;  
  38.     restore_flags(flags);  
  39. #endif        
  40. #ifdef CONFIG_SKB_CHECK   
  41.     IS_SKB(skb);//检查数据包的合法性  
  42. #endif      
  43.     skb->dev = dev;//指向数据包发送设备对应结构  
  44.   
  45.     /* 
  46.      *  This just eliminates some race conditions, but not all...  
  47.      */  
  48.     //检查以免造成竞争条件,事实上skb->next == NULL的  
  49.     if (skb->next != NULL)   
  50.     {  
  51.         /* 
  52.          *  Make sure we haven't missed an interrupt.  
  53.          */  
  54.         printk("dev_queue_xmit: worked around a missed interrupt ");  
  55.         start_bh_atomic();//原子操作,宏定义  
  56.         dev->hard_start_xmit(NULL, dev);  
  57.         end_bh_atomic();  
  58.         return;  
  59.     }  
  60.   
  61.     /* 
  62.      *  Negative priority is used to flag a frame that is being pulled from the 
  63.      *  queue front as a retransmit attempt. It therefore goes back on the queue 
  64.      *  start on a failure. 
  65.      */  
  66. //优先级为负数,表示当前处理的数据包是从硬件队列中取下的,而非上层传递的新数据包  
  67.     if (pri < 0)   
  68.     {  
  69.         pri = -pri-1;  
  70.         where = 1;  
  71.     }  
  72.   
  73.     if (pri >= DEV_NUMBUFFS)   
  74.     {  
  75.         printk("bad priority in dev_queue_xmit. ");  
  76.         pri = 1;  
  77.     }  
  78.   
  79.     /* 
  80.      *  If the address has not been resolved. Call the device header rebuilder. 
  81.      *  This can cover all protocols and technically not just ARP either. 
  82.      */  
  83. //arp标识是否完成链路层的硬件地址解析,如果没完成,则需要调用rebuild_header(eth_rebuild_header函数)  
  84. //完成链路层首部的创建工作  
  85.     if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {  
  86.         return;//这将启动arp地址解析过程,则数据包的发送则由arp协议模块负责,所以这里直接返回  
  87.     }  
  88.   
  89.     save_flags(flags);  
  90.     cli();    
  91.     if (!where) {//where=1,表示这是从上层接受的新数据包  
  92. #ifdef CONFIG_SLAVE_BALANCING     
  93.         skb->in_dev_queue=1;//标识该数据包缓存在设备队列中  
  94. #endif        
  95.         skb_queue_tail(dev->buffs + pri,skb);//插入到设备缓存队列的尾部  
  96.         skb_device_unlock(skb);     /* Buffer is on the device queue and can be freed safely */  
  97.         skb = skb_dequeue(dev->buffs + pri);//从设备缓存队列的首部读取数据包,这样取得的数据包可能不是我们之前插入的数据包  
  98.         skb_device_lock(skb);       /* New buffer needs locking down */  
  99. #ifdef CONFIG_SLAVE_BALANCING         
  100.         skb->in_dev_queue=0;//该数据包当前不在缓存队列中  
  101. #endif        
  102.     }  
  103.     restore_flags(flags);//恢复状态  
  104.   
  105.     /* copy outgoing packets to any sniffer packet handlers */  
  106.     //内核对混杂模式的支持。不明白...  
  107.     if(!where)  
  108.     {  
  109.         for (nitcount= dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next)   
  110.         {  
  111.             /* Never send packets back to the socket 
  112.              * they originated from - MvS (miquels@drinkel.ow.org) 
  113.              */  
  114.             if (ptype->type == htons(ETH_P_ALL) &&  
  115.                (ptype->dev == dev || !ptype->dev) &&  
  116.                ((struct sock *)ptype->data != skb->sk))  
  117.             {  
  118.                 struct sk_buff *skb2;  
  119.                 if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)//复制一份数据包  
  120.                     break;  
  121.                 /* 
  122.                  *  The protocol knows this has (for other paths) been taken off 
  123.                  *  and adds it back. 
  124.                  */  
  125.                 skb2->len-=skb->dev->hard_header_len;//长度  
  126.                 ptype->func(skb2, skb->dev, ptype);//协议处理函数  
  127.                 nitcount--;  
  128.             }  
  129.         }  
  130.     }  
  131.     start_bh_atomic();  
  132. //下面调用hard_start_xmit函数,前面skb->next不为NULL时,也调用这个函数,不过参数数据包skb是NULL  
  133. //驱动层发送数据包,关联到了具体的网络设备处理函数,将进入真实的网卡驱动(物理层)  
  134. //高版本的内核协议栈,还有虚拟设备,这个版本就是直接进入真实设备  
  135.     if (dev->hard_start_xmit(skb, dev) == 0) {  
  136.         end_bh_atomic();  
  137.         /* 
  138.          *  Packet is now solely the responsibility of the driver 
  139.          */  
  140.         return;  
  141.     }  
  142.     end_bh_atomic();  
  143.   
  144.     /* 
  145.      *  Transmission failed, put skb back into a list. Once on the list it's safe and 
  146.      *  no longer device locked (it can be freed safely from the device queue) 
  147.      */  
  148.     cli();  
  149. #ifdef CONFIG_SLAVE_BALANCING  
  150.     skb->in_dev_queue=1;//如果使用主从设备,就缓存在队列中  
  151.     dev->pkt_queue++;//该设备缓存的待发送数据包个数加1  
  152. #endif        
  153.     skb_device_unlock(skb);  
  154.     skb_queue_head(dev->buffs + pri,skb);//把数据包插入到数据包队列头中  
  155.     restore_flags(flags);  
  156. }  
7、物理层

物理层则牵扯到具体的网络接口硬件设备了,实则是一个网络驱动程序。不同的网卡其驱动程序有所不同,这跟硬件的时序,延迟等有关。

【Linux 内核网络协议栈源码剖析】数据包发送

关于驱动,这里我们就不介绍了。
这部分介绍的就是数据包的发送过程,从网络层到最底层的网卡驱动。下篇将介绍数据包的接收过程。

connect 函数可真是渗透到网络栈的各个层啊,connect 函数是客户端向服务器端发出连接请求数据包,该数据包需要最终到达服务器端处理,自然要从客户端从上至下经过应用层、传输层、网络层、链路层、硬件接口到达对端(对端接收则是反过来从下往上)。所以通信双方进行数据传输的函数都要经过这些网络协议栈。

从侧面可看出,内核网络协议栈的设计体现了高内聚,低耦合的原则,各层之间只提供接口函数,协议栈某一层的改动,不需要取改动其余层,保持接口的一致性就可以了,面对日益复杂的网络栈,这种设计风格无疑很有利于维护和升级。

另外中间协议各层还有一些牵扯到的操作函数,会在后面一一介绍。