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

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

/*
 * Queues a packet to be sent, and starts the transmitter
 * if necessary.  if free = 1 then we free the block after
 * transmit, otherwise we don't. If free==2 we not only
 * free the block but also don't assign a new ip seq number.
 * This routine also needs to put in the total length,
 * and compute the checksum
 */

void ip_queue_xmit(struct sock *sk, //发送数据的队列所对应的sock结构
       struct device *dev,//发送该数据包的网卡设备
            struct sk_buff *skb,//封装好的sk_buff结构,要发送的数据在该结构中
            int free)//主要配合TCP协议使用,用于数据包的重发,UDP等协议调用是free=1
{
 struct iphdr *iph;//IP数据报首部指针
 unsigned char *ptr;

 /* Sanity check */
 if (dev == NULL)
 {
  printk("IP: ip_queue_xmit dev = NULL ");
  return;
 }

 IS_SKB(skb);

 /*
  * Do some book-keeping in the packet for later
  */


 skb->dev = dev;//进一步完整sk_buff的相应字段
 skb->when = jiffies;//用于TCP协议的超时重传

 /*
  * Find the IP header and set the length. This is bad
  * but once we get the skb data handling code in the
  * hardware will push its header sensibly and we will
  * set skb->ip_hdr to avoid this mess and the fixed
  * header length problem
  */

 ptr = skb->data;//指针指向sk_buff中的数据部分
 ptr += dev->hard_header_len;//hard_header_len为硬件首部长度,在net_init.c的函数eth_setup()函数中设置的,dev->hard_header_len = ETH_HLEN; 以太网首部长度为14
 iph = (struct iphdr *)ptr;//prt已经指向IP数据包的首部
 skb->ip_hdr = iph;
 iph->tot_len = ntohs(skb->len-dev->hard_header_len);//计算IP数据报的总长度

#ifdef CONFIG_IP_FIREWALL
 if(ip_fw_chk(iph, dev, ip_fw_blk_chain, ip_fw_blk_policy, 0) != 1)
  /* just don't send this packet */
  return;
#endif

 /*
  * No reassigning numbers to fragments...
  */

 if(free!=2)
  iph->id      = htons(ip_id_count++);
 else
  free=1;

 /* All buffers without an owner socket get freed */
 if (sk == NULL)
  free = 1;

 skb->free = free;//设置skb的free值,free=1,发送后立即释放;free=2,不但释放缓存,而且不分配新的序列号

 /*
  * Do we need to fragment. Again this is inefficient.
  * We need to somehow lock the original buffer and use
  * bits of it.
  */
 //数据帧中的数据部分必须小于等于MTU
 if(skb->len > dev->mtu + dev->hard_header_len)//发送的数据长度大于数据帧的数据部分和帧首部之和,则需要分片
 {
  ip_fragment(sk,skb,dev,0);//对数据报分片后继续调用ip _queue_xmit()函数发送数据
  IS_SKB(skb);
  kfree_skb(skb,FREE_WRITE);
  return;
 }

 /*
  * Add an IP checksum
  */

 ip_send_check(iph);//IP数据报首部检查

 /*
  * Print the frame when debugging
  */

 /*
  * More debugging. You cannot queue a packet already on a list
  * Spot this and moan loudly.
  */
 if (skb->next != NULL)//说明该数据包仍然存在于某个缓存队列
 {
  printk("ip_queue_xmit: next != NULL ");
  skb_unlink(skb);//将其从缓存链表中删除,否则可能导致内核错误
 }

 /*
  * If a sender wishes the packet to remain unfreed
  * we add it to his send queue. This arguably belongs
  * in the TCP level since nobody else uses it. BUT
  * remember IPng might change all the rules.
  */

 if (!free)//free=0
 {
  unsigned long flags;
  /* The socket now has more outstanding blocks */

  sk->packets_out++;

  /* Protect the list for a moment */
  save_flags(flags);
  cli();

  if (skb->link3 != NULL)//link3指向数据报道呃重发队列
  {
   printk("ip.c: link3 != NULL ");
   skb->link3 = NULL;
  }
  //sk中send_tail和send_head是用户缓存的单向链表表尾和表头
  if (sk->send_head == NULL)
  {
   sk->send_tail = skb;
   sk->send_head = skb;
  }
  else
  {
   sk->send_tail->link3 = skb;//link3指针用于数据包的连接
   sk->send_tail = skb;
  }
  /* skb->link3 is NULL */

  /* Interrupt restore */
  restore_flags(flags);
 }
 else
  /* Remember who owns the buffer */
  skb->sk = sk;

 /*
  * If the indicated interface is up and running, send the packet.
  */
 
 ip_statistics.IpOutRequests++;
#ifdef CONFIG_IP_ACCT
 ip_acct_cnt(iph,dev, ip_acct_chain);
#endif
 
#ifdef CONFIG_IP_MULTICAST //这部分是IP数据报的多播处理

 /*
  * Multicasts are looped back for other local users
  */
 
 .......................................
#endif
 if((dev->flags&IFF_BROADCAST) && iph->daddr==dev->pa_brdaddr && !(dev->flags&IFF_LOOPBACK))//广播数据包的处理
  ip_loopback(dev,skb);
 
 if (dev->flags & IFF_UP)//设备状态正常
 {
  /*
   * If we have an owner use its priority setting,
   * otherwise use NORMAL
   */
  //调用设备接口层函数发送数据: dev_queue_xmit()函数
  if (sk != NULL)
  {
   dev_queue_xmit(skb, dev, sk->priority);
  }
  else
  {
   dev_queue_xmit(skb, dev, SOPRI_NORMAL);
  }
 }
 else//设备状态不正常
 {
  ip_statistics.IpOutDiscards++;
  if (free)
   kfree_skb(skb, FREE_WRITE);
 }
}