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()函数,将发送数据的任务交给网络层,下面就分析了下该函数:

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

[cpp] view plaincopy

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

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

[cpp] view plaincopy

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

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

该函数的调用关系如下:

[cpp] view plaincopy

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

具体的硬件发送函数dev->hard_start_xmit的实现将做下篇博文中分析。

时间: 2024-10-13 15:08:49

Linux内核--网络栈实现分析(十)--网络层之IP协议(下)的相关文章

Linux内核--网络栈实现分析(七)--数据包的传递过程(下)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 在博文Linux内核--网络栈

Linux内核--网络栈实现分析(十一)--驱动程序层(下)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7555870 更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 在博文Linux内核--网络栈

Linux内核--网络栈实现分析(一)--网络栈初始化

本文分析基于内核Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7488828 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 以后的系列博文将深入分析Linux内核的网络栈实现原理,这里看到曹桂平博士的分析后,也决定选择Linux内核1.2.13版本进行分析. 原因如下: 1.功能和网络栈层次

Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7497260 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 经过前面两篇博文的分析,已经对L

Linux内核--网络栈实现分析(六)--应用层获取数据包(上)

本文分析基于内核Linux 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7541907 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 上篇博文分析了传输层从网络层获取数据包后将

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 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 简单分析了链路层之后,上升到网络

Linux内核--网络栈实现分析(二)--数据包的传递过程(上)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7492423 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 上一篇博文中我们从宏观上分析了L

Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 这里看看数据包从IP层是如何

Linux内核--网络栈实现分析(八)--应用层发送数据(下)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7547826 更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 下面是发送数据的流程: 应用层