深度探索Linux内核之自动分配UDP本地端口

来源:互联网 发布:nginx 常用第三方模块 编辑:程序博客网 时间:2024/05/16 09:04
       当建立一个UDP的socket用于网络通讯时,我们需要先为这个socket绑定一个本地端口号。因为端口在一台主机上是用于标识进程的,如果没有端口号,当收到来自对端主机的报文时,就不知道应该由哪一个进程来接收这个报文。但有时,我们建立UDP的sokcet以后,并不调用bind进行端口绑定,也能正常工作。这是因为协议栈对于没有进行端口绑定的socket进行了自动绑定。
        在SOCK_DGRAM类型的套接字的操作函数集的sendmsg成员函数中,每次调用对应的第4层协议的sendmsg成员函数时,都会进行端口号的检查,如果没有绑定就调用协议的成员函数get_port进行自动绑定。代表INET域网络层套接字的结构体struct inet_sock有两个端口号相关的成员__u16 num和__u16 sport。它们都代表套接字的本地端口号。num是主机字节序,sport是网络字节序。当套接字类型为SOCK_RAW时,它们代表的是协议号(icmp,igmp等)。套接字层的sendmsg检查端口号绑定时,就是查看num是否为零。
        udp协议提供udp_v4_get_port函数用于自动获取本地端口号。端口号有一个固定的数值范围,自动获取必须在这个范围内进行。数组int sysctl_local_port_range[2]指定了本地端口号的范围。其默认值为1024到4999。对于高可用性系统,它的值应该是32768到61000(在TCP协议进行初始化时,会进行这项设置)。可通过修改文件/proc/sys/net/ipv4/ip_local_port_range的内容来修改这个范围。
        udp_hash是一个list数组,总共有128项,所有在协议栈中建立的udp socket全部以本地端口号为关键字被放入这个哈希数组中,全局变量udp_port_rover记录了最近一次被分配的端口号。寻找一个新的可用的端口,总是从udp_port_rover开始找,检查udp_hash[udp_port_rover & (UDP_HTABLE_SIZE - 1)]的list是否为空,如果为空,则取udp_port_rover为新的端口,如果不为空,则记录下这个list的size,同时保存下该端口号,然后遍历整个数组,找到size最小的一个list,取对应的端口号为我们所要获得的端口。然后,检查这个新获得的端口号是否已经被使用(同样,通过检查udp_hash实现)。如果已在使用中,则把端口号加上UDP_HTABLE_SIZE(128)再检查。直至获得未使用的端口号。