青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大龍的博客

常用鏈接

統計

最新評論

Linux TCP/IP協議棧之Socket的實現分析(一 套接字的創建) --- 轉

Socket并不是TCP/IP協議的一部份,從廣義上來講,socket是Unix/Linux抽像的進程間通訊的一種方法
網絡socket通訊僅僅是其若干協議中的一類,而tcp/ip又是網絡協議各類中的一種
從tcp/ip的角度看socket,它更多地體現了用戶API與協議棧的一個中間層接口層
用戶通過調用socket API將報文遞交給協議棧,或者從協議棧中接收報文

系統總入口
Linux內核為所有的與socket有關操作的API,提供了一個統一的系統調用入口,其代碼在net/socket.c中
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
    ...
    /* copy_from_user should be SMP safe. */
    if (copy_from_user(a, args, nargs[call]))
        return -EFAULT;

    a0=a[0];
    a1=a[1];
    switch(call)
    {
        case SYS_SOCKET:
            err = sys_socket(a0,a1,a[2]);
            break;
        case SYS_BIND:
            err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]);
            break;
        case SYS_CONNECT:
            err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
            break;
        case SYS_LISTEN:
            err = sys_listen(a0,a1);
            break;
        case SYS_ACCEPT:
            err = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
            break;
        case SYS_GETSOCKNAME:
            err = sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
            break;
        case SYS_GETPEERNAME:
            err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]);
            break;
        case SYS_SOCKETPAIR:
            err = sys_socketpair(a0,a1, a[2], (int __user *)a[3]);
            break;
        case SYS_SEND:
            err = sys_send(a0, (void __user *)a1, a[2], a[3]);
            break;
        case SYS_SENDTO:
            err = sys_sendto(a0,(void __user *)a1, a[2], a[3],
                     (struct sockaddr __user *)a[4], a[5]);
            break;
        case SYS_RECV:
            err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
            break;
        case SYS_RECVFROM:
            err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                       (struct sockaddr __user *)a[4], (int __user *)a[5]);
            break;
        case SYS_SHUTDOWN:
            err = sys_shutdown(a0,a1);
            break;
        case SYS_SETSOCKOPT:
            err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
            break;
        case SYS_GETSOCKOPT:
            err = sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]);
            break;
        case SYS_SENDMSG:
            err = sys_sendmsg(a0, (struct msghdr __user *) a1, a[2]);
            break;
        case SYS_RECVMSG:
            err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]);
            break;
        default:
            err = -EINVAL;
            break;
    }
    return err;
}
首先調用 copy_from_user將用戶態參數拷貝至數組a,但是問題在于每個被調用的 API 的參數不盡相同,
那么每次拷貝的字節在小如何判斷.來看其第三個參數 nargs[call],其中 call 是操作碼,后面有個大大的
switch...case 就是判斷它。對應的操作碼定義在 include/linux/net.h
#define SYS_SOCKET                     1                /* sys_socket(2)                */
#define SYS_BIND                         2                /* sys_bind(2)                        */
#define SYS_CONNECT                  3                /* sys_connect(2)                */
#define SYS_LISTEN                       4                /* sys_listen(2)                */
#define SYS_ACCEPT                     5                /* sys_accept(2)                */
#define SYS_GETSOCKNAME        6                /* sys_getsockname(2)                */
#define SYS_GETPEERNAME         7                /* sys_getpeername(2)                */
#define SYS_SOCKETPAIR             8                /* sys_socketpair(2)                */
#define SYS_SEND                          9                /* sys_send(2)                        */
#define SYS_RECV                         10                /* sys_recv(2)                        */
#define SYS_SENDTO                    11                /* sys_sendto(2)                */
#define SYS_RECVFROM               12                /* sys_recvfrom(2)                */
#define SYS_SHUTDOWN              13                /* sys_shutdown(2)                */
#define SYS_SETSOCKOPT           14                /* sys_setsockopt(2)                */
#define SYS_GETSOCKOPT          15                /* sys_getsockopt(2)                */
#define SYS_SENDMSG                 16                /* sys_sendmsg(2)                */
#define SYS_RECVMSG                 17                /* sys_recvmsg(2)                */
而數組nargs則根據操作碼的不同,計算對應的參數的空間大小:
/* Argument list sizes for sys_socketcall */
#define AL(x) ((x) * sizeof(unsigned long))
static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
                                 AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
                                 AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
#undef AL
當拷貝完成參數后,就進入一個switch...case...判斷操作碼,跳轉至對應的系統接口.

sys_socket函數
操作碼 SYS_SOCKET 是由 sys_socket()實現的:
1239 asmlinkage long sys_socket(int family, int type, int protocol)
1240 {
1241     int retval;
1242     struct socket *sock;
1243
1244     retval = sock_create(family, type, protocol, &sock);
1245     if (retval < 0)
1246         goto out;
1247
1248     retval = sock_map_fd(sock);
1249     if (retval < 0)
1250         goto out_release;
1251
1252 out:
1253     /* It may be already another descriptor 8) Not kernel problem. */
1254     return retval;
1255
1256 out_release:
1257     sock_release(sock);
1258     return retval;
1259 }
在分析這段代碼之前, 首先來看創建一個Socket, 對內核而言,究竟意味著什么?究竟需要內核干什么事?
當用戶空間要創建一個 socke 接口時,會調用 API 函數
int socket(int domain, int type, int protocol)
其三個參數分別表示協議族,協議類型(面向連接或無連接)以及協議

對于用戶態而言, 一個Socket, 就是一個特殊的已經打開的文件,為了對socket抽像出文件的概念,
內核中為socket定義了一個專門的文件系統類型sockfs.
 344 static struct vfsmount *sock_mnt __read_mostly;
 345
 346 static struct file_system_type sock_fs_type = {                                                                        
 347     .name =     "sockfs",
 348     .get_sb =   sockfs_get_sb,
 349     .kill_sb =    kill_anon_super,
 350 };
在模塊初始化的時候,安裝該文件系統: 
void __init sock_init(void)
{
        ……
        register_filesystem(&sock_fs_type);
        sock_mnt = kern_mount(&sock_fs_type);        
}
稍后還要回來繼續分析安裝中的一點細節

有了文件系統后,對內核而言,創建一個socket,就是在sockfs文件系統中創建一個文件節點(inode),并建立起為了實現
socket功能所需的一整套數據結構,包括struct inode和struct socket結構.
struct socket結構在內核中,就代表了一個"Socket",當一個struct socket數據結構被分配空間后,再將其與一個已打開
的文件“建立映射關系”.這樣,用戶態就可以用抽像的文件的概念來操作socket了
——當然由于網絡的特殊性,至少就目前而言,這種抽像,并不如其它模塊的抽像那么完美.

文件系統struct vfsmount中有一個成員指針mnt_sb指向該文件系統的超級塊,而超級塊結構struct super_lock
有一個重要的成員s_op指向了超級塊的操作函數表,其中有函數指針alloc_inode()即為在給定的超級塊下創建并初始化
一個新的索引節點對像. 也就是調用:
sock_mnt->mnt_sb->s_op->alloc_inode(sock_mnt->mnt_sb);
當然,連同相關的處理細節一起,這一操作被層層封裝至一個上層函數new_inode()

那如何分配一個struct socket結構呢?
如前所述,一個socket總是與一個inode 密切相關的.當然,在 inode 中設置一個socket成員是完全可行的,
但是這貌似浪費了空間——畢竟更多的文件系統沒有socket這個東東.
所以,內核引入了另一個socket_alloc結構
struct socket_alloc {
        struct socket socket;
        struct inode vfs_inode;
};
顯而易見,該結構實現了inode和socket的封裝.已知一個inode可以通過宏SOCKET_I來獲取
與之對應的 socket:
sock = SOCKET_I(inode);
static inline struct socket *SOCKET_I(struct inode *inode)
{
        return &container_of(inode, struct socket_alloc, vfs_inode)->socket;
}
但是這樣做也同時意味著在分配一個inode后,必須再分配一個socket_alloc結構,并實現對應的封裝.
否則container_of又能到哪兒去找到socket呢?
現在來簡要地看一個這個流程——這是文件系統安裝中的一個重要步驟

881 struct vfsmount *kern_mount(struct file_system_type *type)
882 {
883     return vfs_kern_mount(type, 0, type->name, NULL);
884 }

817 struct vfsmount *
818 vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
819 {
820     struct vfsmount *mnt;
821     char *secdata = NULL;
822     int error;
           ....
828     mnt = alloc_vfsmnt(name);
829     if (!mnt)
830         goto out;
841     ....
842     error = type->get_sb(type, flags, name, data, mnt);
843     if (error < 0)
844         goto out_free_secdata;
849
850     mnt->mnt_mountpoint = mnt->mnt_root;
851     mnt->mnt_parent = mnt;
852     up_write(&mnt->mnt_sb->s_umount);
853     free_secdata(secdata);
854     return mnt;
855     .....
865 }
申請文件系統mnt結構, 調用之前注冊的sock_fs_type的get_sb成員函數指針, 獲取相應的超級塊sb.
并將mnt->mnt_sb指向sock_fs_type中的超級塊

337 static int sockfs_get_sb(struct file_system_type *fs_type,
338     int flags, const char *dev_name, void *data, struct vfsmount *mnt)
339 {
340     return get_sb_pseudo(fs_type, "socket:", &sockfs_ops, SOCKFS_MAGIC,                                                
341                  mnt);
342 }
注意其第三個參數 sockfs_ops,它封裝了 sockfs 的功能函數表
331 static struct super_operations sockfs_ops = {
332     .alloc_inode =  sock_alloc_inode,
333     .destroy_inode =sock_destroy_inode,
334     .statfs =   simple_statfs,
335 };

struct super_block *
get_sb_pseudo(struct file_system_type *fs_type, char *name,
        struct super_operations *ops, unsigned long magic)
{
        struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);
                ……
        s->s_op = ops ? ops : &default_ops;
}
這里就是先獲取/分配一個超級塊,然后初始化超級塊的各成員,包括s_op,它封裝了對應的功能函數表.
s_op自然就指向了sockfs_ops,那前面提到的new_inode()函數分配inode時調用的
sock_mnt->mnt_sb->s_op->alloc_inode(sock_mnt->mnt_sb);
這個alloc_inode函數指針也就是sockfs_ops的sock_alloc_inode()函數——轉了一大圈,終于指到它了.

來看看sock_alloc_inode是如何分配一個inode節點的
 283 static kmem_cache_t * sock_inode_cachep __read_mostly;
 284
 285 static struct inode *sock_alloc_inode(struct super_block *sb)
 286 {
 287     struct socket_alloc *ei;
 288     ei = (struct socket_alloc *)kmem_cache_alloc(sock_inode_cachep, SLAB_KERNEL);                                      
 289     if (!ei)
 290         return NULL;
 291     init_waitqueue_head(&ei->socket.wait);
 292    
 293     ei->socket.fasync_list = NULL;
 294     ei->socket.state = SS_UNCONNECTED;
 295     ei->socket.flags = 0;
 296     ei->socket.ops = NULL;
 297     ei->socket.sk = NULL;
 298     ei->socket.file = NULL;
 299     ei->socket.flags = 0;
 300
 301     return &ei->vfs_inode;
 302 }
函數先分配了一個用于封裝socket和inode的 ei,然后在高速緩存中為之申請了一塊空間.
這樣inode和socket就同時都被分配了,接下來初始化socket的各個成員,這些成員在后面都會一一提到
 96 /**
 97  *  struct socket - general BSD socket
 98  *  @state: socket state (%SS_CONNECTED, etc)
 99  *  @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc)
100  *  @ops: protocol specific socket operations
101  *  @fasync_list: Asynchronous wake up list
102  *  @file: File back pointer for gc
103  *  @sk: internal networking protocol agnostic socket representation
104  *  @wait: wait queue for several uses
105  *  @type: socket type (%SOCK_STREAM, etc)
106  */
107 struct socket {
108     socket_state        state;
109     unsigned long       flags;
110     const struct proto_ops  *ops;
111     struct fasync_struct    *fasync_list;
112     struct file     *file;
113     struct sock     *sk;                                                                                                
114     wait_queue_head_t   wait;
115     short           type;
116 };
至目前為止,分配inode,socket以及兩者如何關聯,都已一一分析了.
最后一個關鍵問題,就是如何把socket與一個已打開的文件,建立映射關系.

在內核中,用struct file結構描述一個已經打開的文件,指向該結構的指針內核中通常用file或filp來描述.
我們知道,內核中可以通過全局項current來獲得當前進程,它是一個struct task_struct類型的指針.
tastk_struct有一個成員:
struct files_struct *files;指向一個已打開的文件.
當然,由于一個進程可能打開多個文件,所以,struct files_struct 結構有
struct file *fd_array[NR_OPEN_DEFAULT]成員,
這是個數組,以文件描述符為下標,即current->files->fd[fd],可以找到與當前進程指定文件描述符的文件

有了這些基礎,如果要把一個socket與一個已打開的文件建立映射,首先要做的就是為socket分配一個struct file,
并申請分配一個相應的文件描述符fd. 因為socket并不支持open方法,所以不能期望用戶界面通過調用open() API
來分配一個struct file,而是通過調用get_empty_filp來獲取
struct file *file = get_empty_filp()
fd = get_unused_fd();獲取一個空間的文件描述符
然后讓current的files指針的fd數組的fd索引項指向該file
void fastcall fd_install(unsigned int fd, struct file * file)
{
        struct files_struct *files = current->files;
        spin_lock(&files->file_lock);
        if (unlikely(files->fd[fd] != NULL))
                BUG();
        files->fd[fd] = file;
        spin_unlock(&files->file_lock);
}
做到這一步,有了一個文件描述符fd和一個打開的文件file,它們與當前進程相連,但是好像與創建的socket并無任何瓜葛.
要做的映射還是沒有進展,struct file或者文件描述述fd或current都沒有任何能夠與 inode或者是socket相關的東東
這需要一個中間的橋梁,目錄項:struct dentry結構

因為一個文件都有與其對應的目錄項:
struct file {
        struct list_head        f_list;
        struct dentry          *f_dentry;
        ……
而一個目錄項:
struct dentry {
        ……
        struct inode *d_inode;                /* Where the name belongs to - NULL is negative */
d_inode 成員指向了與之對應的 inode節點

之前已經創建了一個inode節點和與之對應的 socket. 所以現在要做的就是:
“先為當前文件分配一個對應的目錄項,再將已創建的 inode節點安裝至該目錄項”
這樣一個完成的映射關系:
進程,文件描述符,打開文件,目錄項,inode節點,socket就完整地串起來了
 
基本要分析的一些前導的東東都一一羅列了,雖然已盡量避免陷入文件系統的細節分析,但是還是不可避免地進入其中,
因為它們關系實現太緊密了,現在可以來看套接字的創建過程了
1239 asmlinkage long sys_socket(int family, int type, int protocol)
1240 {
1241     int retval;
1242     struct socket *sock;
1243
1244     retval = sock_create(family, type, protocol, &sock);                                                               
1245     if (retval < 0)
1246         goto out;
1247
1248     retval = sock_map_fd(sock);
1249     if (retval < 0)
1250         goto out_release;
1251
1252 out:
1253     /* It may be already another descriptor 8) Not kernel problem. */
1254     return retval;
1255
1256 out_release:
1257     sock_release(sock);
1258     return retval;
1259 }
1229 int sock_create(int family, int type, int protocol, struct socket **res)
1230 {
1231     return __sock_create(family, type, protocol, res, 0);
1232 }

AF_INET協議簇的協議封裝
接下來,函數調用之前已經注冊的inet_family_ops的函數指針create,也就是inet_create()函數.
前面可以說一個通用的socket已經創建好了,這里要完成與協議本身相關的一些創建socket的工作.
這一部份的工作比較復雜,還是先來看看af_inet.c中的模塊初始化時候,做了哪些與此相關的工作.

要引入的第一個數據結構是struct inet_protosw,它封裝了一個協議類型(如 SOCK_STREAM,SOCK_DGRAM等)
與IP協議中對應的傳輸層協議.
 68 /* This is used to register socket interfaces for IP protocols.  */                                                     
 69 struct inet_protosw {     
 70     struct list_head list;
 71
 72     /* These two fields form the lookup key.  */
 73     unsigned short   type;     /* This is the 2nd argument to socket(2). */
 74     int      protocol; /* This is the L4 protocol number.  */                                                           
 75
 76     struct proto     *prot;
 77     const struct proto_ops *ops;                                                                                        
 78  
 79     int                capability;   /* Which (if any) capability do we need to use this socket interface*/            
 83     char             no_check;   /* checksum on rcv/xmit/none? */
 84     unsigned char    flags;    /* See INET_PROTOSW_* below.  */                                                       
 85 };
type是協議類型,對于 ipv4 而言就是SOCK_STREAM,SOCK_DGRAM或者是SOCK_RAW之一.
protocol是傳輸層的協議號,prot用于描述一個具體的傳輸層協議,而ops指向對應的當前協議類型的操作函數集
針對不同的協議類型,定義了不同的 ops:
791 const struct proto_ops inet_stream_ops = {
 792     .family        = PF_INET,
 793     .owner         = THIS_MODULE,
 794     .release       = inet_release,
 795     .bind          = inet_bind,
 796     .connect       = inet_stream_connect,
 797     .socketpair    = sock_no_socketpair,
 798     .accept        = inet_accept,
 799     .getname       = inet_getname,
 800     .poll          = tcp_poll,
 801     .ioctl         = inet_ioctl,
 802     .listen        = inet_listen,
 803     .shutdown      = inet_shutdown,
 804     .setsockopt    = sock_common_setsockopt,
 805     .getsockopt    = sock_common_getsockopt,
 806     .sendmsg       = inet_sendmsg,
 807     .recvmsg       = sock_common_recvmsg,
 808     .mmap          = sock_no_mmap,
 809     .sendpage      = tcp_sendpage,
 810 #ifdef CONFIG_COMPAT
 811     .compat_setsockopt = compat_sock_common_setsockopt,
 812     .compat_getsockopt = compat_sock_common_getsockopt,
 813 #endif
 814 };
 815
 816 const struct proto_ops inet_dgram_ops = {
 817     .family        = PF_INET,
 818     .owner         = THIS_MODULE,
 819     .release       = inet_release,
 820     .bind          = inet_bind,
 821     .connect       = inet_dgram_connect,
 822     .socketpair    = sock_no_socketpair,
 823     .accept        = sock_no_accept,
 824     .getname       = inet_getname,
 825     .poll          = udp_poll,
 826     .ioctl         = inet_ioctl,
 827     .listen        = sock_no_listen,
828     .shutdown      = inet_shutdown,
 829     .setsockopt    = sock_common_setsockopt,
 830     .getsockopt    = sock_common_getsockopt,
 831     .sendmsg       = inet_sendmsg,
 832     .recvmsg       = sock_common_recvmsg,
 833     .mmap          = sock_no_mmap,
 834     .sendpage      = inet_sendpage,
 835 #ifdef CONFIG_COMPAT
 836     .compat_setsockopt = compat_sock_common_setsockopt,
 837     .compat_getsockopt = compat_sock_common_getsockopt,
 838 #endif
 839 };
 840
 841 /*
 842  * For SOCK_RAW sockets; should be the same as inet_dgram_ops but without
 843  * udp_poll
 844  */
845 static const struct proto_ops inet_sockraw_ops = {
 846     .family        = PF_INET,
 847     .owner         = THIS_MODULE,
 848     .release       = inet_release,
 849     .bind          = inet_bind,
 850     .connect       = inet_dgram_connect,
 851     .socketpair    = sock_no_socketpair,
 852     .accept        = sock_no_accept,
 853     .getname       = inet_getname,
 854     .poll          = datagram_poll,
 855     .ioctl         = inet_ioctl,                                                                                       
 856     .listen        = sock_no_listen,
 857     .shutdown      = inet_shutdown,
 858     .setsockopt    = sock_common_setsockopt,
 859     .getsockopt    = sock_common_getsockopt,
 860     .sendmsg       = inet_sendmsg,
 861     .recvmsg       = sock_common_recvmsg,
 862     .mmap          = sock_no_mmap,
 863     .sendpage      = inet_sendpage,
 864 #ifdef CONFIG_COMPAT
 865     .compat_setsockopt = compat_sock_common_setsockopt,
 866     .compat_getsockopt = compat_sock_common_getsockopt,
 867 #endif
 868 };
從各個函數指針的名稱,我們就可以大約知道它們是做什么事的了.進一步進以看到,
它們的函數指針指向的函數差不多都是相同的.除了一些細節上的區別,例如后面兩種協議類型并不支持listen.

socket()API第二個參數是協議類型,第三個參數是該協議類型下的協議——不過對于ipv4而言,
它們都是一一對應的,但是從抽像封裝的角度看,數據結構的設計本身應該滿足一個協議類型下邊可能存在多個不同的協議,
即一對多的情況.而一一對應,僅是它們的特例:
876 /* Upon startup we insert all the elements in inetsw_array[] into
 877  * the linked list inetsw.
 878  */
 879 static struct inet_protosw inetsw_array[] =
 880 {
 881         {
 882                 .type =       SOCK_STREAM,
 883                 .protocol =   IPPROTO_TCP,
 884                 .prot =       &tcp_prot,
 885                 .ops =        &inet_stream_ops,
 886                 .capability = -1,
 887                 .no_check =   0,
 888                 .flags =      INET_PROTOSW_PERMANENT |
 889                   INET_PROTOSW_ICSK,
 890         },
 892         {
 893                 .type =       SOCK_DGRAM,
 894                 .protocol =   IPPROTO_UDP,
 895                 .prot =       &udp_prot,
 896                 .ops =        &inet_dgram_ops,
 897                 .capability = -1,
 898                 .no_check =   UDP_CSUM_DEFAULT,
 899                 .flags =      INET_PROTOSW_PERMANENT,
 900        },
 903        {
 904                .type =       SOCK_RAW,
 905                .protocol =   IPPROTO_IP,    /* wild card */
 906                .prot =       &raw_prot,
 907                .ops =        &inet_sockraw_ops,
 908                .capability = CAP_NET_RAW,
 909                .no_check =   UDP_CSUM_DEFAULT,
 910                .flags =      INET_PROTOSW_REUSE,
 911        }
 912 };
數組的每一個元素,就是支持的一種協議名稱,例如IPOROTO_TCP,但是由于IPV4本身協議類型跟協議是一一對應的,
所以沒有更多的.type= SOCK_xxx 了.這樣數組實現了對PF_INET協議族下支持的協議類型,
以及協議類型下邊的協議進行了封裝,雖然事實上它們是一一對應的關系,不過理論上完全可能存在一對多的可能.

數組內,封裝的一個具體的協議,由 struct proto 結構來描述
以 TCP協議為例,TCP協議的 sokcet 操作函數都被封裝在這里了。 
struct proto tcp_prot = {
        .name                        = "TCP",
        .owner                        = THIS_MODULE,
        .close                        = tcp_close,
        .connect                = tcp_v4_connect,
        .disconnect                = tcp_disconnect,
        .accept                        = tcp_accept,
        .ioctl                        = tcp_ioctl,
        .init                        = tcp_v4_init_sock,
        .destroy                = tcp_v4_destroy_sock,
        .shutdown                = tcp_shutdown,
        .setsockopt                = tcp_setsockopt,
        .getsockopt                = tcp_getsockopt,
        .sendmsg                = tcp_sendmsg,
        .recvmsg                = tcp_recvmsg,
        .backlog_rcv                = tcp_v4_do_rcv,
        .hash                        = tcp_v4_hash,
        .unhash                        = tcp_unhash,
        .get_port                = tcp_v4_get_port,
        .enter_memory_pressure        = tcp_enter_memory_pressure,
        .sockets_allocated        = &tcp_sockets_allocated,
        .memory_allocated        = &tcp_memory_allocated,
        .memory_pressure        = &tcp_memory_pressure,
        .sysctl_mem                = sysctl_tcp_mem,
        .sysctl_wmem                = sysctl_tcp_wmem,
        .sysctl_rmem                = sysctl_tcp_rmem,
        .max_header                = MAX_TCP_HEADER,
        .obj_size                = sizeof(struct tcp_sock),
}
分配struct sock
看完了PF_INET的協議簇,協議類型和協議(也就是socket調用的三個參數)的封裝關系,它們通過了兩個數據結構
inet_protosw,struct proto來描述,被一個數組inetsw_array所封裝.接下來看它的初始化工作:
static struct list_head inetsw[SOCK_MAX];
static int __init inet_init(void)
{
        ……
        /* Register the socket-side information for inet_create. */
        for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
                INIT_LIST_HEAD(r);
        for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
                inet_register_protosw(q);
        ……
}
inetsw是一個數組,其每一個元素都是一個鏈表首部,前面一個循環初始化之.后一個循環就值得注意了,
也就是函數
916 void inet_register_protosw(struct inet_protosw *p)
 917 {
 918     struct list_head *lh;
 919     struct inet_protosw *answer;
 920     int protocol = p->protocol;
 921     struct list_head *last_perm;
 922
 923     spin_lock_bh(&inetsw_lock);
 925     if (p->type >= SOCK_MAX)
 926         goto out_illegal;
 928     /* If we are trying to override a permanent protocol, bail. */
 929     answer = NULL;
 930     last_perm = &inetsw[p->type];
 931     list_for_each(lh, &inetsw[p->type]) {
 932         answer = list_entry(lh, struct inet_protosw, list);
 934         /* Check only the non-wild match. */
 935         if (INET_PROTOSW_PERMANENT & answer->flags) {
 936             if (protocol == answer->protocol)
 937                 break;
 938             last_perm = lh;
 939         }
 941         answer = NULL;
 942     }
 943     if (answer)
 944         goto out_permanent;
 943     if (answer)
 944         goto out_permanent;
 945
 946     /* Add the new entry after the last permanent entry if any, so that
 947      * the new entry does not override a permanent entry when matched with
 948      * a wild-card protocol. But it is allowed to override any existing
 949      * non-permanent entry.  This means that when we remove this entry, the
 950      * system automatically returns to the old behavior.
 951      */
 952     list_add_rcu(&p->list, last_perm);
 953 out:
 954     spin_unlock_bh(&inetsw_lock);
 955
 956     synchronize_net();
 957
 958     return;
 .....................
這個函數完成的工作就是把inetsw_array數組中相同的協議類型下邊的協議,
加入到inetsw對應的協議類型的鏈表中去,因為事實上一對一的關系,所以這個函數要簡單得多
因為不存在其它成員,所以每一次list_entry都為空值,所以不存在覆蓋和追加的情況,直接調用
list_add_rcu(&p->list, last_perm);
把協議類型節點(struct inet_protosw類型的數組的某個元素)添加到鏈表(鏈表首部本身是一個數組,
數組索引是協議對應的協議類型的值)的第一個成員.

OK,繞了這么大一圈子,了解了協議的封裝及鏈表的注冊. 現在回到inet_create中來
 220 /*
 221  *  Create an inet socket.
 222  */
 223
 224 static int inet_create(struct socket *sock, int protocol)
 225 {
 226     struct sock *sk;
 227     struct list_head *p;
 228     struct inet_protosw *answer;
 229     struct inet_sock *inet;
 230     struct proto *answer_prot;
 231     unsigned char answer_flags;
 232     char answer_no_check;
 233     int try_loading_module = 0;
 234     int err;
 235
 236     sock->state = SS_UNCONNECTED;
socket的初始狀態設置為“未連接”,這意味著面向連接的協議類型,如 tcp,在使用之前必須建立連接, 修改狀態位.

 237
 238     /* Look for the requested type/protocol pair. */
 239     answer = NULL;
 240 lookup_protocol:
 241     err = -ESOCKTNOSUPPORT;
 242     rcu_read_lock();
 243     list_for_each_rcu(p, &inetsw[sock->type]) {
 244         answer = list_entry(p, struct inet_protosw, list);
 245
 246         /* Check the non-wild match. */
 247         if (protocol == answer->protocol) {
 248             if (protocol != IPPROTO_IP)
 249                 break;
 250         } else {
 251             /* Check for the two wild cases. */
 252             if (IPPROTO_IP == protocol) {
 253                 protocol = answer->protocol;
 254                 break;
 255             }
 256             if (IPPROTO_IP == answer->protocol)
 257                 break;
 258         }
 259         err = -EPROTONOSUPPORT;
 260         answer = NULL;
 261     }                                                                                                                  
這個循環根據socket(2)調用的protocol把之前在鏈表中注冊的協議節點找出來.
一個問題是,因為一一對應關系的存在,用戶態調用socket(2)的時候,常常第三個參數直接就置 0 了.
也就是這里protocol為 0.那內核又如何處理這一默認值呢?
也就是protocol != answer->protocol,而是被if (IPPROTO_IP == protocol) 所匹配了.
這樣將protocol置為鏈表中第一個協議,而當循環結束時,answer自然也是指向這個鏈表中的第一個注冊節點.
假設SOCK_STREAM下同時注冊了TCP和123,那么這里默認就取TCP了.當然如果把123在inetsw_array數組中的
位置調前,那么就 默認取123了.

將創建的socket的ops函數指針集指向具體協議類型的.例如創建的是SOCK_STREAM,
那么就指向了inet_stream_ops.
 289     sock->ops = answer->ops;
answer_prot指針指向當前要創建的socket的協議類型下邊的協議,如上例它就是IPPROTO_TCP的tcp_prot結構
 290     answer_prot = answer->prot;
 291     answer_no_check = answer->no_check;
 292     answer_flags = answer->flags;
 293     rcu_read_unlock();
 294
 295     BUG_TRAP(answer_prot->slab != NULL);

接下來一個重要的工作,就是為 socket 分配一個sock,并初始化它
 298     sk = sk_alloc(PF_INET, GFP_KERNEL, answer_prot, 1);
 299     if (sk == NULL)
 300         goto out;
 301
 302     err = 0;
 303     sk->sk_no_check = answer_no_check;
 304     if (INET_PROTOSW_REUSE & answer_flags)
 305         sk->sk_reuse = 1;
 306
 307     inet = inet_sk(sk);
 308     inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;
 309
 310     if (SOCK_RAW == sock->type) {
 311         inet->num = protocol;
 312         if (IPPROTO_RAW == protocol)
 313             inet->hdrincl = 1;
 314     }
 315
 316     if (ipv4_config.no_pmtu_disc)
 317         inet->pmtudisc = IP_PMTUDISC_DONT;
 318     else
 319         inet->pmtudisc = IP_PMTUDISC_WANT;

 321     inet->id = 0;
 322
 323     sock_init_data(sock, sk);
 324
 325     sk->sk_destruct    = inet_sock_destruct;
 326     sk->sk_family      = PF_INET;
 327     sk->sk_protocol    = protocol;
 328     sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
 329
 330     inet->uc_ttl    = -1;
 331     inet->mc_loop   = 1;
 332     inet->mc_ttl    = 1;
 333     inet->mc_index  = 0;
 334     inet->mc_list   = NULL;
 335
 336     sk_refcnt_debug_inc(sk);
 337
 338     if (inet->num) {
 339         /* It assumes that any protocol which allows
 340          * the user to assign a number at socket
 341          * creation time automatically
 342          * shares.
 343          */
 344         inet->sport = htons(inet->num);
 345         /* Add to protocol hash chains. */
 346         sk->sk_prot->hash(sk);
 347     }
 348
 349     if (sk->sk_prot->init) {
 350         err = sk->sk_prot->init(sk);
 351         if (err)
 352             sk_common_release(sk);
 353     }
 354 out:
 355     return err;
 356 out_rcu_unlock:
 357     rcu_read_unlock();  
 358     goto out;
 359 }
雖然create的代碼就到這兒了,不過要說清楚sk的分配,還得費上大力氣.每一個 Socket 套接字,
都有一個對應的struct socket結構來描述(內核中一般使用名稱為sock),但是同時又有一個
struct sock結構(內核中一般使用名稱為 sk).兩者之間是一一對應的關系.在后面的sock_init_data函數中可以看到
sk->sk_socket=sock;
sock->sk=sk;
這樣的代碼.

socket結構和sock結構實際上是同一個事物的兩個方面.不妨說socket結構是面向進程和系統調用界面的側面,
而sock結構則是面向底層驅動程序的側面.設計者把socket套接字中與文件系統關系比較密切的那一部份放在
socket結構中而把與通信關系比較密切的那一部份,則單獨成為一個數結結構,那就是sock結構.
由于這兩部份邏輯上本來就是一體的,所以要通過指針互相指向對方形成一對一的關系.

再暫時回到inet_init中來,初始化工作中有如下代碼:
1262     rc = proto_register(&tcp_prot, 1);
1263     if (rc)
1264         goto out;
1265
1266     rc = proto_register(&udp_prot, 1);
1267     if (rc)
1268         goto out_unregister_tcp_proto;
1269
1270     rc = proto_register(&raw_prot, 1);
1271     if (rc)
1272         goto out_unregister_udp_proto;
這里為每個protocol都調用了proto_register函數,其重要功能之一就是根據協議的obj_size成員的大小,
為協議創建高速緩存.
1701 static DEFINE_RWLOCK(proto_list_lock);
1702 static LIST_HEAD(proto_list);
1703
1704 int proto_register(struct proto *prot, int alloc_slab)
1705 {
1706     char *request_sock_slab_name = NULL;
1707     char *timewait_sock_slab_name;
1708     int rc = -ENOBUFS;
1709
1710     if (alloc_slab) {

可以看到函數最重要的功能就是根據prot的obj_size成員的大小為協議創建高速緩存
1711         prot->slab = kmem_cache_create(prot->name, prot->obj_size, 0,
1712                            SLAB_HWCACHE_ALIGN, NULL, NULL);
1713
1714         if (prot->slab == NULL) {
1715             printk(KERN_CRIT "%s: Can't create sock SLAB cache!\n",
1716                    prot->name);
1717             goto out;
1718         }
1719
順便看到它的另一個重要的功能是維護一個以proto_list為首的鏈表
1758     write_lock(&proto_list_lock);
1759     list_add(&prot->node, &proto_list);
1760     write_unlock(&proto_list_lock);
這里要注意的是prot->obj_size的大小,它它非僅僅是一個sk的大小,以 TCP為例:
.obj_size = sizeof(struct tcp_sock)。稍后再來分析這個東東

回到inet_create()函數中來,其調用sk_alloc()分配一個sk
sk = sk_alloc(PF_INET, GFP_KERNEL, answer_prot, 1);
840 struct sock *sk_alloc(int family, gfp_t priority,
 841               struct proto *prot, int zero_it)
 842 {
 843     struct sock *sk = NULL;
 844     kmem_cache_t *slab = prot->slab;
 845
 846     if (slab != NULL)
 847         sk = kmem_cache_alloc(slab, priority);
 848     else
 849         sk = kmalloc(prot->obj_size, priority);
 850
 851     if (sk) {
 852         if (zero_it) {
 853             memset(sk, 0, prot->obj_size);
 854             sk->sk_family = family;
 855             /*
 856              * See comment in struct sock definition to understand
 857              * why we need sk_prot_creator -acme
 858              */
 859             sk->sk_prot = sk->sk_prot_creator = prot;
 860             sock_lock_init(sk);
 861         }
 862        
 863         if (security_sk_alloc(sk, family, priority))
 864             goto out_free;
 865
 866         if (!try_module_get(prot->owner))
 867             goto out_free;
 868     }
 869     return sk;
 870
 871 out_free:
 872     if (slab != NULL)
 873         kmem_cache_free(slab, sk);
 874     else
 875         kfree(sk);
 876     return NULL;               
 877 }
在之前創建的高速緩存中申請分配一個slab緩存項并清零,然后設置協議族,并把sk中的sk_prot與對應的協議關聯起來

初始化sk

分配完成sk后另一個重要的功能就是初始化它,sk的成員相當復雜,其主要的初始化工作是在函數sock_init_data()
中完成的.
1477 void sock_init_data(struct socket *sock, struct sock *sk)
1478 {
1479     skb_queue_head_init(&sk->sk_receive_queue);
1480     skb_queue_head_init(&sk->sk_write_queue);
1481     skb_queue_head_init(&sk->sk_error_queue);
sock結構中有三個重要的雙向隊列分別是sk_receive_queue,sk_write_queue和sk_error_queue
從它們的名字就可以看出來其作用了
隊列并非采用通用的list_head來維護而是使用skb_buffer隊列
// 109 struct sk_buff_head {                                                                                                  
// 110     /* These two members must be first. */
// 111     struct sk_buff  *next;
// 112     struct sk_buff  *prev;
// 113    
// 114     __u32       qlen;
// 115     spinlock_t  lock;
// 116 }; 
這樣隊列中指向的每一個skb_buffer就是一個數據包,分別是接收、發送和投遞錯誤
剩余的就是初始化其它成員變量了,后面再來專門分析這些成員的作用
1482 #ifdef CONFIG_NET_DMA
1483     skb_queue_head_init(&sk->sk_async_wait_queue);
1484 #endif
1485
1486     sk->sk_send_head    =   NULL;
1487
1488     init_timer(&sk->sk_timer);
1489    
1490     sk->sk_allocation   =   GFP_KERNEL;
1491     sk->sk_rcvbuf       =   sysctl_rmem_default;
1492     sk->sk_sndbuf       =   sysctl_wmem_default;
1493     sk->sk_state        =   TCP_CLOSE;
1494     sk->sk_socket       =   sock;
1495
1496     sock_set_flag(sk, SOCK_ZAPPED);
1497
1498     if(sock)
1499     {
1500         sk->sk_type =   sock->type;
1501         sk->sk_sleep    =   &sock->wait;
1502         sock->sk    =   sk;
1503     } else
1504         sk->sk_sleep    =   NULL;
1505
1506     rwlock_init(&sk->sk_dst_lock);
1507     rwlock_init(&sk->sk_callback_lock);
1508     lockdep_set_class(&sk->sk_callback_lock,
1509                af_callback_keys + sk->sk_family);
1510
1511     sk->sk_state_change =   sock_def_wakeup;
1512     sk->sk_data_ready   =   sock_def_readable;
1513     sk->sk_write_space  =   sock_def_write_space;  
1514     sk->sk_error_report =   sock_def_error_report;
1515     sk->sk_destruct     =   sock_def_destruct;
1516
1517     sk->sk_sndmsg_page  =   NULL;
1518     sk->sk_sndmsg_off   =   0;
1519
1520     sk->sk_peercred.pid     =   0;
1521     sk->sk_peercred.uid =   -1;
1522     sk->sk_peercred.gid =   -1;
1523     sk->sk_write_pending    =   0;
1524     sk->sk_rcvlowat     =   1;
1525     sk->sk_rcvtimeo     =   MAX_SCHEDULE_TIMEOUT;
1526     sk->sk_sndtimeo     =   MAX_SCHEDULE_TIMEOUT;
1527
1528     sk->sk_stamp.tv_sec     = -1L;
1529     sk->sk_stamp.tv_usec    = -1L;
1530
1531     atomic_set(&sk->sk_refcnt, 1);
1532 }

inet_create函數中除了初始化sk成員的值還有一部份代碼是初始化一個inet的東東
 307     inet = inet_sk(sk);
 308     inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;

inet是一個struct inet_sock結構類型來看它的定義
struct inet_sock {
        /* sk and pinet6 has to be the first two members of inet_sock */
        struct sock                sk;
               ……
}
我們說sock是面向用戶態調用而sk是面向內核驅動調用的,那sk是如何與協議棧交互的呢?
對于每一個類型的協議,為了與 sk 聯系起來都定義了一個struct XXX_sock結構XXX是協議名
struct tcp_sock {
        /* inet_sock has to be the first member of tcp_sock */
        struct inet_sock        inet;
        int        tcp_header_len;        /* Bytes of tcp header to send                */
        ……

struct udp_sock {
        /* inet_sock has to be the first member */
        struct inet_sock inet;
        int                 pending;        /* Any pending frames ? */
        unsigned int         corkflag;        /* Cork is required */
          __u16                 encap_type;        /* Is this an Encapsulation socket? */
        /*
         * Following member retains the infomation to create a UDP header
         * when the socket is uncorked.
         */
        __u16                 len;                /* total length of pending frames */
};
 
struct raw_sock {
        /* inet_sock has to be the first member */
        struct inet_sock   inet;
        struct icmp_filter filter;
};
 
很明顯它們的結構定義是“af_inet一般屬性+自己的私有屬性”, 因為它們的第一個成員總是inet 
 
現在回頭來找一下起初在af_inet.c中封裝協議注冊的時候size成員, 對于 tcp 而言:
.obj_size  = sizeof(struct tcp_sock),
其它協議類似.
以obj_size來確定每個slab緩存項分配的大小,所以我們就可說每次申請分配的實際上是一個struct XXX_sock
結構大小的結構.因為都是定義于上層結構的第一個成員,可以使用強制類型轉換來使用這塊分配的內存空間.
例如inet = inet_sk(sk);
 
static inline struct inet_sock *inet_sk(const struct sock *sk)
{
        return (struct inet_sock *)sk;
}
 
struct tcp_sock *tp = tcp_sk(sk);
 
static inline struct tcp_sock *tcp_sk(const struct sock *sk)
{
        return (struct tcp_sock *)sk;
}
 
OK,inet_create()運行完,一個socket套接字基本上就創建完畢了,剩下的就是與文件系統掛鉤,回到最初的sys_socket()
函數中來,它在調用完sock_create()后,緊接著調用sock_map_fd()函數
 422 int sock_map_fd(struct socket *sock)
 423 {
 424     struct file *newfile;
 425     int fd = sock_alloc_fd(&newfile);
 426
 427     if (likely(fd >= 0)) {
 428         int err = sock_attach_fd(sock, newfile);
 429
 430         if (unlikely(err < 0)) {           
 431             put_filp(newfile);
 432             put_unused_fd(fd);
 433             return err;  
 434         }                
 435         fd_install(fd, newfile);   
 436     }                    
 437     return fd;           
 438 }
這個函數的核心思想在一開始就已經分析過了.從進程的角度來講一個socket套接字就是一個特殊的已打開的文件.
前面分配好一個socket 后,這里要做的就是將它與文件系統拉上親戚關系.
首先獲取一個空閑的文件描述符號和file結構,然后在文件系統中分配一個目錄項(d_alloc),使其指向已經分配的inode節
點(d_add),然后把其目錄項掛在sockfs文件系統的根目錄之下,并且把目錄項的指針d_op設置成
指向sockfs_dentry_operati,這個數據結構通過函數指針提供他與文件路徑有關的操作.
static struct dentry_operations sockfs_dentry_operations = {
        .d_delete =        sockfs_delete_dentry,
};
最后一步就是將file結構中的f_op和sock結構中的i_fop都指向socket_file_ops,它是一個函數指針集,
指向了socket面向文件系統的用戶態調用的一些接口函數.
static struct file_operations socket_file_ops = {
        .owner =        THIS_MODULE,
        .llseek =        no_llseek,
        .aio_read =        sock_aio_read,
        .aio_write =        sock_aio_write,
        .poll =                sock_poll,
        .unlocked_ioctl = sock_ioctl,
        .mmap =                sock_mmap,
        .open =                sock_no_open,        /* special open code to disallow open via /proc */
        .release =        sock_close,
        .fasync =        sock_fasync,
        .readv =        sock_readv,
        .writev =        sock_writev,
        .sendpage =        sock_sendpage
};
OK,到這里整個socket套接字的創建工作就宣告完成了

寫到這里,可以為 socket 的創建下一個小結了:
  1. 所謂創建socket,對內核而言最重要的工作就是分配sock與sk
  2. sock面向上層系統調用,主要是與文件系統交互.通過進程的current指針的files,結合創建socket
    時返回的文件描符述,可以找到內 核中對應的struct file,再根據file的f_dentry可以找到對應的目
    錄項,而目錄項struct dentry中,有d_inode指針,指向與sock封裝在一起的inode.sock又與
    sk指針互指一一對應.在這串結構中有兩個重要的函數集指針,一個是文件系統struct file中的
    f_op指針,它指向了對應的用戶態調用的read,write等操調用,但不支持open,
    另一個是struct socket結構,即sock的ops指針,它在inet_create()中被置為
    sock->ops = answer->ops指向具體協議類型的ops
    例如inet_stream_ops,inet_dgram_ops或者是inet_sockraw_ops等等
    它用來支持上層的socket的其它API調用
  3. sk面向內核協議棧,協議棧與它的接口數據結構是struct protoname_sock,該結構中包含了一般性
    的inet結構和自己的私有成員,struct inet_sock的第一個成員就是一個 sk 指針,而分配的sk實
    際上空間大小是struct protoname_sock,所以這三者可以通過強制類型轉換來獲取需要的指針
  4. 由于水平有限,文件系統的一些細節被我跳過了,sk 中的大多數成員變量的作用,也被我跳出過了.
    呵呵,還好,終于還是把這塊流程給初步分析出來了.另外當時寫的時候,沒有想到會寫這么長,
    大大超出了每貼的字限制。所以,每個小節內容跟標題可能會有點對不上號。

posted on 2013-02-16 21:51 大龍 閱讀(1206) 評論(1)  編輯 收藏 引用

評論

# re: Linux TCP/IP協議棧之Socket的實現分析(一 套接字的創建) --- 轉 2013-08-09 15:31 wtd321@163.com

sadsd  回復  更多評論   


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            91久久精品国产91久久| 欧美三区免费完整视频在线观看| 午夜精品久久久久久久| 亚洲福利专区| 欧美有码视频| 欧美一区二区三区在线看| 欧美日韩成人| 亚洲成人资源网| 国产精品亚洲综合久久| 亚洲美女色禁图| 日韩视频中文| 欧美大片在线观看一区| 女同一区二区| 一区二区三区在线视频观看| 亚洲欧美日韩另类| 午夜精品久久久久久| 欧美三级电影大全| 日韩视频永久免费观看| 日韩午夜中文字幕| 欧美激情2020午夜免费观看| 亚洲国产高清一区| 亚洲精品一区在线观看香蕉| 欧美激情亚洲激情| 亚洲国产精品久久久久秋霞不卡 | 亚洲电影激情视频网站| 黑人巨大精品欧美黑白配亚洲| 久久久久久久久蜜桃| 国产欧美综合在线| 午夜亚洲伦理| 老妇喷水一区二区三区| 禁久久精品乱码| 久久久久国产一区二区| 你懂的视频欧美| 亚洲高清免费视频| 欧美成人一区二区| 亚洲精品专区| 亚洲一区精彩视频| 国产欧美日韩在线播放| 午夜久久一区| 蜜月aⅴ免费一区二区三区 | 欧美午夜精品久久久久久人妖| 香蕉免费一区二区三区在线观看| 欧美在线亚洲综合一区| 久久伊人一区二区| 亚洲国产一区在线观看| 欧美日韩精品一区二区在线播放| 久久裸体艺术| 亚洲国产一区二区三区青草影视 | 午夜激情综合网| 国产日韩欧美自拍| 狂野欧美激情性xxxx| 欧美国产日产韩国视频| 日韩一区二区高清| 国产精品在线看| 久久国产精品一区二区三区四区 | 欧美一区二区视频网站| 久久免费少妇高潮久久精品99| 欧美午夜剧场| 久久精品中文字幕一区| 91久久久久久国产精品| 亚洲一区国产精品| 影音先锋在线一区| 欧美日韩视频专区在线播放 | 欧美日韩一区二区在线播放| 国产精品99久久久久久宅男| 久久久久www| 夜夜嗨av一区二区三区中文字幕 | 国产一区二区三区四区在线观看| avtt综合网| 老司机免费视频一区二区三区| 国产精品久久网| 久久久久久亚洲精品中文字幕| 欧美亚洲免费| 亚洲伦理在线免费看| 国产一区二区成人| 欧美视频日韩视频| 欧美成人免费网站| 久久国产精品99国产| 一本色道**综合亚洲精品蜜桃冫| 99精品视频一区| 日韩视频免费| 麻豆国产精品777777在线| 欧美 日韩 国产一区二区在线视频 | 欧美日韩播放| 久久久久久黄| 亚洲视频电影在线| 亚洲人成毛片在线播放女女| 麻豆国产va免费精品高清在线| 黄色一区二区三区四区| 国产精品成人一区二区三区吃奶| 日韩西西人体444www| 欧美成人69av| 久久综合电影一区| 欧美综合国产| 亚洲一区视频在线| 在线亚洲欧美视频| 妖精成人www高清在线观看| 亚洲国产精品久久人人爱蜜臀| 久久影院午夜片一区| 欧美专区在线观看| 午夜精品久久久久久久久| 亚洲视频观看| 亚洲一区二区三区欧美| 亚洲一区精品电影| 中文精品一区二区三区| 在线亚洲一区观看| 夜夜爽www精品| 亚洲午夜激情| 亚洲欧美日韩一区二区在线 | 欧美不卡在线视频| 久久经典综合| 久久久之久亚州精品露出| 亚洲欧美综合精品久久成人 | 亚洲黄色高清| 最新国产成人av网站网址麻豆| 亚洲综合国产激情另类一区| 一区二区欧美在线观看| 一本一本久久a久久精品综合麻豆| 国产精品人人做人人爽人人添| 欧美中文在线观看| 久久青草久久| 欧美精品一区二区三区视频| 欧美日韩黄视频| 国产精品你懂的在线| 国产日韩在线亚洲字幕中文| 国内外成人在线| 亚洲第一区中文99精品| 99v久久综合狠狠综合久久| 一区二区日韩精品| 欧美在线啊v| 奶水喷射视频一区| 91久久精品久久国产性色也91| 久久视频精品在线| 亚洲成人在线视频播放 | 欧美激情片在线观看| 欧美精品一区在线播放| 欧美日韩一二三区| 国产一区二区在线观看免费播放 | 亚洲国产另类精品专区| 99国产精品99久久久久久| 亚洲欧美日韩天堂| 免费一区二区三区| 日韩午夜av电影| 久久成人亚洲| 欧美日韩亚洲国产一区| 国产精品羞羞答答| 亚洲国产精品视频| 亚洲视频欧美在线| 久久久久久久久久久久久女国产乱 | 亚洲三级性片| 欧美一区中文字幕| 亚洲国产免费看| 亚洲欧美日韩系列| 欧美精品一区三区在线观看| 国产日韩三区| 一区二区三区免费看| 久久久久久久久久久久久女国产乱| 亚洲女人天堂av| 欧美福利电影在线观看| 亚洲国产精品嫩草影院| 亚洲免费视频观看| 欧美日韩国产va另类| 影音先锋另类| 久久精品人人做人人综合| 日韩亚洲欧美成人| 欧美大片免费| 在线看不卡av| 久久精品国产清高在天天线 | 欧美风情在线观看| 亚洲欧美日韩一区二区三区在线| 亚洲欧美成人精品| 欧美日韩一区二区三区在线视频 | 欧美一区二区在线看| 亚洲日本电影| 毛片基地黄久久久久久天堂| 国产精品大片| 99re8这里有精品热视频免费| 在线视频精品一区| 亚洲高清一区二区三区| 久久女同互慰一区二区三区| 国产精品视频导航| 亚洲一区二区免费| 亚洲精品黄网在线观看| 免费一级欧美片在线播放| 在线观看欧美激情| 久久亚洲国产精品日日av夜夜| 欧美激情a∨在线视频播放| 午夜精品999| 国产亚洲精品一区二区| 久久av老司机精品网站导航| 亚洲一区国产精品| 国产精品视频网| 欧美一区2区三区4区公司二百| 欧美影院精品一区| 欧美亚洲日本国产| 国产在线精品成人一区二区三区| 亚洲精美视频| 亚洲欧洲精品一区二区三区| 欧美国产一区在线| 一区二区三区精品|