SO_REUSEADDR socketA socketB Result--------------------------------------------------------------------- ON/OFF 192.168.0.1:21 192.168.0.1:21 Error (EADDRINUSE) ON/OFF 192.168.0.1:21 10.0.0.1:21 OK ON/OFF 10.0.0.1:21 192.168.0.1:21 OK OFF 0.0.0.0:21 192.168.1.0:21 Error (EADDRINUSE) OFF 192.168.1.0:21 0.0.0.0:21 Error (EADDRINUSE) ON 0.0.0.0:21 192.168.1.0:21 OK ON 192.168.1.0:21 0.0.0.0:21 OK ON/OFF 0.0.0.0:21 0.0.0.0:21 Error (EADDRINUSE)
struct sock_common { /* omitted */ unsigned char skc_reuse; /* omitted */}int sock_setsockopt(struct socket *sock, int level, int optname,...{ ...... case SO_REUSEADDR: sk->sk_reuse = (valbool ? SK_CAN_REUSE : SK_NO_REUSE); break;}
inet_bind_bucket表示一个绑定的端口。
struct inet_bind_bucket { /* omitted */ unsigned short port; signed short fastreuse; int num_owners; struct hlist_node node; struct hlist_head owners;};
struct sock_common { unsigned short skc_family; volatile unsigned char skc_state;- unsigned char skc_reuse;+ unsigned char skc_reuse:4;+ unsigned char skc_reuseport:4;@@ int sock_setsockopt(struct socket *sock, int level, int optname, case SO_REUSEADDR: sk->sk_reuse = (valbool ? SK_CAN_REUSE : SK_NO_REUSE); break;+ case SO_REUSEPORT:+ sk->sk_reuseport = valbool;+ break;
然后对inet_bind_bucket也相应进行了扩展
struct inet_bind_bucket { /* omitted */ unsigned short port;- signed short fastreuse;+ signed char fastreuse;+ signed char fastreuseport;+ kuid_t fastuid;
/* inet_hashtables.c */struct sock *__inet_lookup_listener(struct......){ struct sock *sk, *result; unsigned int hash = inet_lhashfn(net, hnum); struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; // 根据本地端口找到hash冲突链 /* code omitted */ result = NULL; hiscore = 0; sk_nulls_for_each_rcu(sk, node, &ilb->head) { score = compute_score(sk, net, hnum, daddr, dif); // 根据匹配程度进行打分 if (score > hiscore) { result = sk; hiscore = score; reuseport = sk->sk_reuseport; if (reuseport) { phash = inet_ehashfn(net, daddr, hnum, saddr, sport); matches = 1; // 如果是reuseport 则累计多少个socket满足 } } elseif (score == hiscore && reuseport) { matches++; if (reciprocal_scale(phash, matches) == 0) result = sk; phash = next_pseudo_random32(phash); } } /* * if the nulls value we got at the end of this lookup is * not the expected one, we must restart lookup. * We probably met an item that was moved to another chain. */ return result;}