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) { |