2020#include <linux/wait.h>
2121#include <linux/vmalloc.h>
2222
23+ #include <net/addrconf.h>
2324#include <net/inet_connection_sock.h>
2425#include <net/inet_hashtables.h>
2526#include <net/secure_seq.h>
2627#include <net/ip.h>
28+ #include <net/sock_reuseport.h>
2729
2830static u32 inet_ehashfn (const struct net * net , const __be32 laddr ,
2931 const __u16 lport , const __be32 faddr ,
@@ -215,6 +217,7 @@ struct sock *__inet_lookup_listener(struct net *net,
215217 unsigned int hash = inet_lhashfn (net , hnum );
216218 struct inet_listen_hashbucket * ilb = & hashinfo -> listening_hash [hash ];
217219 int score , hiscore , matches = 0 , reuseport = 0 ;
220+ bool select_ok = true;
218221 u32 phash = 0 ;
219222
220223 rcu_read_lock ();
@@ -230,6 +233,15 @@ struct sock *__inet_lookup_listener(struct net *net,
230233 if (reuseport ) {
231234 phash = inet_ehashfn (net , daddr , hnum ,
232235 saddr , sport );
236+ if (select_ok ) {
237+ struct sock * sk2 ;
238+ sk2 = reuseport_select_sock (sk , phash ,
239+ skb , doff );
240+ if (sk2 ) {
241+ result = sk2 ;
242+ goto found ;
243+ }
244+ }
233245 matches = 1 ;
234246 }
235247 } else if (score == hiscore && reuseport ) {
@@ -247,11 +259,13 @@ struct sock *__inet_lookup_listener(struct net *net,
247259 if (get_nulls_value (node ) != hash + LISTENING_NULLS_BASE )
248260 goto begin ;
249261 if (result ) {
262+ found :
250263 if (unlikely (!atomic_inc_not_zero (& result -> sk_refcnt )))
251264 result = NULL ;
252265 else if (unlikely (compute_score (result , net , hnum , daddr ,
253266 dif ) < hiscore )) {
254267 sock_put (result );
268+ select_ok = false;
255269 goto begin ;
256270 }
257271 }
@@ -450,34 +464,74 @@ bool inet_ehash_nolisten(struct sock *sk, struct sock *osk)
450464}
451465EXPORT_SYMBOL_GPL (inet_ehash_nolisten );
452466
453- void __inet_hash (struct sock * sk , struct sock * osk )
467+ static int inet_reuseport_add_sock (struct sock * sk ,
468+ struct inet_listen_hashbucket * ilb ,
469+ int (* saddr_same )(const struct sock * sk1 ,
470+ const struct sock * sk2 ,
471+ bool match_wildcard ))
472+ {
473+ struct sock * sk2 ;
474+ struct hlist_nulls_node * node ;
475+ kuid_t uid = sock_i_uid (sk );
476+
477+ sk_nulls_for_each_rcu (sk2 , node , & ilb -> head ) {
478+ if (sk2 != sk &&
479+ sk2 -> sk_family == sk -> sk_family &&
480+ ipv6_only_sock (sk2 ) == ipv6_only_sock (sk ) &&
481+ sk2 -> sk_bound_dev_if == sk -> sk_bound_dev_if &&
482+ sk2 -> sk_reuseport && uid_eq (uid , sock_i_uid (sk2 )) &&
483+ saddr_same (sk , sk2 , false))
484+ return reuseport_add_sock (sk , sk2 );
485+ }
486+
487+ /* Initial allocation may have already happened via setsockopt */
488+ if (!rcu_access_pointer (sk -> sk_reuseport_cb ))
489+ return reuseport_alloc (sk );
490+ return 0 ;
491+ }
492+
493+ int __inet_hash (struct sock * sk , struct sock * osk ,
494+ int (* saddr_same )(const struct sock * sk1 ,
495+ const struct sock * sk2 ,
496+ bool match_wildcard ))
454497{
455498 struct inet_hashinfo * hashinfo = sk -> sk_prot -> h .hashinfo ;
456499 struct inet_listen_hashbucket * ilb ;
500+ int err = 0 ;
457501
458502 if (sk -> sk_state != TCP_LISTEN ) {
459503 inet_ehash_nolisten (sk , osk );
460- return ;
504+ return 0 ;
461505 }
462506 WARN_ON (!sk_unhashed (sk ));
463507 ilb = & hashinfo -> listening_hash [inet_sk_listen_hashfn (sk )];
464508
465509 spin_lock (& ilb -> lock );
510+ if (sk -> sk_reuseport ) {
511+ err = inet_reuseport_add_sock (sk , ilb , saddr_same );
512+ if (err )
513+ goto unlock ;
514+ }
466515 __sk_nulls_add_node_rcu (sk , & ilb -> head );
467516 sock_prot_inuse_add (sock_net (sk ), sk -> sk_prot , 1 );
517+ unlock :
468518 spin_unlock (& ilb -> lock );
519+
520+ return err ;
469521}
470522EXPORT_SYMBOL (__inet_hash );
471523
472524int inet_hash (struct sock * sk )
473525{
526+ int err = 0 ;
527+
474528 if (sk -> sk_state != TCP_CLOSE ) {
475529 local_bh_disable ();
476- __inet_hash (sk , NULL );
530+ err = __inet_hash (sk , NULL , ipv4_rcv_saddr_equal );
477531 local_bh_enable ();
478532 }
479533
480- return 0 ;
534+ return err ;
481535}
482536EXPORT_SYMBOL_GPL (inet_hash );
483537
@@ -496,6 +550,8 @@ void inet_unhash(struct sock *sk)
496550 lock = inet_ehash_lockp (hashinfo , sk -> sk_hash );
497551
498552 spin_lock_bh (lock );
553+ if (rcu_access_pointer (sk -> sk_reuseport_cb ))
554+ reuseport_detach_sock (sk );
499555 done = __sk_nulls_del_node_init_rcu (sk );
500556 if (done )
501557 sock_prot_inuse_add (sock_net (sk ), sk -> sk_prot , -1 );
0 commit comments