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);
unsigned short snum;
int chk_addr_ret;
int err;
/* If the socket has its own bind function then use it. (RAW) */
/*
* 如果是TCP套接字,sk->sk_prot指向的是tcp_prot,在
* inet_create()中调用的sk_alloc()函数中初始化。由于
* tcp_prot中没有设置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;
/*
* 判断传入的地址类型。
*/
chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
/* 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;
/*
* 如果系统不支持绑定本地地址,或者
* 传入的地址类型有误,则返回EADDRNOTAVAIL
* 错误。
*/
if (!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;
/*
* 如果绑定的端口号小于1024(保留端口号),但是
* 当前用户没有CAP_NET_BIND_SERVICE权限,则返回EACCESS错误。
*/
if (snum && snum < PROT_SOCK && !capable(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;
/*
* 如果套接字状态不是TCP_CLOSE(套接字的初始状态,参见
* sock_init_data()函数),或者已经绑定过,则返回EINVAL错误。
*/
if (sk->sk_state != TCP_CLOSE || inet->num)
goto out_release_sock;
inet->rcv_saddr = inet->saddr = addr->sin_addr.s_addr;
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
inet->saddr = 0; /* Use device */
/* Make sure we are allowed to bind here. */
/*
* 这里实际调用的是inet_csk_get_port()函数。
* 检查要绑定的端口号是否已经使用,如果已经使用,
* 则检查是否允许复用。如果检查失败,则返回
* EADDRINUSE错误。
*/
if (sk->sk_prot->get_port(sk, snum)) {
inet->saddr = inet->rcv_saddr = 0;
err = -EADDRINUSE;
goto out_release_sock;
}
/*
* rcv_saddr存储的是已绑定的本地地址,接收数据时使用。
* 如果已绑定的地址不为0,则设置SOCK_BINDADDR_LOCK标志,
* 表示已绑定本地地址。
*/
if (inet->rcv_saddr)
sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
/*
* 如果绑定的端口号不为0,则设置SOCK_BINDPORT_LOCK标志,
* 表示已绑定本地端口号。
*/
if (snum)
sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
inet->sport = htons(inet->num);
inet->daddr = 0;
inet->dport = 0;
/*
* 重新初始化目的路由缓存项,如果之前已设置,则
* 调用dst_release()释放老的路由缓存项。
*/
sk_dst_reset(sk);
err = 0;
out_release_sock:
release_sock(sk);
out:
return err;
}