• 【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、物理层

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


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

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

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

    另外中间协议各层还有一些牵扯到的操作函数,会在后面一一介绍。
  • 相关阅读:
    vue用户登录状态判断
    Android webView包装WebAPP
    浮动粒子制作404动画页面
    使用 typescript 和 canvas 重构snow效果
    毕达哥拉斯树(pythagorasTree)原理解析及canvas动画实现
    canvas入门之时钟的实现
    canvas动画之动态绘出六边形
    分享一篇vue项目规范
    css3 ajax加载进度线
    react 体验 react与vue的比较
  • 原文地址:https://www.cnblogs.com/ztguang/p/12645491.html
Copyright © 2020-2023  润新知