linux-kernel 3.5.3Tcp系统调用,源码分析6-bind系统调用

服务器端在调用socket系统调用后,初始化地址,就调用bind系统调用了,socket系统调用创建了一个socket_alloc,这个结构一个是inode,一个是socket,对这2个结构进行了初始化,尤其是初始化了socket结构中的sock结构。

bind函数最终调用的是

/*

 * Bind a name to a socket. Nothing much to do here since it's

 * the protocol's responsibility to handle the local address.

 * We move the socket address to kernel space before we call

 * the protocol layer (having also checked the address is ok).

 */

SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)

{

struct socket *sock;

struct sockaddr_storage address;

int err, fput_needed;

/*

根据fd,找到fd对应的socket结构,sockfd_lookup_light首先调用fget_light得到file,然后调用sock_from_file得到file对应的socket结构(return file->private_data;)

其中fget_light函数说明下,内核文件基本操作:

fget_light()和fput_light()(位于fs/file_table.c和include/linux/file.h)必须是成对出现的!fget_light在当前进程的struct files_struct中根据所谓的用户空间文件描述符fd来获取文件描述符。另外,根据当前fs_struct是否被多各进程共享来判断是否需要对文件描述符进行加锁,并将加锁结果存到一个int中返回, fput_light则根据该结果来判断是否需要对文件描述符解锁 引自http://blog.csdn.net/qq327662250/article/details/4570608

*/

sock = sockfd_lookup_light(fd, &err, &fput_needed);

if (sock) {

/*

move_addr_to_kernel主要调用copy_from_user将用户空间的数据拷贝到内核中, 关于copy_from_user,这是一个很著名的函数,这里有很详细的介绍http://blog.csdn.net/eroswang/article/details/3529750

*/

err = move_addr_to_kernel(umyaddr, addrlen, &address);

if (err >= 0) {

/*

里面调用security_ops->socket_bind来初始化,security_ops的初始化为default_security_ops, 而default_security_ops的初始化在函数security_fixup_ops中

set_to_cap_if_null(ops, socket_bind); 注册的函数为cap_socket_bind,默认什么都没做,如果需要进行安全检查,可以向security_ops注册自己的函数,它属于LSM模块

*/

err = security_socket_bind(sock,

  (struct sockaddr *)&address,

  addrlen);

if (!err)

/*

这里真正开始完成绑定过程,这个函数真正调用的是inet_bind

*/

err = sock->ops->bind(sock,

      (struct sockaddr *)

      &address, addrlen);

}

fput_light(sock->file, fput_needed);

}

return err;

}


辛苦了一大把,下面来看真正的bind过程

/*

设置inet_sock结构的下列域
    inet->rcv_saddr   本地接收地址
    inet->saddr       本地发送地址
    inet->sport       本地端口
    inet->daddr       目的地址(远程地址)
    inet->dport       目的端口(远程端口)

*/

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); //这里其实就是(inet_sock*)sk

/*

struct inet_sock {
/* sk and pinet6 has to be the first two members of inet_sock */
struct sock sk;
......
__be16 inet_dport;
__u16 inet_num;
__be32 inet_saddr;
__s16 uc_ttl;
__u16 cmsg_flags;
__be16 inet_sport;
__u16 inet_id;
......
freebind:1,
hdrincl:1,
mc_loop:1,
transparent:1,
mc_all:1,
nodefrag:1;
__u8 rcv_tos;
.....
};

*/
unsigned short snum;
int chk_addr_ret;
int err;

/*

只有SOCK_RAW定义了自己的bind函数

*/
/* If the socket has its own bind function then use it. (RAW) */
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;


if (addr->sin_family != AF_INET) {
/* Compatibility games : accept AF_UNSPEC (mapped to AF_INET)
* only if s_addr is INADDR_ANY.
*/
err = -EAFNOSUPPORT;
if (addr->sin_family != AF_UNSPEC ||
   addr->sin_addr.s_addr != htonl(INADDR_ANY))
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;
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;
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;
if (sk->sk_state != TCP_CLOSE || inet->inet_num)
goto out_release_sock;


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. */
if (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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值