本文主要内容是 sys_bind()的调用过程,比较清晰。 操作 SYS_BIND 为已创建的插口指定一个地址,是由 sys_bind()实现的,其整体调用过程如下图所示: 其中回调函数inet_bind代码如下:
/* IPV4的bind回调 */ 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 (sk->sk_prot->bind) {/* 如果传输层接口上实现了bind调用,则回调它。目前只有SOCK_RAW类型的传输层实现了该接口raw_bind */ err = sk->sk_prot->bind(sk, uaddr, addr_len); goto out; } err = -EINVAL; if (addr_len < sizeof(struct sockaddr_in))/* 参数合法性检查 */ goto out; … err = -EADDRNOTAVAIL; if (!sysctl_ip_nonlocal_bind &&/* 必须绑定到本地接口的地址 */ !inet->freebind && addr->sin_addr.s_addr != 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,则需要检查应用程序是否有相应的权限 */ if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) goto out; lock_sock(sk);/* 对套接口进行加锁,因为后面要对其状态进行判断 */ /* Check these errors (active socket, double bind). */ err = -EINVAL; /** * 如果状态不为CLOSE,表示套接口已经处于活动状态,不能再绑定 * 或者已经指定了本地端口号,也不能再绑定 */ 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 */ /* 调用传输层的get_port来进行地址绑定。如tcp_v4_get_port或udp_v4_get_port */ if (sk->sk_prot->get_port(sk, snum)) { … } /* 设置标志,表示已经绑定了本地地址和端口 */ if (inet->rcv_saddr) sk->sk_userlocks |= SOCK_BINDADDR_LOCK; if (snum) sk->sk_userlocks |= SOCK_BINDPORT_LOCK; inet->sport = htons(inet->num); /* 还没有连接到对方,清除远端地址和端口 */ inet->daddr = 0; inet->dport = 0; /* 清除路由缓存 */ sk_dst_reset(sk); err = 0; out_release_sock: release_sock(sk); out: return err; }当中一些重要的数据结构如下: 上述数据结构在sys_socket()中已经涉及,以下为新出现的数据结构:
