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 }

 

 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 }

 

bind系统调用函数实现的红色部分,最终会调用inet_bind;

  1 /* 地址绑定 */
  2 int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
  3 {
  4     struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
  5     struct sock *sk = sock->sk;
  6     struct inet_sock *inet = inet_sk(sk);
  7     struct net *net = sock_net(sk);
  8     unsigned short snum;
  9     int chk_addr_ret;
 10     u32 tb_id = RT_TABLE_LOCAL;
 11     int err;
 12 
 13     /* If the socket has its own bind function then use it. (RAW) */
 14     /* 
 15         如果传输控制块有自己的bind操作则调用,
 16         目前只有raw实现了自己的bind 
 17     */
 18     if (sk->sk_prot->bind) {
 19         err = sk->sk_prot->bind(sk, uaddr, addr_len);
 20         goto out;
 21     }
 22     
 23     err = -EINVAL;
 24     /* 地址长度错误 */
 25     if (addr_len < sizeof(struct sockaddr_in))
 26         goto out;
 27 
 28     /* 如果不是AF_INET协议族 */
 29     if (addr->sin_family != AF_INET) {
 30         /* Compatibility games : accept AF_UNSPEC (mapped to AF_INET)
 31          * only if s_addr is INADDR_ANY.
 32          */
 33         err = -EAFNOSUPPORT;
 34 
 35         /* 接受AF_UNSPEC && s_addr=htonl(INADDR_ANY)的情况 */
 36         if (addr->sin_family != AF_UNSPEC ||
 37             addr->sin_addr.s_addr != htonl(INADDR_ANY))
 38             goto out;
 39     }
 40 
 41     tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id;
 42     chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id);
 43 
 44     /* Not specified by any standard per-se, however it breaks too
 45      * many applications when removed.  It is unfortunate since
 46      * allowing applications to make a non-local bind solves
 47      * several problems with systems using dynamic addressing.
 48      * (ie. your servers still start up even if your ISDN link
 49      *  is temporarily down)
 50      */
 51     err = -EADDRNOTAVAIL;
 52 
 53     /* 合法性检查 */
 54     if (!net->ipv4.sysctl_ip_nonlocal_bind &&
 55         !(inet->freebind || inet->transparent) &&
 56         addr->sin_addr.s_addr != htonl(INADDR_ANY) &&
 57         chk_addr_ret != RTN_LOCAL &&
 58         chk_addr_ret != RTN_MULTICAST &&
 59         chk_addr_ret != RTN_BROADCAST)
 60         goto out;
 61 
 62     /* 源端口 */
 63     snum = ntohs(addr->sin_port);
 64     err = -EACCES;
 65 
 66     /* 绑定特权端口的权限检查 */
 67     if (snum && snum < inet_prot_sock(net) &&
 68         !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
 69         goto out;
 70 
 71     /*      We keep a pair of addresses. rcv_saddr is the one
 72      *      used by hash lookups, and saddr is used for transmit.
 73      *
 74      *      In the BSD API these are the same except where it
 75      *      would be illegal to use them (multicast/broadcast) in
 76      *      which case the sending device address is used.
 77      */
 78     lock_sock(sk);
 79 
 80     /* Check these errors (active socket, double bind). */
 81     err = -EINVAL;
 82 
 83     /* 传输控制块的状态不是CLOSE || 存在本地端口 */
 84     if (sk->sk_state != TCP_CLOSE || inet->inet_num)
 85         goto out_release_sock;
 86 
 87     /* 设置源地址rcv_addr用作hash查找,saddr用作传输 */
 88     inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
 89 
 90     /* 组播或者广播,使用设备地址 */
 91     if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
 92         inet->inet_saddr = 0;  /* Use device */
 93 
 94     /* Make sure we are allowed to bind here. */
 95 
 96     /* 
 97         端口不为0,或者端口为0允许绑定 
 98         则使用协议的具体获取端口函数绑定端口
 99     */
100     if ((snum || !inet->bind_address_no_port) &&
101         sk->sk_prot->get_port(sk, snum)) {
102 
103         /* 绑定失败 */
104         inet->inet_saddr = inet->inet_rcv_saddr = 0;
105 
106         /* 端口在使用中 */
107         err = -EADDRINUSE;
108         goto out_release_sock;
109     }
110 
111     /* 传输控制块已经绑定本地地址或端口标志 */
112     if (inet->inet_rcv_saddr)
113         sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
114     if (snum)
115         sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
116 
117     /* 设置源端口 */
118     inet->inet_sport = htons(inet->inet_num);
119 
120     /* 设置目的地址和端口默认值 */
121     inet->inet_daddr = 0;
122     inet->inet_dport = 0;
123 
124     /* 设置路由默认值 */
125     sk_dst_reset(sk);
126     err = 0;
127 out_release_sock:
128     release_sock(sk);
129 out:
130     return err;
131 }
132 EXPORT_SYMBOL(inet_bind);

 

get_port会调用具体协议的获取端口函数,比如tcp的inet_csk_get_port函数,具体流程会在阅读tcp代码时分析;

转载于:https://www.cnblogs.com/wanpengcoder/p/7618371.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值