zoukankan      html  css  js  c++  java
  • 套接字之bind系统调用

    在socket创建成功之后,调用bind函数以完成对指定地址和端口的绑定工作;

    下面详细分析bind相关代码;

     1 /*
     2  *    Bind a name to a socket. Nothing much to do here since it's
     3  *    the protocol's responsibility to handle the local address.
     4  *
     5  *    We move the socket address to kernel space before we call
     6  *    the protocol layer (having also checked the address is ok).
     7  */
     8 
     9 SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
    10 {
    11     struct socket *sock;
    12     struct sockaddr_storage address;
    13     int err, fput_needed;
    14 
    15     /* 获取socket ,fput_need标识是否需要减少文件引用计数*/
    16     sock = sockfd_lookup_light(fd, &err, &fput_needed);
    17     if (sock) {
    18         /* 将用户空间地址复制到内核空间 */
    19         err = move_addr_to_kernel(umyaddr, addrlen, &address);
    20         if (err >= 0) {
    21             /* 安全模块的bind检查 */
    22             err = security_socket_bind(sock,
    23                            (struct sockaddr *)&address,
    24                            addrlen);
    25             if (!err)
    26                 /* 调用socket的bind操作 */
    27                 err = sock->ops->bind(sock,
    28                               (struct sockaddr *)
    29                               &address, addrlen);
    30         }
    31 
    32         /* 根据fput_needed决定是否减少引用计数 */
    33         fput_light(sock->file, fput_needed);
    34     }
    35     return err;
    36 }

    首先根据传入的socket描述符来获取socket结构;

     1 static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed)
     2 {
     3     /* 获取fd结构 */
     4     struct fd f = fdget(fd);
     5     struct socket *sock;
     6 
     7     *err = -EBADF;
     8     if (f.file) {
     9         /* 从文件的私有数据中获取socket */
    10         sock = sock_from_file(f.file, err);
    11         if (likely(sock)) {
    12             /* 设置是否需要减少引用计数的标志 */
    13             *fput_needed = f.flags;
    14             return sock;
    15         }
    16         fdput(f);
    17     }
    18     return NULL;
    19 }

    之后,将用户空间的地址拷贝到内核空间;

     1 /*
     2  * Support routines.
     3  * Move socket addresses back and forth across the kernel/user
     4  * divide and look after the messy bits.
     5  */
     6 
     7 /**
     8  *    move_addr_to_kernel    -    copy a socket address into kernel space
     9  *    @uaddr: Address in user space
    10  *    @kaddr: Address in kernel space
    11  *    @ulen: Length in user space
    12  *
    13  *    The address is copied into kernel space. If the provided address is
    14  *    too long an error code of -EINVAL is returned. If the copy gives
    15  *    invalid addresses -EFAULT is returned. On a success 0 is returned.
    16  */
    17 
    18 /* 复制socket地址到内核空间 */
    19 int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr)
    20 {
    21     /* 长度检查 */
    22     if (ulen < 0 || ulen > sizeof(struct sockaddr_storage))
    23         return -EINVAL;
    24     if (ulen == 0)
    25         return 0;
    26 
    27     /* 从用户空间拷贝数据 */
    28     if (copy_from_user(kaddr, uaddr, ulen))
    29         return -EFAULT;
    30 
    31     /* 审计信息 */
    32     return audit_sockaddr(ulen, kaddr);
    33 }

    最后,来看最核心的函数,sock->ops->bind调用inet_bind(为什么? 具体可以参考本博套接字调用关系那片文章),inet_bind将会进行一些列检查之后,调用传输层的sk->sk_prot->get_port函数来执行更详细的绑定工作,比如tcp会调用inet_csk_get_port函数;

    /* 地址绑定 */
    int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
    {
        struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
        struct sock *sk = sock->sk;
        struct inet_sock *inet = inet_sk(sk);
        struct net *net = sock_net(sk);
        unsigned short snum;
        int chk_addr_ret;
        u32 tb_id = RT_TABLE_LOCAL;
        int err;
    
        /* If the socket has its own bind function then use it. (RAW) */
        /* 
            如果传输控制块有自己的bind操作则调用,
            目前只有raw实现了自己的bind 
        */
        if (sk->sk_prot->bind) {
            err = sk->sk_prot->bind(sk, uaddr, addr_len);
            goto out;
        }
        
        err = -EINVAL;
        /* 地址长度错误 */
        if (addr_len < sizeof(struct sockaddr_in))
            goto out;
    
        /* 如果不是AF_INET协议族 */
        if (addr->sin_family != AF_INET) {
            /* Compatibility games : accept AF_UNSPEC (mapped to AF_INET)
             * only if s_addr is INADDR_ANY.
             */
            err = -EAFNOSUPPORT;
    
            /* 接受AF_UNSPEC && s_addr=htonl(INADDR_ANY)的情况 */
            if (addr->sin_family != AF_UNSPEC ||
                addr->sin_addr.s_addr != htonl(INADDR_ANY))
                goto out;
        }
    
        tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id;
        chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id);
    
        /* Not specified by any standard per-se, however it breaks too
         * many applications when removed.  It is unfortunate since
         * allowing applications to make a non-local bind solves
         * several problems with systems using dynamic addressing.
         * (ie. your servers still start up even if your ISDN link
         *  is temporarily down)
         */
        err = -EADDRNOTAVAIL;
    
        /* 合法性检查 */
        if (!net->ipv4.sysctl_ip_nonlocal_bind &&
            !(inet->freebind || inet->transparent) &&
            addr->sin_addr.s_addr != htonl(INADDR_ANY) &&
            chk_addr_ret != RTN_LOCAL &&
            chk_addr_ret != RTN_MULTICAST &&
            chk_addr_ret != RTN_BROADCAST)
            goto out;
    
        /* 源端口 */
        snum = ntohs(addr->sin_port);
        err = -EACCES;
    
        /* 绑定特权端口的权限检查 */
        if (snum && snum < inet_prot_sock(net) &&
            !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
            goto out;
    
        /*      We keep a pair of addresses. rcv_saddr is the one
         *      used by hash lookups, and saddr is used for transmit.
         *
         *      In the BSD API these are the same except where it
         *      would be illegal to use them (multicast/broadcast) in
         *      which case the sending device address is used.
         */
        lock_sock(sk);
    
        /* Check these errors (active socket, double bind). */
        err = -EINVAL;
    
        /* 传输控制块的状态不是CLOSE || 存在本地端口 */
        if (sk->sk_state != TCP_CLOSE || inet->inet_num)
            goto out_release_sock;
    
        /* 设置源地址rcv_addr用作hash查找,saddr用作传输 */
        inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
    
        /* 组播或者广播,使用设备地址 */
        if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
            inet->inet_saddr = 0;  /* Use device */
    
        /* Make sure we are allowed to bind here. */
    
        /* 
            端口不为0,或者端口为0允许绑定 
            则使用协议的具体获取端口函数绑定端口
        */
        if ((snum || !inet->bind_address_no_port) &&
            sk->sk_prot->get_port(sk, snum)) {
    
            /* 绑定失败 */
            inet->inet_saddr = inet->inet_rcv_saddr = 0;
    
            /* 端口在使用中 */
            err = -EADDRINUSE;
            goto out_release_sock;
        }
    
        /* 传输控制块已经绑定本地地址或端口标志 */
        if (inet->inet_rcv_saddr)
            sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
        if (snum)
            sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
    
        /* 设置源端口 */
        inet->inet_sport = htons(inet->inet_num);
    
        /* 设置目的地址和端口默认值 */
        inet->inet_daddr = 0;
        inet->inet_dport = 0;
    
        /* 设置路由默认值 */
        sk_dst_reset(sk);
        err = 0;
    out_release_sock:
        release_sock(sk);
    out:
        return err;
    }
    EXPORT_SYMBOL(inet_bind);

    TCP bind()系统调用实现部分,请阅读<TCP层bind系统调用的实现分析>

  • 相关阅读:
    Sum Root to Leaf Numbers 解答
    459. Repeated Substring Pattern
    71. Simplify Path
    89. Gray Code
    73. Set Matrix Zeroes
    297. Serialize and Deserialize Binary Tree
    449. Serialize and Deserialize BST
    451. Sort Characters By Frequency
    165. Compare Version Numbers
    447. Number of Boomerangs
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/7618371.html
Copyright © 2011-2022 走看看