diff options
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r-- | net/ipv6/udp.c | 112 |
1 files changed, 62 insertions, 50 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index e51da8c092fa..ccee7244ca0f 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -54,62 +54,73 @@ 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 = NULL; |
63 | struct hlist_node *node; | 101 | struct hlist_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 = -1; |
68 | sk_for_each(sk, node, &udptable[udp_hashfn(net, hnum)]) { | 106 | |
69 | struct inet_sock *inet = inet_sk(sk); | 107 | spin_lock(&hslot->lock); |
70 | 108 | sk_for_each(sk, node, &hslot->head) { | |
71 | if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && | 109 | score = compute_score(sk, net, hnum, saddr, sport, daddr, dport, dif); |
72 | sk->sk_family == PF_INET6) { | 110 | if (score > badness) { |
73 | struct ipv6_pinfo *np = inet6_sk(sk); | 111 | result = sk; |
74 | int score = 0; | 112 | badness = score; |
75 | if (inet->dport) { | ||
76 | if (inet->dport != sport) | ||
77 | continue; | ||
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 | } | 113 | } |
103 | } | 114 | } |
104 | if (result) | 115 | if (result) |
105 | sock_hold(result); | 116 | sock_hold(result); |
106 | read_unlock(&udp_hash_lock); | 117 | spin_unlock(&hslot->lock); |
107 | return result; | 118 | return result; |
108 | } | 119 | } |
109 | 120 | ||
110 | static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, | 121 | static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, |
111 | __be16 sport, __be16 dport, | 122 | __be16 sport, __be16 dport, |
112 | struct hlist_head udptable[]) | 123 | struct udp_table *udptable) |
113 | { | 124 | { |
114 | struct sock *sk; | 125 | struct sock *sk; |
115 | struct ipv6hdr *iph = ipv6_hdr(skb); | 126 | struct ipv6hdr *iph = ipv6_hdr(skb); |
@@ -239,7 +250,7 @@ csum_copy_err: | |||
239 | 250 | ||
240 | void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 251 | void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
241 | int type, int code, int offset, __be32 info, | 252 | int type, int code, int offset, __be32 info, |
242 | struct hlist_head udptable[] ) | 253 | struct udp_table *udptable) |
243 | { | 254 | { |
244 | struct ipv6_pinfo *np; | 255 | struct ipv6_pinfo *np; |
245 | struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; | 256 | struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; |
@@ -275,7 +286,7 @@ static __inline__ void udpv6_err(struct sk_buff *skb, | |||
275 | struct inet6_skb_parm *opt, int type, | 286 | struct inet6_skb_parm *opt, int type, |
276 | int code, int offset, __be32 info ) | 287 | int code, int offset, __be32 info ) |
277 | { | 288 | { |
278 | __udp6_lib_err(skb, opt, type, code, offset, info, udp_hash); | 289 | __udp6_lib_err(skb, opt, type, code, offset, info, &udp_table); |
279 | } | 290 | } |
280 | 291 | ||
281 | int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) | 292 | int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) |
@@ -374,14 +385,15 @@ static struct sock *udp_v6_mcast_next(struct sock *sk, | |||
374 | */ | 385 | */ |
375 | static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, | 386 | static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, |
376 | struct in6_addr *saddr, struct in6_addr *daddr, | 387 | struct in6_addr *saddr, struct in6_addr *daddr, |
377 | struct hlist_head udptable[]) | 388 | struct udp_table *udptable) |
378 | { | 389 | { |
379 | struct sock *sk, *sk2; | 390 | struct sock *sk, *sk2; |
380 | const struct udphdr *uh = udp_hdr(skb); | 391 | const struct udphdr *uh = udp_hdr(skb); |
392 | struct udp_hslot *hslot = &udptable->hash[udp_hashfn(net, ntohs(uh->dest))]; | ||
381 | int dif; | 393 | int dif; |
382 | 394 | ||
383 | read_lock(&udp_hash_lock); | 395 | spin_lock(&hslot->lock); |
384 | sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]); | 396 | sk = sk_head(&hslot->head); |
385 | dif = inet6_iif(skb); | 397 | dif = inet6_iif(skb); |
386 | sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); | 398 | sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); |
387 | if (!sk) { | 399 | if (!sk) { |
@@ -409,7 +421,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, | |||
409 | sk_add_backlog(sk, skb); | 421 | sk_add_backlog(sk, skb); |
410 | bh_unlock_sock(sk); | 422 | bh_unlock_sock(sk); |
411 | out: | 423 | out: |
412 | read_unlock(&udp_hash_lock); | 424 | spin_unlock(&hslot->lock); |
413 | return 0; | 425 | return 0; |
414 | } | 426 | } |
415 | 427 | ||
@@ -447,7 +459,7 @@ static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, | |||
447 | return 0; | 459 | return 0; |
448 | } | 460 | } |
449 | 461 | ||
450 | int __udp6_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], | 462 | int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, |
451 | int proto) | 463 | int proto) |
452 | { | 464 | { |
453 | struct sock *sk; | 465 | struct sock *sk; |
@@ -544,7 +556,7 @@ discard: | |||
544 | 556 | ||
545 | static __inline__ int udpv6_rcv(struct sk_buff *skb) | 557 | static __inline__ int udpv6_rcv(struct sk_buff *skb) |
546 | { | 558 | { |
547 | return __udp6_lib_rcv(skb, udp_hash, IPPROTO_UDP); | 559 | return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP); |
548 | } | 560 | } |
549 | 561 | ||
550 | /* | 562 | /* |
@@ -1008,7 +1020,7 @@ int udp6_seq_show(struct seq_file *seq, void *v) | |||
1008 | static struct udp_seq_afinfo udp6_seq_afinfo = { | 1020 | static struct udp_seq_afinfo udp6_seq_afinfo = { |
1009 | .name = "udp6", | 1021 | .name = "udp6", |
1010 | .family = AF_INET6, | 1022 | .family = AF_INET6, |
1011 | .hashtable = udp_hash, | 1023 | .udp_table = &udp_table, |
1012 | .seq_fops = { | 1024 | .seq_fops = { |
1013 | .owner = THIS_MODULE, | 1025 | .owner = THIS_MODULE, |
1014 | }, | 1026 | }, |
@@ -1050,7 +1062,7 @@ struct proto udpv6_prot = { | |||
1050 | .sysctl_wmem = &sysctl_udp_wmem_min, | 1062 | .sysctl_wmem = &sysctl_udp_wmem_min, |
1051 | .sysctl_rmem = &sysctl_udp_rmem_min, | 1063 | .sysctl_rmem = &sysctl_udp_rmem_min, |
1052 | .obj_size = sizeof(struct udp6_sock), | 1064 | .obj_size = sizeof(struct udp6_sock), |
1053 | .h.udp_hash = udp_hash, | 1065 | .h.udp_table = &udp_table, |
1054 | #ifdef CONFIG_COMPAT | 1066 | #ifdef CONFIG_COMPAT |
1055 | .compat_setsockopt = compat_udpv6_setsockopt, | 1067 | .compat_setsockopt = compat_udpv6_setsockopt, |
1056 | .compat_getsockopt = compat_udpv6_getsockopt, | 1068 | .compat_getsockopt = compat_udpv6_getsockopt, |