aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/udp.c115
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)
305static 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() */
343static 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
352begin:
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) {
374exact_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 }
316begin: 425begin:
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) {