首页 > 代码库 > Linux内核--网络栈实现分析(七)--数据包的传递过程(下)

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内核--网络栈实现分析(二)--数据包的传递过程(上)中分析了数据包从网卡设备经过驱动链路层,网络层,传输层到应用层的过程。

本文就分析一下本机产生数据是如何通过传输层,网络层到达物理层的。

综述来说,数据流程图如下:

一、应用层

应用层可以通过系统调用或文件操作来调用内核函数,BSD层的sock_write()函数会调用INET层的inet_wirte()函数。

 

[cpp] view plaincopy
  1. /* 
  2.  *  Write data to a socket. We verify that the user area ubuf..ubuf+size-1 is 
  3.  *  readable by the user process. 
  4.  */  
  5.   
  6. static int sock_write(struct inode *inode, struct file *file, char *ubuf, int size)  
  7. {  
  8.     struct socket *sock;  
  9.     int err;  
  10.       
  11.     if (!(sock = socki_lookup(inode)))   
  12.     {  
  13.         printk("NET: sock_write: can‘t find socket for inode!\n");  
  14.         return(-EBADF);  
  15.     }  
  16.   
  17.     if (sock->flags & SO_ACCEPTCON)   
  18.         return(-EINVAL);  
  19.       
  20.     if(size<0)  
  21.         return -EINVAL;  
  22.     if(size==0)  
  23.         return 0;  
  24.           
  25.     if ((err=verify_area(VERIFY_READ,ubuf,size))<0)  
  26.         return err;  
  27.     return(sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK)));  
  28. }  

INET层会调用具体传输层协议的write函数,该函数是通过调用本层的inet_send()函数实现功能的,inet_send()函数的UDP协议对应的函数为udp_write()

 

 

[cpp] view plaincopy
  1. static int inet_send(struct socket *sock, void *ubuf, int size, int noblock,   
  2.            unsigned flags)  
  3. {  
  4.     struct sock *sk = (struct sock *) sock->data;  
  5.     if (sk->shutdown & SEND_SHUTDOWN)   
  6.     {  
  7.         send_sig(SIGPIPE, current, 1);  
  8.         return(-EPIPE);  
  9.     }  
  10.     if(sk->err)  
  11.         return inet_error(sk);  
  12.     /* We may need to bind the socket. */  
  13.     if(inet_autobind(sk)!=0)  
  14.         return(-EAGAIN);  
  15.     return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags));  
  16. }  
  17.   
  18. static int inet_write(struct socket *sock, char *ubuf, int size, int noblock)  
  19. {  
  20.     return inet_send(sock,ubuf,size,noblock,0);  
  21. }  

二、传输层

 

在传输层udp_write()函数调用本层的udp_sendto()函数完成功能。

 

[cpp] view plaincopy
  1. /* 
  2.  *  In BSD SOCK_DGRAM a write is just like a send. 
  3.  */  
  4.   
  5. static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,  
  6.       unsigned flags)  
  7. {  
  8.     return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));  
  9. }  

udp_send()函数完成sk_buff结构相应的设置和报头的填写后会调用udp_send()来发送数据。具体的实现过程后面会详细分析。

 

而在udp_send()函数中,最后会调用ip_queue_xmit()函数,将数据包下放的网络层。

下面是udp_prot定义:

 

[cpp] view plaincopy
  1. struct proto udp_prot = {  
  2.     sock_wmalloc,  
  3.     sock_rmalloc,  
  4.     sock_wfree,  
  5.     sock_rfree,  
  6.     sock_rspace,  
  7.     sock_wspace,  
  8.     udp_close,  
  9.     udp_read,  
  10.     udp_write,  
  11.     udp_sendto,  
  12.     udp_recvfrom,  
  13.     ip_build_header,  
  14.     udp_connect,  
  15.     NULL,  
  16.     ip_queue_xmit,  
  17.     NULL,  
  18.     NULL,  
  19.     NULL,  
  20.     udp_rcv,  
  21.     datagram_select,  
  22.     udp_ioctl,  
  23.     NULL,  
  24.     NULL,  
  25.     ip_setsockopt,  
  26.     ip_getsockopt,  
  27.     128,  
  28.     0,  
  29.     {NULL,},  
  30.     "UDP",  
  31.     0, 0  
  32. };  

[cpp] view plaincopy
  1. static int udp_send(struct sock *sk, struct sockaddr_in *sin,  
  2.      unsigned char *from, int len, int rt)  
  3. {  
  4.     struct sk_buff *skb;  
  5.     struct device *dev;  
  6.     struct udphdr *uh;  
  7.     unsigned char *buff;  
  8.     unsigned long saddr;  
  9.     int size, tmp;  
  10.     int ttl;  
  11.     
  12.     /*  
  13.      *  Allocate an sk_buff copy of the packet. 
  14.      */  
  15.        
  16.     ........................  
  17.   
  18.     /* 
  19.      *  Now build the IP and MAC header.  
  20.      */  
  21.        
  22.     ..........................  
  23.     /* 
  24.      *  Fill in the UDP header.  
  25.      */  
  26.        
  27.     ..............................  
  28.   
  29.     /* 
  30.      *  Copy the user data.  
  31.      */  
  32.        
  33.     memcpy_fromfs(buff, from, len);  
  34.   
  35.     /* 
  36.      *  Set up the UDP checksum.  
  37.      */  
  38.        
  39.     udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);  
  40.   
  41.     /*  
  42.      *  Send the datagram to the interface.  
  43.      */  
  44.        
  45.     udp_statistics.UdpOutDatagrams++;  
  46.        
  47.     sk->prot->queue_xmit(sk, dev, skb, 1);  
  48.     return(len);  
  49. }  


 

三、网络层

 在网络层,函数ip_queue_xmit()的功能是将数据包进行一系列复杂的操作,比如是检查数据包是否需要分片,是否是多播等一系列检查,最后调用dev_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.   
  10. void ip_queue_xmit(struct sock *sk, struct device *dev,  
  11.           struct sk_buff *skb, int free)  
  12. {  
  13.     struct iphdr *iph;  
  14.     unsigned char *ptr;  
  15.   
  16.     /* Sanity check */  
  17.     ............  
  18.     /* 
  19.      *  Do some book-keeping in the packet for later 
  20.      */  
  21.   
  22.   
  23.     ...........  
  24.   
  25.     /* 
  26.      *  Find the IP header and set the length. This is bad 
  27.      *  but once we get the skb data handling code in the 
  28.      *  hardware will push its header sensibly and we will 
  29.      *  set skb->ip_hdr to avoid this mess and the fixed 
  30.      *  header length problem 
  31.      */  
  32.   
  33.     ..............  
  34.     /* 
  35.      *  No reassigning numbers to fragments... 
  36.      */  
  37.   
  38.     if(free!=2)  
  39.         iph->id      = htons(ip_id_count++);  
  40.     else  
  41.         free=1;  
  42.   
  43.     /* All buffers without an owner socket get freed */  
  44.     if (sk == NULL)  
  45.         free = 1;  
  46.   
  47.     skb->free = free;  
  48.   
  49.     /* 
  50.      *  Do we need to fragment. Again this is inefficient. 
  51.      *  We need to somehow lock the original buffer and use 
  52.      *  bits of it. 
  53.      */  
  54.   
  55.     ................  
  56.   
  57.     /* 
  58.      *  Add an IP checksum 
  59.      */  
  60.   
  61.     ip_send_check(iph);  
  62.   
  63.     /* 
  64.      *  Print the frame when debugging 
  65.      */  
  66.   
  67.     /* 
  68.      *  More debugging. You cannot queue a packet already on a list 
  69.      *  Spot this and moan loudly. 
  70.      */  
  71.     .......................  
  72.   
  73.     /* 
  74.      *  If a sender wishes the packet to remain unfreed 
  75.      *  we add it to his send queue. This arguably belongs 
  76.      *  in the TCP level since nobody else uses it. BUT 
  77.      *  remember IPng might change all the rules. 
  78.      */  
  79.   
  80.     ......................  
  81.     /* 
  82.      *  If the indicated interface is up and running, send the packet. 
  83.      */  
  84.        
  85.     ip_statistics.IpOutRequests++;  
  86.         .............................  
  87.     .............................  
  88.         if((dev->flags&IFF_BROADCAST) && iph->daddr==dev->pa_brdaddr && !(dev->flags&IFF_LOOPBACK))  
  89.         ip_loopback(dev,skb);  
  90.           
  91.     if (dev->flags & IFF_UP)  
  92.     {  
  93.         /* 
  94.          *  If we have an owner use its priority setting, 
  95.          *  otherwise use NORMAL 
  96.          */  
  97.   
  98.         if (sk != NULL)  
  99.         {  
  100.             dev_queue_xmit(skb, dev, sk->priority);  
  101.         }  
  102.         else  
  103.         {  
  104.             dev_queue_xmit(skb, dev, SOPRI_NORMAL);  
  105.         }  
  106.     }  
  107.     else  
  108.     {  
  109.         ip_statistics.IpOutDiscards++;  
  110.         if (free)  
  111.             kfree_skb(skb, FREE_WRITE);  
  112.     }  
  113. }  

四、驱动层(链路层)

在函数中,函数调用会调用具体设备的发送函数来发送数据包

dev->hard_start_xmit(skb, dev);

具体设备的发送函数在网络初始化的时候已经设置了。

这里以8390网卡为例来说明驱动层的工作原理,在net/drivers/8390.c中函数ethdev_init()函数中设置如下:

 

[cpp] view plaincopy
  1. /* Initialize the rest of the 8390 device structure. */  
  2. int ethdev_init(struct device *dev)  
  3. {  
  4.     if (ei_debug > 1)  
  5.         printk(version);  
  6.       
  7.     if (dev->priv == NULL) {//申请私有空间  
  8.         struct ei_device *ei_local;//8390网卡设备的结构体  
  9.           
  10.         dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);//申请内核内存空间  
  11.         memset(dev->priv, 0, sizeof(struct ei_device));  
  12.         ei_local = (struct ei_device *)dev->priv;  
  13. #ifndef NO_PINGPONG  
  14.         ei_local->pingpong = 1;  
  15. #endif  
  16.     }  
  17.       
  18.     /* The open call may be overridden by the card-specific code. */  
  19.     if (dev->open == NULL)  
  20.         dev->open = &ei_open;//设备的打开函数  
  21.     /* We should have a dev->stop entry also. */  
  22.     dev->hard_start_xmit = &ei_start_xmit;//设备的发送函数,定义在8390.c中  
  23.     dev->get_stats   = get_stats;  
  24. #ifdef HAVE_MULTICAST  
  25.     dev->set_multicast_list = &set_multicast_list;  
  26. #endif  
  27.   
  28.     ether_setup(dev);  
  29.           
  30.     return 0;  
  31. }  

驱动中的发送函数比较复杂,和硬件关系紧密,这里不再详细分析。

 

 

这样就大体分析了下网络数据从应用层到物理层的数据通路,后面会详细分析。

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