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

发表于:2013-1-06 09:41

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:闫明    来源:51Testing软件测试网采编

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

  该函数的调用关系如下:

/*
 * Send (or queue for sending) a packet.
 *
 * IMPORTANT: When this is called to resend frames. The caller MUST
 * already have locked the sk_buff. Apart from that we do the
 * rest of the magic.
 */

void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
{
 unsigned long flags;
 int nitcount;
 struct packet_type *ptype;
 int where = 0;  /* used to say if the packet should go */
    /* at the front or the back of the */
    /* queue - front is a retransmit try */
    /* where=0 表示是刚从上层传递的新数据包;where=1 表示从硬件队列中取出的数据包*/

 if (dev == NULL)
 {
  printk("dev.c: dev_queue_xmit: dev = NULL\n");
  return;
 }
 
 if(pri>=0 && !skb_device_locked(skb))//锁定该skb再进行操作,避免造成内核的不一致情况
  skb_device_lock(skb); /* Shove a lock on the frame */
#ifdef CONFIG_SLAVE_BALANCING
 save_flags(flags);
 cli();
 if(dev->slave!=NULL && dev->slave->pkt_queue < dev->pkt_queue &&
    (dev->slave->flags & IFF_UP))
  dev=dev->slave;
 restore_flags(flags);
#endif  
#ifdef CONFIG_SKB_CHECK
 IS_SKB(skb);
#endif   
 skb->dev = dev;

 /*
  * This just eliminates some race conditions, but not all...
  */

 if (skb->next != NULL) //这种条件似乎永远不能成立,因为发送数据包前,数据包已经从缓存队列摘下
 {//以防内核代码有BUG
  /*
   * Make sure we haven't missed an interrupt.
   */
  printk("dev_queue_xmit: worked around a missed interrupt\n");
  start_bh_atomic();
  dev->hard_start_xmit(NULL, dev);
  end_bh_atomic();
  return;
   }

 /*
  * Negative priority is used to flag a frame that is being pulled from the
  * queue front as a retransmit attempt. It therefore goes back on the queue
  * start on a failure.
  */
 
   if (pri < 0) //优先级小于0表示是从硬件队列中取出的数据包
   {
  pri = -pri-1;
  where = 1;
   }

 if (pri >= DEV_NUMBUFFS)
 {
  printk("bad priority in dev_queue_xmit.\n");
  pri = 1;
 }

 /*
  * If the address has not been resolved. Call the device header rebuilder.
  * This can cover all protocols and technically not just ARP either.
  */
 
 if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {//用于ARP协议,并重建MAC帧首部
  return;
 }

 save_flags(flags);
 cli(); 
 if (!where) {//表示是新数据包,需要将其加入设备队列中
#ifdef CONFIG_SLAVE_BALANCING 
  skb->in_dev_queue=1;//该数据包在设备队列
#endif  
  skb_queue_tail(dev->buffs + pri,skb);//将发送数据包加入硬件队列
  skb_device_unlock(skb);  /* Buffer is on the device queue and can be freed safely */
  skb = skb_dequeue(dev->buffs + pri);//从硬件队列中取出一个数据包
  skb_device_lock(skb);  /* New buffer needs locking down */
#ifdef CONFIG_SLAVE_BALANCING  
  skb->in_dev_queue=0;
#endif  
 }
 restore_flags(flags);

 /* copy outgoing packets to any sniffer packet handlers */
 if(!where)//对于新的数据包,则遍历网络层协议队列,内核支持混杂模式
 {
  for (nitcount= dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next)
  {
   /* Never send packets back to the socket
    * they originated from - MvS (miquels@drinkel.ow.org)
    */
   if (ptype->type == htons(ETH_P_ALL) &&
      (ptype->dev == dev || !ptype->dev) &&
      ((struct sock *)ptype->data != skb->sk))
   {
    struct sk_buff *skb2;
    if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
     break;
    /*
     * The protocol knows this has (for other paths) been taken off
     * and adds it back.
     */
    skb2->len-=skb->dev->hard_header_len;
    ptype->func(skb2, skb->dev, ptype);//IP层函数对应func为ip_rcv(),将发送的数据回送一份给对应的网络层协议
    nitcount--;//用于及时退出循环
   }
  }
 }
 start_bh_atomic();//开始原子操作
 if (dev->hard_start_xmit(skb, dev) == 0) {//调用硬件的发送函数发送数据
  end_bh_atomic();//结束原子操作
  /*
   * Packet is now solely the responsibility of the driver
   */
  return;//到这里说明数据包成功发送
 }
 //数据包没有成功发送,进行处理,将数据包从新加入硬件队列
 end_bh_atomic();

 /*
  * Transmission failed, put skb back into a list. Once on the list it's safe and
  * no longer device locked (it can be freed safely from the device queue)
  */
 cli();
#ifdef CONFIG_SLAVE_BALANCING
 skb->in_dev_queue=1;
 dev->pkt_queue++;
#endif  
 skb_device_unlock(skb);//对SKB解锁
 skb_queue_head(dev->buffs + pri,skb);//这次采用头插法插入硬件发送队列
 restore_flags(flags);
}

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

  本文转载自:http://blog.csdn.net/yming0221/article/details/7492423

相关链接:

Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构

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

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

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

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

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

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

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

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

33/3<123
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号