/* * 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). */ //bind函数对应的BSD层函数,用于绑定一个本地地址,服务器端 //umyaddr表示需要绑定的地址结构,addrlen表示改地址结构的长度 //这里的fd,即为套接字描述符 static int sock_bind(int fd, struct sockaddr *umyaddr, int addrlen) { struct socket *sock; int i; char address[MAX_SOCK_ADDR]; int err; //套接字参数有效性检查 if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) return(-EBADF); //获取fd对应的socket结构 if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK); //将地址从用户缓冲区复制到内核缓冲区,umyaddr->address if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0) return err; //转调用bind指向的函数,下层函数(inet_bind) if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0) { return(i); } return(0); }
/* this needs to be changed to disallow the rebinding of sockets. What error should it return? */ //完成本地地址绑定,本地地址绑定包括IP地址和端口号两个部分 static int inet_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) { struct sockaddr_in *addr=(struct sockaddr_in *)uaddr; struct sock *sk=(struct sock *)sock->data, *sk2; unsigned short snum = 0 /* Stoopid compiler.. this IS ok */; int chk_addr_ret; /* check this error. */ //在进行地址绑定时,该套接字应该处于关闭状态 if (sk->state != TCP_CLOSE) return(-EIO); //地址长度字段校验 if(addr_len<sizeof(struct sockaddr_in)) return -EINVAL; //非原始套接字类型,绑定前,没有端口号,则绑定端口号 if(sock->type != SOCK_RAW) { if (sk->num != 0)//从inet_create函数可以看出,非原始套接字类型,端口号是初始化为0的 return(-EINVAL); snum = ntohs(addr->sin_port);//将地址结构中的端口号转为主机字节顺序 /* * We can't just leave the socket bound wherever it is, it might * be bound to a privileged port. However, since there seems to * be a bug here, we will leave it if the port is not privileged. */ //如果端口号为0,则自动分配一个 if (snum == 0) { snum = get_new_socknum(sk->prot, 0);//得到一个新的端口号 } //端口号有效性检验,1024以上,超级用户权限 if (snum < PROT_SOCK && !suser()) return(-EACCES); } //下面则进行ip地址绑定 //检查地址是否是一个本地接口地址 chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr); //如果指定的地址不是本地地址,并且也不是一个多播地址,则错误返回 if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST) return(-EADDRNOTAVAIL); /* Source address MUST be ours! */ //如果没有指定地址,则系统自动分配一个本地地址 if (chk_addr_ret || addr->sin_addr.s_addr == 0) sk->saddr = addr->sin_addr.s_addr;//本地地址绑定 if(sock->type != SOCK_RAW) { /* Make sure we are allowed to bind here. */ cli(); //for循环主要是检查检查有无冲突的端口号以及本地地址,有冲突,但不允许地址复用,肯定错误退出 //成功跳出for循环时,已经定位到了哈希表sock_array指定索引的链表的末端 for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)]; sk2 != NULL; sk2 = sk2->next) { /* should be below! */ if (sk2->num != snum) //没有重复,继续搜索下一个 continue;//除非有重复,否则后面的代码将不会被执行 if (!sk->reuse)//端口号重复,如果没有设置地址复用标志,退出 { sti(); return(-EADDRINUSE); } if (sk2->num != snum) continue; /* more than one */ if (sk2->saddr != sk->saddr) //地址和端口一个意思 continue; /* socket per slot ! -FB */ //如果状态是LISTEN表明该套接字是一个服务端,服务端不可使用地址复用选项 if (!sk2->reuse || sk2->state==TCP_LISTEN) { sti(); return(-EADDRINUSE); } } sti(); remove_sock(sk);//将sk sock结构从其之前的表中删除,inet_create中 put_sock,这里remove_sock put_sock(snum, sk);//然后根据新分配的端口号插入到新的表中。可以得知系统在维护许多这样的表 sk->dummy_th.source = ntohs(sk->num);//tcp首部,源端口号绑定 sk->daddr = 0;//sock结构所代表套接字的远端地址 sk->dummy_th.dest = 0;//tcp首部,目的端口号 } return(0); }