aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/udp.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-12-28 15:49:40 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2008-12-28 15:49:40 -0500
commit0191b625ca5a46206d2fb862bb08f36f2fcb3b31 (patch)
tree454d1842b1833d976da62abcbd5c47521ebe9bd7 /net/ipv6/udp.c
parent54a696bd07c14d3b1192d03ce7269bc59b45209a (diff)
parenteb56092fc168bf5af199d47af50c0d84a96db898 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1429 commits) net: Allow dependancies of FDDI & Tokenring to be modular. igb: Fix build warning when DCA is disabled. net: Fix warning fallout from recent NAPI interface changes. gro: Fix potential use after free sfc: If AN is enabled, always read speed/duplex from the AN advertising bits sfc: When disabling the NIC, close the device rather than unregistering it sfc: SFT9001: Add cable diagnostics sfc: Add support for multiple PHY self-tests sfc: Merge top-level functions for self-tests sfc: Clean up PHY mode management in loopback self-test sfc: Fix unreliable link detection in some loopback modes sfc: Generate unique names for per-NIC workqueues 802.3ad: use standard ethhdr instead of ad_header 802.3ad: generalize out mac address initializer 802.3ad: initialize ports LACPDU from const initializer 802.3ad: remove typedef around ad_system 802.3ad: turn ports is_individual into a bool 802.3ad: turn ports is_enabled into a bool 802.3ad: make ntt bool ixgbe: Fix set_ringparam in ixgbe to use the same memory pools. ... Fixed trivial IPv4/6 address printing conflicts in fs/cifs/connect.c due to the conversion to %pI (in this networking merge) and the addition of doing IPv6 addresses (from the earlier merge of CIFS).
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r--net/ipv6/udp.c151
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
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_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 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_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
110static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, 139static 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
254void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 283void __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
295int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) 324int 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 */
389static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, 418static 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);
425out: 455out:
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
464int __udp6_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], 494int __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
559static __inline__ int udpv6_rcv(struct sk_buff *skb) 589static __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)
1022static struct udp_seq_afinfo udp6_seq_afinfo = { 1056static 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,