diff options
Diffstat (limited to 'net/ipv6/udp.c')
| -rw-r--r-- | net/ipv6/udp.c | 71 |
1 files changed, 16 insertions, 55 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4bb5c13777f1..0a71a312d0d8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
| @@ -115,11 +115,10 @@ static void udp_v6_rehash(struct sock *sk) | |||
| 115 | udp_lib_rehash(sk, new_hash); | 115 | udp_lib_rehash(sk, new_hash); |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | static inline int compute_score(struct sock *sk, struct net *net, | 118 | static int compute_score(struct sock *sk, struct net *net, |
| 119 | unsigned short hnum, | 119 | const struct in6_addr *saddr, __be16 sport, |
| 120 | const struct in6_addr *saddr, __be16 sport, | 120 | const struct in6_addr *daddr, unsigned short hnum, |
| 121 | const struct in6_addr *daddr, __be16 dport, | 121 | int dif) |
| 122 | int dif) | ||
| 123 | { | 122 | { |
| 124 | int score; | 123 | int score; |
| 125 | struct inet_sock *inet; | 124 | struct inet_sock *inet; |
| @@ -162,54 +161,11 @@ static inline int compute_score(struct sock *sk, struct net *net, | |||
| 162 | return score; | 161 | return score; |
| 163 | } | 162 | } |
| 164 | 163 | ||
| 165 | static inline int compute_score2(struct sock *sk, struct net *net, | 164 | /* called with rcu_read_lock() */ |
| 166 | const struct in6_addr *saddr, __be16 sport, | ||
| 167 | const struct in6_addr *daddr, | ||
| 168 | unsigned short hnum, int dif) | ||
| 169 | { | ||
| 170 | int score; | ||
| 171 | struct inet_sock *inet; | ||
| 172 | |||
| 173 | if (!net_eq(sock_net(sk), net) || | ||
| 174 | udp_sk(sk)->udp_port_hash != hnum || | ||
| 175 | sk->sk_family != PF_INET6) | ||
| 176 | return -1; | ||
| 177 | |||
| 178 | if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) | ||
| 179 | return -1; | ||
| 180 | |||
| 181 | score = 0; | ||
| 182 | inet = inet_sk(sk); | ||
| 183 | |||
| 184 | if (inet->inet_dport) { | ||
| 185 | if (inet->inet_dport != sport) | ||
| 186 | return -1; | ||
| 187 | score++; | ||
| 188 | } | ||
| 189 | |||
| 190 | if (!ipv6_addr_any(&sk->sk_v6_daddr)) { | ||
| 191 | if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr)) | ||
| 192 | return -1; | ||
| 193 | score++; | ||
| 194 | } | ||
| 195 | |||
| 196 | if (sk->sk_bound_dev_if) { | ||
| 197 | if (sk->sk_bound_dev_if != dif) | ||
| 198 | return -1; | ||
| 199 | score++; | ||
| 200 | } | ||
| 201 | |||
| 202 | if (sk->sk_incoming_cpu == raw_smp_processor_id()) | ||
| 203 | score++; | ||
| 204 | |||
| 205 | return score; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* called with read_rcu_lock() */ | ||
| 209 | static struct sock *udp6_lib_lookup2(struct net *net, | 165 | static struct sock *udp6_lib_lookup2(struct net *net, |
| 210 | const struct in6_addr *saddr, __be16 sport, | 166 | const struct in6_addr *saddr, __be16 sport, |
| 211 | const struct in6_addr *daddr, unsigned int hnum, int dif, | 167 | const struct in6_addr *daddr, unsigned int hnum, int dif, |
| 212 | struct udp_hslot *hslot2, unsigned int slot2, | 168 | struct udp_hslot *hslot2, |
| 213 | struct sk_buff *skb) | 169 | struct sk_buff *skb) |
| 214 | { | 170 | { |
| 215 | struct sock *sk, *result; | 171 | struct sock *sk, *result; |
| @@ -219,7 +175,7 @@ static struct sock *udp6_lib_lookup2(struct net *net, | |||
| 219 | result = NULL; | 175 | result = NULL; |
| 220 | badness = -1; | 176 | badness = -1; |
| 221 | udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { | 177 | udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { |
| 222 | score = compute_score2(sk, net, saddr, sport, | 178 | score = compute_score(sk, net, saddr, sport, |
| 223 | daddr, hnum, dif); | 179 | daddr, hnum, dif); |
| 224 | if (score > badness) { | 180 | if (score > badness) { |
| 225 | reuseport = sk->sk_reuseport; | 181 | reuseport = sk->sk_reuseport; |
| @@ -268,17 +224,22 @@ struct sock *__udp6_lib_lookup(struct net *net, | |||
| 268 | 224 | ||
| 269 | result = udp6_lib_lookup2(net, saddr, sport, | 225 | result = udp6_lib_lookup2(net, saddr, sport, |
| 270 | daddr, hnum, dif, | 226 | daddr, hnum, dif, |
| 271 | hslot2, slot2, skb); | 227 | hslot2, skb); |
| 272 | if (!result) { | 228 | if (!result) { |
| 229 | unsigned int old_slot2 = slot2; | ||
| 273 | hash2 = udp6_portaddr_hash(net, &in6addr_any, hnum); | 230 | hash2 = udp6_portaddr_hash(net, &in6addr_any, hnum); |
| 274 | slot2 = hash2 & udptable->mask; | 231 | slot2 = hash2 & udptable->mask; |
| 232 | /* avoid searching the same slot again. */ | ||
| 233 | if (unlikely(slot2 == old_slot2)) | ||
| 234 | return result; | ||
| 235 | |||
| 275 | hslot2 = &udptable->hash2[slot2]; | 236 | hslot2 = &udptable->hash2[slot2]; |
| 276 | if (hslot->count < hslot2->count) | 237 | if (hslot->count < hslot2->count) |
| 277 | goto begin; | 238 | goto begin; |
| 278 | 239 | ||
| 279 | result = udp6_lib_lookup2(net, saddr, sport, | 240 | result = udp6_lib_lookup2(net, saddr, sport, |
| 280 | &in6addr_any, hnum, dif, | 241 | daddr, hnum, dif, |
| 281 | hslot2, slot2, skb); | 242 | hslot2, skb); |
| 282 | } | 243 | } |
| 283 | return result; | 244 | return result; |
| 284 | } | 245 | } |
| @@ -286,7 +247,7 @@ begin: | |||
| 286 | result = NULL; | 247 | result = NULL; |
| 287 | badness = -1; | 248 | badness = -1; |
| 288 | sk_for_each_rcu(sk, &hslot->head) { | 249 | sk_for_each_rcu(sk, &hslot->head) { |
| 289 | score = compute_score(sk, net, hnum, saddr, sport, daddr, dport, dif); | 250 | score = compute_score(sk, net, saddr, sport, daddr, hnum, dif); |
| 290 | if (score > badness) { | 251 | if (score > badness) { |
| 291 | reuseport = sk->sk_reuseport; | 252 | reuseport = sk->sk_reuseport; |
| 292 | if (reuseport) { | 253 | if (reuseport) { |
