aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/inet_hashtables.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/inet_hashtables.c')
-rw-r--r--net/ipv4/inet_hashtables.c28
1 files changed, 22 insertions, 6 deletions
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index fa3ae8148710..0ce0595d9861 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -39,6 +39,7 @@ struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep,
39 write_pnet(&tb->ib_net, hold_net(net)); 39 write_pnet(&tb->ib_net, hold_net(net));
40 tb->port = snum; 40 tb->port = snum;
41 tb->fastreuse = 0; 41 tb->fastreuse = 0;
42 tb->fastreuseport = 0;
42 tb->num_owners = 0; 43 tb->num_owners = 0;
43 INIT_HLIST_HEAD(&tb->owners); 44 INIT_HLIST_HEAD(&tb->owners);
44 hlist_add_head(&tb->node, &head->chain); 45 hlist_add_head(&tb->node, &head->chain);
@@ -151,16 +152,16 @@ static inline int compute_score(struct sock *sk, struct net *net,
151 if (net_eq(sock_net(sk), net) && inet->inet_num == hnum && 152 if (net_eq(sock_net(sk), net) && inet->inet_num == hnum &&
152 !ipv6_only_sock(sk)) { 153 !ipv6_only_sock(sk)) {
153 __be32 rcv_saddr = inet->inet_rcv_saddr; 154 __be32 rcv_saddr = inet->inet_rcv_saddr;
154 score = sk->sk_family == PF_INET ? 1 : 0; 155 score = sk->sk_family == PF_INET ? 2 : 1;
155 if (rcv_saddr) { 156 if (rcv_saddr) {
156 if (rcv_saddr != daddr) 157 if (rcv_saddr != daddr)
157 return -1; 158 return -1;
158 score += 2; 159 score += 4;
159 } 160 }
160 if (sk->sk_bound_dev_if) { 161 if (sk->sk_bound_dev_if) {
161 if (sk->sk_bound_dev_if != dif) 162 if (sk->sk_bound_dev_if != dif)
162 return -1; 163 return -1;
163 score += 2; 164 score += 4;
164 } 165 }
165 } 166 }
166 return score; 167 return score;
@@ -176,6 +177,7 @@ static inline int compute_score(struct sock *sk, struct net *net,
176 177
177struct sock *__inet_lookup_listener(struct net *net, 178struct sock *__inet_lookup_listener(struct net *net,
178 struct inet_hashinfo *hashinfo, 179 struct inet_hashinfo *hashinfo,
180 const __be32 saddr, __be16 sport,
179 const __be32 daddr, const unsigned short hnum, 181 const __be32 daddr, const unsigned short hnum,
180 const int dif) 182 const int dif)
181{ 183{
@@ -183,17 +185,29 @@ struct sock *__inet_lookup_listener(struct net *net,
183 struct hlist_nulls_node *node; 185 struct hlist_nulls_node *node;
184 unsigned int hash = inet_lhashfn(net, hnum); 186 unsigned int hash = inet_lhashfn(net, hnum);
185 struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; 187 struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
186 int score, hiscore; 188 int score, hiscore, matches = 0, reuseport = 0;
189 u32 phash = 0;
187 190
188 rcu_read_lock(); 191 rcu_read_lock();
189begin: 192begin:
190 result = NULL; 193 result = NULL;
191 hiscore = -1; 194 hiscore = 0;
192 sk_nulls_for_each_rcu(sk, node, &ilb->head) { 195 sk_nulls_for_each_rcu(sk, node, &ilb->head) {
193 score = compute_score(sk, net, hnum, daddr, dif); 196 score = compute_score(sk, net, hnum, daddr, dif);
194 if (score > hiscore) { 197 if (score > hiscore) {
195 result = sk; 198 result = sk;
196 hiscore = score; 199 hiscore = score;
200 reuseport = sk->sk_reuseport;
201 if (reuseport) {
202 phash = inet_ehashfn(net, daddr, hnum,
203 saddr, sport);
204 matches = 1;
205 }
206 } else if (score == hiscore && reuseport) {
207 matches++;
208 if (((u64)phash * matches) >> 32 == 0)
209 result = sk;
210 phash = next_pseudo_random32(phash);
197 } 211 }
198 } 212 }
199 /* 213 /*
@@ -501,7 +515,8 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
501 inet_bind_bucket_for_each(tb, node, &head->chain) { 515 inet_bind_bucket_for_each(tb, node, &head->chain) {
502 if (net_eq(ib_net(tb), net) && 516 if (net_eq(ib_net(tb), net) &&
503 tb->port == port) { 517 tb->port == port) {
504 if (tb->fastreuse >= 0) 518 if (tb->fastreuse >= 0 ||
519 tb->fastreuseport >= 0)
505 goto next_port; 520 goto next_port;
506 WARN_ON(hlist_empty(&tb->owners)); 521 WARN_ON(hlist_empty(&tb->owners));
507 if (!check_established(death_row, sk, 522 if (!check_established(death_row, sk,
@@ -518,6 +533,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
518 break; 533 break;
519 } 534 }
520 tb->fastreuse = -1; 535 tb->fastreuse = -1;
536 tb->fastreuseport = -1;
521 goto ok; 537 goto ok;
522 538
523 next_port: 539 next_port: