网络编程常用接口的内核实现----sys_bind()(二)

2014-11-24 07:52:31 · 作者: · 浏览: 2
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;
}