diff options
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r-- | net/ipv6/udp.c | 151 |
1 files changed, 93 insertions, 58 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 8b48512ebf6a..84b1a296eecb 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -54,62 +54,91 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum) | |||
54 | return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal); | 54 | return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal); |
55 | } | 55 | } |
56 | 56 | ||
57 | static inline int compute_score(struct sock *sk, struct net *net, | ||
58 | unsigned short hnum, | ||
59 | struct in6_addr *saddr, __be16 sport, | ||
60 | struct in6_addr *daddr, __be16 dport, | ||
61 | int dif) | ||
62 | { | ||
63 | int score = -1; | ||
64 | |||
65 | if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && | ||
66 | sk->sk_family == PF_INET6) { | ||
67 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
68 | struct inet_sock *inet = inet_sk(sk); | ||
69 | |||
70 | score = 0; | ||
71 | if (inet->dport) { | ||
72 | if (inet->dport != sport) | ||
73 | return -1; | ||
74 | score++; | ||
75 | } | ||
76 | if (!ipv6_addr_any(&np->rcv_saddr)) { | ||
77 | if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) | ||
78 | return -1; | ||
79 | score++; | ||
80 | } | ||
81 | if (!ipv6_addr_any(&np->daddr)) { | ||
82 | if (!ipv6_addr_equal(&np->daddr, saddr)) | ||
83 | return -1; | ||
84 | score++; | ||
85 | } | ||
86 | if (sk->sk_bound_dev_if) { | ||
87 | if (sk->sk_bound_dev_if != dif) | ||
88 | return -1; | ||
89 | score++; | ||
90 | } | ||
91 | } | ||
92 | return score; | ||
93 | } | ||
94 | |||
57 | static struct sock *__udp6_lib_lookup(struct net *net, | 95 | static struct sock *__udp6_lib_lookup(struct net *net, |
58 | struct in6_addr *saddr, __be16 sport, | 96 | struct in6_addr *saddr, __be16 sport, |
59 | struct in6_addr *daddr, __be16 dport, | 97 | struct in6_addr *daddr, __be16 dport, |
60 | int dif, struct hlist_head udptable[]) | 98 | int dif, struct udp_table *udptable) |
61 | { | 99 | { |
62 | struct sock *sk, *result = NULL; | 100 | struct sock *sk, *result; |
63 | struct hlist_node *node; | 101 | struct hlist_nulls_node *node; |
64 | unsigned short hnum = ntohs(dport); | 102 | unsigned short hnum = ntohs(dport); |
65 | int badness = -1; | 103 | unsigned int hash = udp_hashfn(net, hnum); |
66 | 104 | struct udp_hslot *hslot = &udptable->hash[hash]; | |
67 | read_lock(&udp_hash_lock); | 105 | int score, badness; |
68 | sk_for_each(sk, node, &udptable[udp_hashfn(net, hnum)]) { | 106 | |
69 | struct inet_sock *inet = inet_sk(sk); | 107 | rcu_read_lock(); |
70 | 108 | begin: | |
71 | if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && | 109 | result = NULL; |
72 | sk->sk_family == PF_INET6) { | 110 | badness = -1; |
73 | struct ipv6_pinfo *np = inet6_sk(sk); | 111 | sk_nulls_for_each_rcu(sk, node, &hslot->head) { |
74 | int score = 0; | 112 | score = compute_score(sk, net, hnum, saddr, sport, daddr, dport, dif); |
75 | if (inet->dport) { | 113 | if (score > badness) { |
76 | if (inet->dport != sport) | 114 | result = sk; |
77 | continue; | 115 | badness = score; |
78 | score++; | ||
79 | } | ||
80 | if (!ipv6_addr_any(&np->rcv_saddr)) { | ||
81 | if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) | ||
82 | continue; | ||
83 | score++; | ||
84 | } | ||
85 | if (!ipv6_addr_any(&np->daddr)) { | ||
86 | if (!ipv6_addr_equal(&np->daddr, saddr)) | ||
87 | continue; | ||
88 | score++; | ||
89 | } | ||
90 | if (sk->sk_bound_dev_if) { | ||
91 | if (sk->sk_bound_dev_if != dif) | ||
92 | continue; | ||
93 | score++; | ||
94 | } | ||
95 | if (score == 4) { | ||
96 | result = sk; | ||
97 | break; | ||
98 | } else if (score > badness) { | ||
99 | result = sk; | ||
100 | badness = score; | ||
101 | } | ||
102 | } | 116 | } |
103 | } | 117 | } |
104 | if (result) | 118 | /* |
105 | sock_hold(result); | 119 | * if the nulls value we got at the end of this lookup is |
106 | read_unlock(&udp_hash_lock); | 120 | * not the expected one, we must restart lookup. |
121 | * We probably met an item that was moved to another chain. | ||
122 | */ | ||
123 | if (get_nulls_value(node) != hash) | ||
124 | goto begin; | ||
125 | |||
126 | if (result) { | ||
127 | if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt))) | ||
128 | result = NULL; | ||
129 | else if (unlikely(compute_score(result, net, hnum, saddr, sport, | ||
130 | daddr, dport, dif) < badness)) { | ||
131 | sock_put(result); | ||
132 | goto begin; | ||
133 | } | ||
134 | } | ||
135 | rcu_read_unlock(); | ||
107 | return result; | 136 | return result; |
108 | } | 137 | } |
109 | 138 | ||
110 | static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, | 139 | static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, |
111 | __be16 sport, __be16 dport, | 140 | __be16 sport, __be16 dport, |
112 | struct hlist_head udptable[]) | 141 | struct udp_table *udptable) |
113 | { | 142 | { |
114 | struct sock *sk; | 143 | struct sock *sk; |
115 | struct ipv6hdr *iph = ipv6_hdr(skb); | 144 | struct ipv6hdr *iph = ipv6_hdr(skb); |
@@ -253,7 +282,7 @@ csum_copy_err: | |||
253 | 282 | ||
254 | void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 283 | void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
255 | int type, int code, int offset, __be32 info, | 284 | int type, int code, int offset, __be32 info, |
256 | struct hlist_head udptable[] ) | 285 | struct udp_table *udptable) |
257 | { | 286 | { |
258 | struct ipv6_pinfo *np; | 287 | struct ipv6_pinfo *np; |
259 | struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; | 288 | struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; |
@@ -289,7 +318,7 @@ static __inline__ void udpv6_err(struct sk_buff *skb, | |||
289 | struct inet6_skb_parm *opt, int type, | 318 | struct inet6_skb_parm *opt, int type, |
290 | int code, int offset, __be32 info ) | 319 | int code, int offset, __be32 info ) |
291 | { | 320 | { |
292 | __udp6_lib_err(skb, opt, type, code, offset, info, udp_hash); | 321 | __udp6_lib_err(skb, opt, type, code, offset, info, &udp_table); |
293 | } | 322 | } |
294 | 323 | ||
295 | int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) | 324 | int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) |
@@ -347,11 +376,11 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk, | |||
347 | __be16 rmt_port, struct in6_addr *rmt_addr, | 376 | __be16 rmt_port, struct in6_addr *rmt_addr, |
348 | int dif) | 377 | int dif) |
349 | { | 378 | { |
350 | struct hlist_node *node; | 379 | struct hlist_nulls_node *node; |
351 | struct sock *s = sk; | 380 | struct sock *s = sk; |
352 | unsigned short num = ntohs(loc_port); | 381 | unsigned short num = ntohs(loc_port); |
353 | 382 | ||
354 | sk_for_each_from(s, node) { | 383 | sk_nulls_for_each_from(s, node) { |
355 | struct inet_sock *inet = inet_sk(s); | 384 | struct inet_sock *inet = inet_sk(s); |
356 | 385 | ||
357 | if (!net_eq(sock_net(s), net)) | 386 | if (!net_eq(sock_net(s), net)) |
@@ -388,14 +417,15 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk, | |||
388 | */ | 417 | */ |
389 | static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, | 418 | static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, |
390 | struct in6_addr *saddr, struct in6_addr *daddr, | 419 | struct in6_addr *saddr, struct in6_addr *daddr, |
391 | struct hlist_head udptable[]) | 420 | struct udp_table *udptable) |
392 | { | 421 | { |
393 | struct sock *sk, *sk2; | 422 | struct sock *sk, *sk2; |
394 | const struct udphdr *uh = udp_hdr(skb); | 423 | const struct udphdr *uh = udp_hdr(skb); |
424 | struct udp_hslot *hslot = &udptable->hash[udp_hashfn(net, ntohs(uh->dest))]; | ||
395 | int dif; | 425 | int dif; |
396 | 426 | ||
397 | read_lock(&udp_hash_lock); | 427 | spin_lock(&hslot->lock); |
398 | sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]); | 428 | sk = sk_nulls_head(&hslot->head); |
399 | dif = inet6_iif(skb); | 429 | dif = inet6_iif(skb); |
400 | sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif); | 430 | sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif); |
401 | if (!sk) { | 431 | if (!sk) { |
@@ -404,7 +434,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, | |||
404 | } | 434 | } |
405 | 435 | ||
406 | sk2 = sk; | 436 | sk2 = sk; |
407 | while ((sk2 = udp_v6_mcast_next(net, sk_next(sk2), uh->dest, daddr, | 437 | while ((sk2 = udp_v6_mcast_next(net, sk_nulls_next(sk2), uh->dest, daddr, |
408 | uh->source, saddr, dif))) { | 438 | uh->source, saddr, dif))) { |
409 | struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC); | 439 | struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC); |
410 | if (buff) { | 440 | if (buff) { |
@@ -423,7 +453,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, | |||
423 | sk_add_backlog(sk, skb); | 453 | sk_add_backlog(sk, skb); |
424 | bh_unlock_sock(sk); | 454 | bh_unlock_sock(sk); |
425 | out: | 455 | out: |
426 | read_unlock(&udp_hash_lock); | 456 | spin_unlock(&hslot->lock); |
427 | return 0; | 457 | return 0; |
428 | } | 458 | } |
429 | 459 | ||
@@ -461,7 +491,7 @@ static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, | |||
461 | return 0; | 491 | return 0; |
462 | } | 492 | } |
463 | 493 | ||
464 | int __udp6_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], | 494 | int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, |
465 | int proto) | 495 | int proto) |
466 | { | 496 | { |
467 | struct sock *sk; | 497 | struct sock *sk; |
@@ -558,7 +588,7 @@ discard: | |||
558 | 588 | ||
559 | static __inline__ int udpv6_rcv(struct sk_buff *skb) | 589 | static __inline__ int udpv6_rcv(struct sk_buff *skb) |
560 | { | 590 | { |
561 | return __udp6_lib_rcv(skb, udp_hash, IPPROTO_UDP); | 591 | return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP); |
562 | } | 592 | } |
563 | 593 | ||
564 | /* | 594 | /* |
@@ -763,6 +793,9 @@ do_udp_sendmsg: | |||
763 | if (!fl.oif) | 793 | if (!fl.oif) |
764 | fl.oif = sk->sk_bound_dev_if; | 794 | fl.oif = sk->sk_bound_dev_if; |
765 | 795 | ||
796 | if (!fl.oif) | ||
797 | fl.oif = np->sticky_pktinfo.ipi6_ifindex; | ||
798 | |||
766 | if (msg->msg_controllen) { | 799 | if (msg->msg_controllen) { |
767 | opt = &opt_space; | 800 | opt = &opt_space; |
768 | memset(opt, 0, sizeof(struct ipv6_txoptions)); | 801 | memset(opt, 0, sizeof(struct ipv6_txoptions)); |
@@ -819,7 +852,8 @@ do_udp_sendmsg: | |||
819 | if (final_p) | 852 | if (final_p) |
820 | ipv6_addr_copy(&fl.fl6_dst, final_p); | 853 | ipv6_addr_copy(&fl.fl6_dst, final_p); |
821 | 854 | ||
822 | if ((err = __xfrm_lookup(&dst, &fl, sk, XFRM_LOOKUP_WAIT)) < 0) { | 855 | err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); |
856 | if (err < 0) { | ||
823 | if (err == -EREMOTE) | 857 | if (err == -EREMOTE) |
824 | err = ip6_dst_blackhole(sk, &dst, &fl); | 858 | err = ip6_dst_blackhole(sk, &dst, &fl); |
825 | if (err < 0) | 859 | if (err < 0) |
@@ -1022,7 +1056,7 @@ int udp6_seq_show(struct seq_file *seq, void *v) | |||
1022 | static struct udp_seq_afinfo udp6_seq_afinfo = { | 1056 | static struct udp_seq_afinfo udp6_seq_afinfo = { |
1023 | .name = "udp6", | 1057 | .name = "udp6", |
1024 | .family = AF_INET6, | 1058 | .family = AF_INET6, |
1025 | .hashtable = udp_hash, | 1059 | .udp_table = &udp_table, |
1026 | .seq_fops = { | 1060 | .seq_fops = { |
1027 | .owner = THIS_MODULE, | 1061 | .owner = THIS_MODULE, |
1028 | }, | 1062 | }, |
@@ -1064,7 +1098,8 @@ struct proto udpv6_prot = { | |||
1064 | .sysctl_wmem = &sysctl_udp_wmem_min, | 1098 | .sysctl_wmem = &sysctl_udp_wmem_min, |
1065 | .sysctl_rmem = &sysctl_udp_rmem_min, | 1099 | .sysctl_rmem = &sysctl_udp_rmem_min, |
1066 | .obj_size = sizeof(struct udp6_sock), | 1100 | .obj_size = sizeof(struct udp6_sock), |
1067 | .h.udp_hash = udp_hash, | 1101 | .slab_flags = SLAB_DESTROY_BY_RCU, |
1102 | .h.udp_table = &udp_table, | ||
1068 | #ifdef CONFIG_COMPAT | 1103 | #ifdef CONFIG_COMPAT |
1069 | .compat_setsockopt = compat_udpv6_setsockopt, | 1104 | .compat_setsockopt = compat_udpv6_setsockopt, |
1070 | .compat_getsockopt = compat_udpv6_getsockopt, | 1105 | .compat_getsockopt = compat_udpv6_getsockopt, |