|
@@ -158,25 +158,38 @@ static inline int compute_score(struct sock *sk, struct net *net,
|
|
|
}
|
|
|
|
|
|
struct sock *inet6_lookup_listener(struct net *net,
|
|
|
- struct inet_hashinfo *hashinfo, const struct in6_addr *daddr,
|
|
|
+ struct inet_hashinfo *hashinfo, const struct in6_addr *saddr,
|
|
|
+ const __be16 sport, const struct in6_addr *daddr,
|
|
|
const unsigned short hnum, const int dif)
|
|
|
{
|
|
|
struct sock *sk;
|
|
|
const struct hlist_nulls_node *node;
|
|
|
struct sock *result;
|
|
|
- int score, hiscore;
|
|
|
+ int score, hiscore, matches = 0, reuseport = 0;
|
|
|
+ u32 phash = 0;
|
|
|
unsigned int hash = inet_lhashfn(net, hnum);
|
|
|
struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
|
|
|
|
|
|
rcu_read_lock();
|
|
|
begin:
|
|
|
result = NULL;
|
|
|
- hiscore = -1;
|
|
|
+ hiscore = 0;
|
|
|
sk_nulls_for_each(sk, node, &ilb->head) {
|
|
|
score = compute_score(sk, net, hnum, daddr, dif);
|
|
|
if (score > hiscore) {
|
|
|
hiscore = score;
|
|
|
result = sk;
|
|
|
+ reuseport = sk->sk_reuseport;
|
|
|
+ if (reuseport) {
|
|
|
+ phash = inet6_ehashfn(net, daddr, hnum,
|
|
|
+ saddr, sport);
|
|
|
+ matches = 1;
|
|
|
+ }
|
|
|
+ } else if (score == hiscore && reuseport) {
|
|
|
+ matches++;
|
|
|
+ if (((u64)phash * matches) >> 32 == 0)
|
|
|
+ result = sk;
|
|
|
+ phash = next_pseudo_random32(phash);
|
|
|
}
|
|
|
}
|
|
|
/*
|