diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/udp.c | 115 |
1 files changed, 112 insertions, 3 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 5f04216f35ce..dd7f3d20989a 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -298,6 +298,91 @@ static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr, | |||
298 | return score; | 298 | return score; |
299 | } | 299 | } |
300 | 300 | ||
301 | /* | ||
302 | * In this second variant, we check (daddr, dport) matches (inet_rcv_sadd, inet_num) | ||
303 | */ | ||
304 | #define SCORE2_MAX (1 + 2 + 2 + 2) | ||
305 | static inline int compute_score2(struct sock *sk, struct net *net, | ||
306 | __be32 saddr, __be16 sport, | ||
307 | __be32 daddr, unsigned int hnum, int dif) | ||
308 | { | ||
309 | int score = -1; | ||
310 | |||
311 | if (net_eq(sock_net(sk), net) && !ipv6_only_sock(sk)) { | ||
312 | struct inet_sock *inet = inet_sk(sk); | ||
313 | |||
314 | if (inet->inet_rcv_saddr != daddr) | ||
315 | return -1; | ||
316 | if (inet->inet_num != hnum) | ||
317 | return -1; | ||
318 | |||
319 | score = (sk->sk_family == PF_INET ? 1 : 0); | ||
320 | if (inet->inet_daddr) { | ||
321 | if (inet->inet_daddr != saddr) | ||
322 | return -1; | ||
323 | score += 2; | ||
324 | } | ||
325 | if (inet->inet_dport) { | ||
326 | if (inet->inet_dport != sport) | ||
327 | return -1; | ||
328 | score += 2; | ||
329 | } | ||
330 | if (sk->sk_bound_dev_if) { | ||
331 | if (sk->sk_bound_dev_if != dif) | ||
332 | return -1; | ||
333 | score += 2; | ||
334 | } | ||
335 | } | ||
336 | return score; | ||
337 | } | ||
338 | |||
339 | #define udp_portaddr_for_each_entry_rcu(__sk, node, list) \ | ||
340 | hlist_nulls_for_each_entry_rcu(__sk, node, list, __sk_common.skc_portaddr_node) | ||
341 | |||
342 | /* called with read_rcu_lock() */ | ||
343 | static struct sock *udp4_lib_lookup2(struct net *net, | ||
344 | __be32 saddr, __be16 sport, | ||
345 | __be32 daddr, unsigned int hnum, int dif, | ||
346 | struct udp_hslot *hslot2, unsigned int slot2) | ||
347 | { | ||
348 | struct sock *sk, *result; | ||
349 | struct hlist_nulls_node *node; | ||
350 | int score, badness; | ||
351 | |||
352 | begin: | ||
353 | result = NULL; | ||
354 | badness = -1; | ||
355 | udp_portaddr_for_each_entry_rcu(sk, node, &hslot2->head) { | ||
356 | score = compute_score2(sk, net, saddr, sport, | ||
357 | daddr, hnum, dif); | ||
358 | if (score > badness) { | ||
359 | result = sk; | ||
360 | badness = score; | ||
361 | if (score == SCORE2_MAX) | ||
362 | goto exact_match; | ||
363 | } | ||
364 | } | ||
365 | /* | ||
366 | * if the nulls value we got at the end of this lookup is | ||
367 | * not the expected one, we must restart lookup. | ||
368 | * We probably met an item that was moved to another chain. | ||
369 | */ | ||
370 | if (get_nulls_value(node) != slot2) | ||
371 | goto begin; | ||
372 | |||
373 | if (result) { | ||
374 | exact_match: | ||
375 | if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt))) | ||
376 | result = NULL; | ||
377 | else if (unlikely(compute_score2(result, net, saddr, sport, | ||
378 | daddr, hnum, dif) < badness)) { | ||
379 | sock_put(result); | ||
380 | goto begin; | ||
381 | } | ||
382 | } | ||
383 | return result; | ||
384 | } | ||
385 | |||
301 | /* UDP is nearly always wildcards out the wazoo, it makes no sense to try | 386 | /* UDP is nearly always wildcards out the wazoo, it makes no sense to try |
302 | * harder than this. -DaveM | 387 | * harder than this. -DaveM |
303 | */ | 388 | */ |
@@ -308,11 +393,35 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, | |||
308 | struct sock *sk, *result; | 393 | struct sock *sk, *result; |
309 | struct hlist_nulls_node *node; | 394 | struct hlist_nulls_node *node; |
310 | unsigned short hnum = ntohs(dport); | 395 | unsigned short hnum = ntohs(dport); |
311 | unsigned int hash = udp_hashfn(net, hnum, udptable->mask); | 396 | unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask); |
312 | struct udp_hslot *hslot = &udptable->hash[hash]; | 397 | struct udp_hslot *hslot2, *hslot = &udptable->hash[slot]; |
313 | int score, badness; | 398 | int score, badness; |
314 | 399 | ||
315 | rcu_read_lock(); | 400 | rcu_read_lock(); |
401 | if (hslot->count > 10) { | ||
402 | hash2 = udp4_portaddr_hash(net, daddr, hnum); | ||
403 | slot2 = hash2 & udptable->mask; | ||
404 | hslot2 = &udptable->hash2[slot2]; | ||
405 | if (hslot->count < hslot2->count) | ||
406 | goto begin; | ||
407 | |||
408 | result = udp4_lib_lookup2(net, saddr, sport, | ||
409 | daddr, hnum, dif, | ||
410 | hslot2, slot2); | ||
411 | if (!result) { | ||
412 | hash2 = udp4_portaddr_hash(net, INADDR_ANY, hnum); | ||
413 | slot2 = hash2 & udptable->mask; | ||
414 | hslot2 = &udptable->hash2[slot2]; | ||
415 | if (hslot->count < hslot2->count) | ||
416 | goto begin; | ||
417 | |||
418 | result = udp4_lib_lookup2(net, INADDR_ANY, sport, | ||
419 | daddr, hnum, dif, | ||
420 | hslot2, slot2); | ||
421 | } | ||
422 | rcu_read_unlock(); | ||
423 | return result; | ||
424 | } | ||
316 | begin: | 425 | begin: |
317 | result = NULL; | 426 | result = NULL; |
318 | badness = -1; | 427 | badness = -1; |
@@ -329,7 +438,7 @@ begin: | |||
329 | * not the expected one, we must restart lookup. | 438 | * not the expected one, we must restart lookup. |
330 | * We probably met an item that was moved to another chain. | 439 | * We probably met an item that was moved to another chain. |
331 | */ | 440 | */ |
332 | if (get_nulls_value(node) != hash) | 441 | if (get_nulls_value(node) != slot) |
333 | goto begin; | 442 | goto begin; |
334 | 443 | ||
335 | if (result) { | 444 | if (result) { |