/* * 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\n"); 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\n"); 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\n"); 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); } } |