zoukankan      html  css  js  c++  java
  • Linux内核网络栈实现分析(六)应用层获取数据包(上)

    本文分析基于内核Linux 1.2.13

    原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7541907

    更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html

    作者:闫明

    注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析、”(下)“表示分析是从上向下分析。


    上篇博文分析了传输层从网络层获取数据包后将数据包缓存结构sk_buff挂载到特定的sock结构的接收队列中。

    这里接着分析应用程序是如何从传输层获取网络数据包的。应用层要得到传输层的数据包有两种主要的方式:系统调用和文件操作


    系统调用:

    Linux下用户程序是通过系统调用来从用户态到内核态,调用内核功能来完成相应的服务。

    网络栈的一些功能是通过系统调用sys_socketcall来完成的

    具体的代码在net/socket.c中,该文件中的函数就相当于一个桥梁,在系统调用和内核网络栈之间。

    1. /* 
    2.  *  System call vectors. Since I (RIB) want to rewrite sockets as streams, 
    3.  *  we have this level of indirection. Not a lot of overhead, since more of 
    4.  *  the work is done via read/write/select directly. 
    5.  * 
    6.  *  I'm now expanding this up to a higher level to separate the assorted 
    7.  *  kernel/user space manipulations and global assumptions from the protocol 
    8.  *  layers proper - AC. 
    9.  */  
    10.   
    11. asmlinkage int sys_socketcall(int call, unsigned long *args)  
    12. {  
    13.     int er;  
    14.     switch(call)   
    15.     {  
    16.         case SYS_SOCKET:  
    17.             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));  
    18.             if(er)  
    19.                 return er;  
    20.             return(sock_socket(get_fs_long(args+0),  
    21.                 get_fs_long(args+1),  
    22.                 get_fs_long(args+2)));  
    23.         case SYS_BIND:  
    24.             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));  
    25.             if(er)  
    26.                 return er;  
    27.             return(sock_bind(get_fs_long(args+0),  
    28.                 (struct sockaddr *)get_fs_long(args+1),  
    29.                 get_fs_long(args+2)));  
    30.         case SYS_CONNECT:  
    31.             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));  
    32.             if(er)  
    33.                 return er;  
    34.             return(sock_connect(get_fs_long(args+0),  
    35.                 (struct sockaddr *)get_fs_long(args+1),  
    36.                 get_fs_long(args+2)));  
    37.         case SYS_LISTEN:  
    38.             er=verify_area(VERIFY_READ, args, 2 * sizeof(long));  
    39.             if(er)  
    40.                 return er;  
    41.             return(sock_listen(get_fs_long(args+0),  
    42.                 get_fs_long(args+1)));  
    43.         case SYS_ACCEPT:  
    44.             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));  
    45.             if(er)  
    46.                 return er;  
    47.             return(sock_accept(get_fs_long(args+0),  
    48.                 (struct sockaddr *)get_fs_long(args+1),  
    49.                 (int *)get_fs_long(args+2)));  
    50.         case SYS_GETSOCKNAME:  
    51.             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));  
    52.             if(er)  
    53.                 return er;  
    54.             return(sock_getsockname(get_fs_long(args+0),  
    55.                 (struct sockaddr *)get_fs_long(args+1),  
    56.                 (int *)get_fs_long(args+2)));  
    57.         case SYS_GETPEERNAME:  
    58.             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));  
    59.             if(er)  
    60.                 return er;  
    61.             return(sock_getpeername(get_fs_long(args+0),  
    62.                 (struct sockaddr *)get_fs_long(args+1),  
    63.                 (int *)get_fs_long(args+2)));  
    64.         case SYS_SOCKETPAIR:  
    65.             er=verify_area(VERIFY_READ, args, 4 * sizeof(long));  
    66.             if(er)  
    67.                 return er;  
    68.             return(sock_socketpair(get_fs_long(args+0),  
    69.                 get_fs_long(args+1),  
    70.                 get_fs_long(args+2),  
    71.                 (unsigned long *)get_fs_long(args+3)));  
    72.         case SYS_SEND:  
    73.             er=verify_area(VERIFY_READ, args, 4 * sizeof(unsigned long));  
    74.             if(er)  
    75.                 return er;  
    76.             return(sock_send(get_fs_long(args+0),  
    77.                 (void *)get_fs_long(args+1),  
    78.                 get_fs_long(args+2),  
    79.                 get_fs_long(args+3)));  
    80.         case SYS_SENDTO:  
    81.             er=verify_area(VERIFY_READ, args, 6 * sizeof(unsigned long));  
    82.             if(er)  
    83.                 return er;  
    84.             return(sock_sendto(get_fs_long(args+0),  
    85.                 (void *)get_fs_long(args+1),  
    86.                 get_fs_long(args+2),  
    87.                 get_fs_long(args+3),  
    88.                 (struct sockaddr *)get_fs_long(args+4),  
    89.                 get_fs_long(args+5)));  
    90.         case SYS_RECV:  
    91.             er=verify_area(VERIFY_READ, args, 4 * sizeof(unsigned long));  
    92.             if(er)  
    93.                 return er;  
    94.             return(sock_recv(get_fs_long(args+0),  
    95.                 (void *)get_fs_long(args+1),  
    96.                 get_fs_long(args+2),  
    97.                 get_fs_long(args+3)));  
    98.         case SYS_RECVFROM:  
    99.             er=verify_area(VERIFY_READ, args, 6 * sizeof(unsigned long));  
    100.             if(er)  
    101.                 return er;  
    102.             return(sock_recvfrom(get_fs_long(args+0),  
    103.                 (void *)get_fs_long(args+1),  
    104.                 get_fs_long(args+2),  
    105.                 get_fs_long(args+3),  
    106.                 (struct sockaddr *)get_fs_long(args+4),  
    107.                 (int *)get_fs_long(args+5)));  
    108.         case SYS_SHUTDOWN:  
    109.             er=verify_area(VERIFY_READ, args, 2* sizeof(unsigned long));  
    110.             if(er)  
    111.                 return er;  
    112.             return(sock_shutdown(get_fs_long(args+0),  
    113.                 get_fs_long(args+1)));  
    114.         case SYS_SETSOCKOPT:  
    115.             er=verify_area(VERIFY_READ, args, 5*sizeof(unsigned long));  
    116.             if(er)  
    117.                 return er;  
    118.             return(sock_setsockopt(get_fs_long(args+0),  
    119.                 get_fs_long(args+1),  
    120.                 get_fs_long(args+2),  
    121.                 (char *)get_fs_long(args+3),  
    122.                 get_fs_long(args+4)));  
    123.         case SYS_GETSOCKOPT:  
    124.             er=verify_area(VERIFY_READ, args, 5*sizeof(unsigned long));  
    125.             if(er)  
    126.                 return er;  
    127.             return(sock_getsockopt(get_fs_long(args+0),  
    128.                 get_fs_long(args+1),  
    129.                 get_fs_long(args+2),  
    130.                 (char *)get_fs_long(args+3),  
    131.                 (int *)get_fs_long(args+4)));  
    132.         default:  
    133.             return(-EINVAL);  
    134.     }  
    135. }  

    上面系统调用的宏定义如下:

    1. #define SYS_SOCKET  1       /* sys_socket(2)        */  
    2. #define SYS_BIND    2       /* sys_bind(2)          */  
    3. #define SYS_CONNECT 3       /* sys_connect(2)       */  
    4. #define SYS_LISTEN  4       /* sys_listen(2)        */  
    5. #define SYS_ACCEPT  5       /* sys_accept(2)        */  
    6. #define SYS_GETSOCKNAME 6       /* sys_getsockname(2)       */  
    7. #define SYS_GETPEERNAME 7       /* sys_getpeername(2)       */  
    8. #define SYS_SOCKETPAIR  8       /* sys_socketpair(2)        */  
    9. #define SYS_SEND    9       /* sys_send(2)          */  
    10. #define SYS_RECV    10      /* sys_recv(2)          */  
    11. #define SYS_SENDTO  11      /* sys_sendto(2)        */  
    12. #define SYS_RECVFROM    12      /* sys_recvfrom(2)      */  
    13. #define SYS_SHUTDOWN    13      /* sys_shutdown(2)      */  
    14. #define SYS_SETSOCKOPT  14      /* sys_setsockopt(2)        */  
    15. #define SYS_GETSOCKOPT  15      /* sys_getsockopt(2)        */  

    应用层在一系列操作后就可以通过参数SYS_RECV或SYS_RECVFROM来获取数据包。由于UDP是无连接的,所以如果需要回复,必须使用recvfrom才能得知是谁发送的数据包。当然UDP也可以用recv类函数,只是它不能回复,只能接收。

    这里还是以INET中UDP来举例说明。

    如果系统调用参数是SYS_RECVFROM,则会进行内存校验后执行函数socket_recvform()函数。

    1. /* 
    2.  *  Receive a frame from the socket and optionally record the address of the  
    3.  *  sender. We verify the buffers are writable and if needed move the 
    4.  *  sender address from kernel to user space. 
    5.  */  
    6.   
    7. static int sock_recvfrom(int fd, void * buff, int len, unsigned flags,  
    8.          struct sockaddr *addr, int *addr_len)  
    9. {  
    10.     struct socket *sock;  
    11.     struct file *file;  
    12.     char address[MAX_SOCK_ADDR];  
    13.     int err;  
    14.     int alen;  
    15.     if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))  
    16.         return(-EBADF);  
    17.     if (!(sock = sockfd_lookup(fd, NULL)))   
    18.         return(-ENOTSOCK);  
    19.     if(len<0)  
    20.         return -EINVAL;  
    21.     if(len==0)  
    22.         return 0;  
    23.   
    24.     err=verify_area(VERIFY_WRITE,buff,len);  
    25.     if(err)  
    26.         return err;  
    27.     //进行相应检查后调用下层函数,INET域则为inet_recvfrom()函数  
    28.     len=sock->ops->recvfrom(sock, buff, len, (file->f_flags & O_NONBLOCK),  
    29.              flags, (struct sockaddr *)address, &alen);  
    30.   
    31.     if(len<0)  
    32.         return len;  
    33.     if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0)//将发送发地址从内核空间COPY到用户空间  
    34.         return err;  
    35.   
    36.     return len;  
    37. }  

    在inet_recvfrom()函数中会调用具体的协议操作函数。UDP的协议操作函数定义如下:

    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. };  

    可以看到,其对应的函数对udp_recvfrom()

    1. /* 
    2.  *  This should be easy, if there is something there we\ 
    3.  *  return it, otherwise we block. 
    4.  */  
    5.   
    6. int udp_recvfrom(struct sock *sk, unsigned char *to, int len,  
    7.          int noblock, unsigned flags, struct sockaddr_in *sin,  
    8.          int *addr_len)  
    9. {  
    10.     int copied = 0;  
    11.     int truesize;  
    12.     struct sk_buff *skb;  
    13.     int er;  
    14.   
    15.     /* 
    16.      *  Check any passed addresses 
    17.      */  
    18.        
    19.     if (addr_len)   
    20.         *addr_len=sizeof(*sin);  
    21.     
    22.     /* 
    23.      *  From here the generic datagram does a lot of the work. Come 
    24.      *  the finished NET3, it will do _ALL_ the work! 
    25.      */  
    26.           
    27.     skb=skb_recv_datagram(sk,flags,noblock,&er);  
    28.     if(skb==NULL)  
    29.         return er;  
    30.     
    31.     truesize = skb->len;  
    32.     copied = min(len, truesize);  
    33.   
    34.     /* 
    35.      *  FIXME : should use udp header size info value  
    36.      */  
    37.        
    38.     skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);//从sk_buff结构中取出数据部分  
    39.     sk->stamp=skb->stamp;  
    40.   
    41.     /* Copy the address. */  
    42.     if (sin)   
    43.     {  
    44.         sin->sin_family = AF_INET;  
    45.         sin->sin_port = skb->h.uh->source;  
    46.         sin->sin_addr.s_addr = skb->daddr;  
    47.     }  
    48.     
    49.     skb_free_datagram(skb);  
    50.     release_sock(sk);  
    51.     return(truesize);  
    52. }  

    这样数据就到达了用户空间。



    普通文件操作函数接口

    最主要的函数就是读写函数:sock_read和sock_write,可以通过文件操作来完成网络数据的读写。谈到文件,就得有文件描述符,文件描述符中的f_inode指针指向文件的存储结点结构。

    文件操作集定义如下:

    1. static struct file_operations socket_file_ops = {  
    2.     sock_lseek,  
    3.     sock_read,  
    4.     sock_write,  
    5.     sock_readdir,  
    6.     sock_select,  
    7.     sock_ioctl,  
    8.     NULL,           /* mmap */  
    9.     NULL,           /* no special open code... */  
    10.     sock_close,  
    11.     NULL,           /* no fsync */  
    12.     sock_fasync  
    13. };  

    read函数和write函数与recvfrom和send类似,这里列出函数,方便查看。

    1. /* 
    2.  *  Read data from a socket. ubuf is a user mode pointer. We make sure the user 
    3.  *  area ubuf...ubuf+size-1 is writable before asking the protocol. 
    4.  */  
    5.   
    6. static int sock_read(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_read: can't find socket for inode!\n");  
    14.         return(-EBADF);  
    15.     }  
    16.     if (sock->flags & SO_ACCEPTCON)   
    17.         return(-EINVAL);  
    18.   
    19.     if(size<0)  
    20.         return -EINVAL;  
    21.     if(size==0)  
    22.         return 0;  
    23.     if ((err=verify_area(VERIFY_WRITE,ubuf,size))<0)  
    24.         return err;  
    25.     return(sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK)));//和recvfrom函数类似,调用INET域相应函数  
    26. }  
    上面会调用inet_read()函数,inet_read()函数会调用udp_read()函数,而udp_read()是通过调用udp_recvfrom()完成功能的。
    这两种方式是内核网络栈对用户的接口。
  • 相关阅读:
    虚拟主机的陷阱
    http://www.xmenglish.com/(外贸知识网站)
    Highlight Table Row
    让你从电脑维修新手到高手
    Flash 视频教程
    ASP Comparison Operators Logical Operators
    Linux 虚拟机 NAT方式上网设置
    vim技巧
    25 条 SSH 命令和技巧
    linux下添加路由的方法
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6173201.html
Copyright © 2011-2022 走看看