aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r--net/ipv6/udp.c137
1 files changed, 83 insertions, 54 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index e51da8c092fa..32d914db6c4f 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -54,62 +54,89 @@ 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
57static 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
57static struct sock *__udp6_lib_lookup(struct net *net, 95static 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_node *node, *next;
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 108begin:
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_for_each_rcu_safenext(sk, node, &hslot->head, next) {
74 int score = 0; 112 /*
75 if (inet->dport) { 113 * lockless reader, and SLAB_DESTROY_BY_RCU items:
76 if (inet->dport != sport) 114 * We must check this item was not moved to another chain
77 continue; 115 */
78 score++; 116 if (udp_hashfn(net, sk->sk_hash) != hash)
79 } 117 goto begin;
80 if (!ipv6_addr_any(&np->rcv_saddr)) { 118 score = compute_score(sk, net, hnum, saddr, sport, daddr, dport, dif);
81 if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) 119 if (score > badness) {
82 continue; 120 result = sk;
83 score++; 121 badness = score;
84 } 122 }
85 if (!ipv6_addr_any(&np->daddr)) { 123 }
86 if (!ipv6_addr_equal(&np->daddr, saddr)) 124 if (result) {
87 continue; 125 if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
88 score++; 126 result = NULL;
89 } 127 else if (unlikely(compute_score(result, net, hnum, saddr, sport,
90 if (sk->sk_bound_dev_if) { 128 daddr, dport, dif) < badness)) {
91 if (sk->sk_bound_dev_if != dif) 129 sock_put(result);
92 continue; 130 goto begin;
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 } 131 }
103 } 132 }
104 if (result) 133 rcu_read_unlock();
105 sock_hold(result);
106 read_unlock(&udp_hash_lock);
107 return result; 134 return result;
108} 135}
109 136
110static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, 137static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
111 __be16 sport, __be16 dport, 138 __be16 sport, __be16 dport,
112 struct hlist_head udptable[]) 139 struct udp_table *udptable)
113{ 140{
114 struct sock *sk; 141 struct sock *sk;
115 struct ipv6hdr *iph = ipv6_hdr(skb); 142 struct ipv6hdr *iph = ipv6_hdr(skb);
@@ -239,7 +266,7 @@ csum_copy_err:
239 266
240void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 267void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
241 int type, int code, int offset, __be32 info, 268 int type, int code, int offset, __be32 info,
242 struct hlist_head udptable[] ) 269 struct udp_table *udptable)
243{ 270{
244 struct ipv6_pinfo *np; 271 struct ipv6_pinfo *np;
245 struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; 272 struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;
@@ -275,7 +302,7 @@ static __inline__ void udpv6_err(struct sk_buff *skb,
275 struct inet6_skb_parm *opt, int type, 302 struct inet6_skb_parm *opt, int type,
276 int code, int offset, __be32 info ) 303 int code, int offset, __be32 info )
277{ 304{
278 __udp6_lib_err(skb, opt, type, code, offset, info, udp_hash); 305 __udp6_lib_err(skb, opt, type, code, offset, info, &udp_table);
279} 306}
280 307
281int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) 308int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
@@ -374,14 +401,15 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
374 */ 401 */
375static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, 402static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
376 struct in6_addr *saddr, struct in6_addr *daddr, 403 struct in6_addr *saddr, struct in6_addr *daddr,
377 struct hlist_head udptable[]) 404 struct udp_table *udptable)
378{ 405{
379 struct sock *sk, *sk2; 406 struct sock *sk, *sk2;
380 const struct udphdr *uh = udp_hdr(skb); 407 const struct udphdr *uh = udp_hdr(skb);
408 struct udp_hslot *hslot = &udptable->hash[udp_hashfn(net, ntohs(uh->dest))];
381 int dif; 409 int dif;
382 410
383 read_lock(&udp_hash_lock); 411 spin_lock(&hslot->lock);
384 sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]); 412 sk = sk_head(&hslot->head);
385 dif = inet6_iif(skb); 413 dif = inet6_iif(skb);
386 sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); 414 sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);
387 if (!sk) { 415 if (!sk) {
@@ -409,7 +437,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
409 sk_add_backlog(sk, skb); 437 sk_add_backlog(sk, skb);
410 bh_unlock_sock(sk); 438 bh_unlock_sock(sk);
411out: 439out:
412 read_unlock(&udp_hash_lock); 440 spin_unlock(&hslot->lock);
413 return 0; 441 return 0;
414} 442}
415 443
@@ -447,7 +475,7 @@ static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh,
447 return 0; 475 return 0;
448} 476}
449 477
450int __udp6_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], 478int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
451 int proto) 479 int proto)
452{ 480{
453 struct sock *sk; 481 struct sock *sk;
@@ -544,7 +572,7 @@ discard:
544 572
545static __inline__ int udpv6_rcv(struct sk_buff *skb) 573static __inline__ int udpv6_rcv(struct sk_buff *skb)
546{ 574{
547 return __udp6_lib_rcv(skb, udp_hash, IPPROTO_UDP); 575 return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP);
548} 576}
549 577
550/* 578/*
@@ -1008,7 +1036,7 @@ int udp6_seq_show(struct seq_file *seq, void *v)
1008static struct udp_seq_afinfo udp6_seq_afinfo = { 1036static struct udp_seq_afinfo udp6_seq_afinfo = {
1009 .name = "udp6", 1037 .name = "udp6",
1010 .family = AF_INET6, 1038 .family = AF_INET6,
1011 .hashtable = udp_hash, 1039 .udp_table = &udp_table,
1012 .seq_fops = { 1040 .seq_fops = {
1013 .owner = THIS_MODULE, 1041 .owner = THIS_MODULE,
1014 }, 1042 },
@@ -1050,7 +1078,8 @@ struct proto udpv6_prot = {
1050 .sysctl_wmem = &sysctl_udp_wmem_min, 1078 .sysctl_wmem = &sysctl_udp_wmem_min,
1051 .sysctl_rmem = &sysctl_udp_rmem_min, 1079 .sysctl_rmem = &sysctl_udp_rmem_min,
1052 .obj_size = sizeof(struct udp6_sock), 1080 .obj_size = sizeof(struct udp6_sock),
1053 .h.udp_hash = udp_hash, 1081 .slab_flags = SLAB_DESTROY_BY_RCU,
1082 .h.udp_table = &udp_table,
1054#ifdef CONFIG_COMPAT 1083#ifdef CONFIG_COMPAT
1055 .compat_setsockopt = compat_udpv6_setsockopt, 1084 .compat_setsockopt = compat_udpv6_setsockopt,
1056 .compat_getsockopt = compat_udpv6_getsockopt, 1085 .compat_getsockopt = compat_udpv6_getsockopt,