/** * udp_lib_get_port - UDP/-Lite port lookup for IPv4 and IPv6 * * @sk: socket struct in question * @snum: port number to look up * @saddr_comp: AF-dependent comparison of bound local IP addresses */ int udp_lib_get_port(struct sock *sk, unsigned short snum, int (*saddr_comp)(const struct sock *sk1, const struct sock *sk2)) { struct udp_hslot *hslot; struct udp_table *udptable = sk->sk_prot->h.udp_table; /**/udp_table int error = 1; struct net *net = sock_net(sk);//得到inet_sock if (!snum) {//--------------------------------------------------如果是0,表示要系统分配 int low, high, remaining; unsigned rand; unsigned short first, last; DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN);//--------------65535/128-------定义一个数组 inet_get_local_port_range(&low, &high);//端口范围sysctl_local_ports remaining = (high - low) + 1; rand = net_random(); first = (((u64)rand * remaining) >> 32) + low;//得到一个随机的端口 /* * force rand to be an odd multiple of UDP_HTABLE_SIZE */ rand = (rand | 1) * UDP_HTABLE_SIZE;//rand最后处理成哈希表大小的奇数倍,因为奇数+奇数=偶数,而偶数+偶数=偶数,强制为奇数,可以保证链表中所有值被访问 for (last = first + UDP_HTABLE_SIZE; first != last; first++) { hslot = &udptable->hash[udp_hashfn(net, first)];//---------------->得到128项中的一项,hash公式可简写为num&mask。即本地端口对udptable大小取模 bitmap_zero(bitmap, PORTS_PER_CHAIN);//bitmap置0 spin_lock_bh(&hslot->lock); udp_lib_lport_inuse(net, snum, hslot, bitmap, sk, saddr_comp); snum = first; /* * Iterate on all possible values of snum for this hash. * Using steps of an odd multiple of UDP_HTABLE_SIZE * give us randomization and full range coverage. * *使用first值作为端口号,从udptable的hash表中找到hslot项,重置bitmap数组全0,调用函数udp_lib_lport_inuse()遍历hslot项的所有表项,将所有已经使用的sport对应于bitmap的位置置1。 */ do { if (low <= snum && snum <= high && !test_bit(snum / UDP_HTABLE_SIZE, bitmap)) goto found;//------------------------------------------------>如果对应的位没有置1,说明没有被使用,即找到了 snum += rand;//哈希表大小的奇数倍UDP_HTABLE_SIZE,这样还在这个链表中找 } while (snum != first); spin_unlock_bh(&hslot->lock); } goto fail; } else { hslot = &udptable->hash[udp_hashfn(net, snum)];//以本地端口号hash spin_lock_bh(&hslot->lock); if (udp_lib_lport_inuse(net, snum, hslot, NULL, sk, saddr_comp))//------------------------------------------------------>判断端口是否被占用 goto fail_unlock; } found: inet_sk(sk)->num = snum;//设置端口 sk->sk_hash = snum; if (sk_unhashed(sk)) { sk_nulls_add_node_rcu(sk, &hslot->head);//将sk添加到hash表中 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); } error = 0; fail_unlock: spin_unlock_bh(&hslot->lock); fail: return error; }