diff options
-rw-r--r-- | include/net/inet6_hashtables.h | 106 | ||||
-rw-r--r-- | net/ipv4/Kconfig | 4 | ||||
-rw-r--r-- | net/ipv6/Makefile | 2 | ||||
-rw-r--r-- | net/ipv6/inet6_hashtables.c | 81 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 154 |
5 files changed, 190 insertions, 157 deletions
diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index 297c2b16967a..03df3b157960 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h | |||
@@ -14,13 +14,117 @@ | |||
14 | #ifndef _INET6_HASHTABLES_H | 14 | #ifndef _INET6_HASHTABLES_H |
15 | #define _INET6_HASHTABLES_H | 15 | #define _INET6_HASHTABLES_H |
16 | 16 | ||
17 | #include <linux/config.h> | ||
18 | |||
19 | #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | ||
20 | #include <linux/in6.h> | ||
21 | #include <linux/ipv6.h> | ||
17 | #include <linux/types.h> | 22 | #include <linux/types.h> |
18 | 23 | ||
19 | struct in6_addr; | 24 | #include <net/ipv6.h> |
25 | |||
20 | struct inet_hashinfo; | 26 | struct inet_hashinfo; |
21 | 27 | ||
28 | /* I have no idea if this is a good hash for v6 or not. -DaveM */ | ||
29 | static inline int inet6_ehashfn(const struct in6_addr *laddr, const u16 lport, | ||
30 | const struct in6_addr *faddr, const u16 fport, | ||
31 | const int ehash_size) | ||
32 | { | ||
33 | int hashent = (lport ^ fport); | ||
34 | |||
35 | hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]); | ||
36 | hashent ^= hashent >> 16; | ||
37 | hashent ^= hashent >> 8; | ||
38 | return (hashent & (ehash_size - 1)); | ||
39 | } | ||
40 | |||
41 | static inline int inet6_sk_ehashfn(const struct sock *sk, const int ehash_size) | ||
42 | { | ||
43 | const struct inet_sock *inet = inet_sk(sk); | ||
44 | const struct ipv6_pinfo *np = inet6_sk(sk); | ||
45 | const struct in6_addr *laddr = &np->rcv_saddr; | ||
46 | const struct in6_addr *faddr = &np->daddr; | ||
47 | const __u16 lport = inet->num; | ||
48 | const __u16 fport = inet->dport; | ||
49 | return inet6_ehashfn(laddr, lport, faddr, fport, ehash_size); | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so | ||
54 | * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM | ||
55 | * | ||
56 | * The sockhash lock must be held as a reader here. | ||
57 | */ | ||
58 | static inline struct sock * | ||
59 | __inet6_lookup_established(struct inet_hashinfo *hashinfo, | ||
60 | const struct in6_addr *saddr, | ||
61 | const u16 sport, | ||
62 | const struct in6_addr *daddr, | ||
63 | const u16 hnum, | ||
64 | const int dif) | ||
65 | { | ||
66 | struct sock *sk; | ||
67 | const struct hlist_node *node; | ||
68 | const __u32 ports = INET_COMBINED_PORTS(sport, hnum); | ||
69 | /* Optimize here for direct hit, only listening connections can | ||
70 | * have wildcards anyways. | ||
71 | */ | ||
72 | const int hash = inet6_ehashfn(daddr, hnum, saddr, sport, | ||
73 | hashinfo->ehash_size); | ||
74 | struct inet_ehash_bucket *head = &hashinfo->ehash[hash]; | ||
75 | |||
76 | read_lock(&head->lock); | ||
77 | sk_for_each(sk, node, &head->chain) { | ||
78 | /* For IPV6 do the cheaper port and family tests first. */ | ||
79 | if (INET6_MATCH(sk, saddr, daddr, ports, dif)) | ||
80 | goto hit; /* You sunk my battleship! */ | ||
81 | } | ||
82 | /* Must check for a TIME_WAIT'er before going to listener hash. */ | ||
83 | sk_for_each(sk, node, &(head + hashinfo->ehash_size)->chain) { | ||
84 | const struct inet_timewait_sock *tw = inet_twsk(sk); | ||
85 | |||
86 | if(*((__u32 *)&(tw->tw_dport)) == ports && | ||
87 | sk->sk_family == PF_INET6) { | ||
88 | const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk); | ||
89 | |||
90 | if (ipv6_addr_equal(&tcp6tw->tw_v6_daddr, saddr) && | ||
91 | ipv6_addr_equal(&tcp6tw->tw_v6_rcv_saddr, daddr) && | ||
92 | (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif)) | ||
93 | goto hit; | ||
94 | } | ||
95 | } | ||
96 | read_unlock(&head->lock); | ||
97 | return NULL; | ||
98 | |||
99 | hit: | ||
100 | sock_hold(sk); | ||
101 | read_unlock(&head->lock); | ||
102 | return sk; | ||
103 | } | ||
104 | |||
105 | extern struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, | ||
106 | const struct in6_addr *daddr, | ||
107 | const unsigned short hnum, | ||
108 | const int dif); | ||
109 | |||
110 | static inline struct sock *__inet6_lookup(struct inet_hashinfo *hashinfo, | ||
111 | const struct in6_addr *saddr, | ||
112 | const u16 sport, | ||
113 | const struct in6_addr *daddr, | ||
114 | const u16 hnum, | ||
115 | const int dif) | ||
116 | { | ||
117 | struct sock *sk = __inet6_lookup_established(hashinfo, saddr, sport, | ||
118 | daddr, hnum, dif); | ||
119 | if (sk) | ||
120 | return sk; | ||
121 | |||
122 | return inet6_lookup_listener(hashinfo, daddr, hnum, dif); | ||
123 | } | ||
124 | |||
22 | extern struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, | 125 | extern struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, |
23 | const struct in6_addr *saddr, const u16 sport, | 126 | const struct in6_addr *saddr, const u16 sport, |
24 | const struct in6_addr *daddr, const u16 dport, | 127 | const struct in6_addr *daddr, const u16 dport, |
25 | const int dif); | 128 | const int dif); |
129 | #endif /* defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) */ | ||
26 | #endif /* _INET6_HASHTABLES_H */ | 130 | #endif /* _INET6_HASHTABLES_H */ |
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index a79b4f9c10c5..960c02faf440 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig | |||
@@ -419,9 +419,7 @@ config IP_TCPDIAG | |||
419 | ---help--- | 419 | ---help--- |
420 | Support for TCP socket monitoring interface used by native Linux | 420 | Support for TCP socket monitoring interface used by native Linux |
421 | tools such as ss. ss is included in iproute2, currently downloadable | 421 | tools such as ss. ss is included in iproute2, currently downloadable |
422 | at <http://developer.osdl.org/dev/iproute2>. If you want IPv6 or DCCP | 422 | at <http://developer.osdl.org/dev/iproute2>. |
423 | support and have selected IPv6 or DCCP as a module, you need to build | ||
424 | this as a module too. | ||
425 | 423 | ||
426 | If unsure, say Y. | 424 | If unsure, say Y. |
427 | 425 | ||
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 5bccea2d81b4..6460eec834b7 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile | |||
@@ -23,3 +23,5 @@ obj-$(CONFIG_NETFILTER) += netfilter/ | |||
23 | obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o | 23 | obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o |
24 | 24 | ||
25 | obj-y += exthdrs_core.o | 25 | obj-y += exthdrs_core.o |
26 | |||
27 | obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o | ||
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c new file mode 100644 index 000000000000..01d5f46d4e40 --- /dev/null +++ b/net/ipv6/inet6_hashtables.c | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * INET An implementation of the TCP/IP protocol suite for the LINUX | ||
3 | * operating system. INET is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * Generic INET6 transport hashtables | ||
7 | * | ||
8 | * Authors: Lotsa people, from code originally in tcp | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | |||
20 | #include <net/inet_connection_sock.h> | ||
21 | #include <net/inet_hashtables.h> | ||
22 | #include <net/inet6_hashtables.h> | ||
23 | |||
24 | struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, | ||
25 | const struct in6_addr *daddr, | ||
26 | const unsigned short hnum, const int dif) | ||
27 | { | ||
28 | struct sock *sk; | ||
29 | const struct hlist_node *node; | ||
30 | struct sock *result = NULL; | ||
31 | int score, hiscore = 0; | ||
32 | |||
33 | read_lock(&hashinfo->lhash_lock); | ||
34 | sk_for_each(sk, node, &hashinfo->listening_hash[inet_lhashfn(hnum)]) { | ||
35 | if (inet_sk(sk)->num == hnum && sk->sk_family == PF_INET6) { | ||
36 | const struct ipv6_pinfo *np = inet6_sk(sk); | ||
37 | |||
38 | score = 1; | ||
39 | if (!ipv6_addr_any(&np->rcv_saddr)) { | ||
40 | if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) | ||
41 | continue; | ||
42 | score++; | ||
43 | } | ||
44 | if (sk->sk_bound_dev_if) { | ||
45 | if (sk->sk_bound_dev_if != dif) | ||
46 | continue; | ||
47 | score++; | ||
48 | } | ||
49 | if (score == 3) { | ||
50 | result = sk; | ||
51 | break; | ||
52 | } | ||
53 | if (score > hiscore) { | ||
54 | hiscore = score; | ||
55 | result = sk; | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | if (result) | ||
60 | sock_hold(result); | ||
61 | read_unlock(&hashinfo->lhash_lock); | ||
62 | return result; | ||
63 | } | ||
64 | |||
65 | EXPORT_SYMBOL_GPL(inet6_lookup_listener); | ||
66 | |||
67 | struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, | ||
68 | const struct in6_addr *saddr, const u16 sport, | ||
69 | const struct in6_addr *daddr, const u16 dport, | ||
70 | const int dif) | ||
71 | { | ||
72 | struct sock *sk; | ||
73 | |||
74 | local_bh_disable(); | ||
75 | sk = __inet6_lookup(hashinfo, saddr, sport, daddr, ntohs(dport), dif); | ||
76 | local_bh_enable(); | ||
77 | |||
78 | return sk; | ||
79 | } | ||
80 | |||
81 | EXPORT_SYMBOL_GPL(inet6_lookup); | ||
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2bc7fafe7668..fb291b81cf63 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -47,6 +47,7 @@ | |||
47 | 47 | ||
48 | #include <net/tcp.h> | 48 | #include <net/tcp.h> |
49 | #include <net/ndisc.h> | 49 | #include <net/ndisc.h> |
50 | #include <net/inet6_hashtables.h> | ||
50 | #include <net/ipv6.h> | 51 | #include <net/ipv6.h> |
51 | #include <net/transp_v6.h> | 52 | #include <net/transp_v6.h> |
52 | #include <net/addrconf.h> | 53 | #include <net/addrconf.h> |
@@ -75,30 +76,6 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok); | |||
75 | static struct tcp_func ipv6_mapped; | 76 | static struct tcp_func ipv6_mapped; |
76 | static struct tcp_func ipv6_specific; | 77 | static struct tcp_func ipv6_specific; |
77 | 78 | ||
78 | /* I have no idea if this is a good hash for v6 or not. -DaveM */ | ||
79 | static inline int inet6_ehashfn(const struct in6_addr *laddr, const u16 lport, | ||
80 | const struct in6_addr *faddr, const u16 fport, | ||
81 | const int ehash_size) | ||
82 | { | ||
83 | int hashent = (lport ^ fport); | ||
84 | |||
85 | hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]); | ||
86 | hashent ^= hashent>>16; | ||
87 | hashent ^= hashent>>8; | ||
88 | return (hashent & (ehash_size - 1)); | ||
89 | } | ||
90 | |||
91 | static inline int inet6_sk_ehashfn(const struct sock *sk, const int ehash_size) | ||
92 | { | ||
93 | const struct inet_sock *inet = inet_sk(sk); | ||
94 | const struct ipv6_pinfo *np = inet6_sk(sk); | ||
95 | const struct in6_addr *laddr = &np->rcv_saddr; | ||
96 | const struct in6_addr *faddr = &np->daddr; | ||
97 | const __u16 lport = inet->num; | ||
98 | const __u16 fport = inet->dport; | ||
99 | return inet6_ehashfn(laddr, lport, faddr, fport, ehash_size); | ||
100 | } | ||
101 | |||
102 | static inline int tcp_v6_bind_conflict(const struct sock *sk, | 79 | static inline int tcp_v6_bind_conflict(const struct sock *sk, |
103 | const struct inet_bind_bucket *tb) | 80 | const struct inet_bind_bucket *tb) |
104 | { | 81 | { |
@@ -259,135 +236,6 @@ static void tcp_v6_hash(struct sock *sk) | |||
259 | } | 236 | } |
260 | } | 237 | } |
261 | 238 | ||
262 | static struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, | ||
263 | const struct in6_addr *daddr, | ||
264 | const unsigned short hnum, | ||
265 | const int dif) | ||
266 | { | ||
267 | struct sock *sk; | ||
268 | struct hlist_node *node; | ||
269 | struct sock *result = NULL; | ||
270 | int score, hiscore; | ||
271 | |||
272 | hiscore=0; | ||
273 | read_lock(&hashinfo->lhash_lock); | ||
274 | sk_for_each(sk, node, &hashinfo->listening_hash[inet_lhashfn(hnum)]) { | ||
275 | if (inet_sk(sk)->num == hnum && sk->sk_family == PF_INET6) { | ||
276 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
277 | |||
278 | score = 1; | ||
279 | if (!ipv6_addr_any(&np->rcv_saddr)) { | ||
280 | if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) | ||
281 | continue; | ||
282 | score++; | ||
283 | } | ||
284 | if (sk->sk_bound_dev_if) { | ||
285 | if (sk->sk_bound_dev_if != dif) | ||
286 | continue; | ||
287 | score++; | ||
288 | } | ||
289 | if (score == 3) { | ||
290 | result = sk; | ||
291 | break; | ||
292 | } | ||
293 | if (score > hiscore) { | ||
294 | hiscore = score; | ||
295 | result = sk; | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | if (result) | ||
300 | sock_hold(result); | ||
301 | read_unlock(&hashinfo->lhash_lock); | ||
302 | return result; | ||
303 | } | ||
304 | |||
305 | /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so | ||
306 | * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM | ||
307 | * | ||
308 | * The sockhash lock must be held as a reader here. | ||
309 | */ | ||
310 | |||
311 | static inline struct sock * | ||
312 | __inet6_lookup_established(struct inet_hashinfo *hashinfo, | ||
313 | const struct in6_addr *saddr, | ||
314 | const u16 sport, | ||
315 | const struct in6_addr *daddr, | ||
316 | const u16 hnum, | ||
317 | const int dif) | ||
318 | { | ||
319 | struct sock *sk; | ||
320 | const struct hlist_node *node; | ||
321 | const __u32 ports = INET_COMBINED_PORTS(sport, hnum); | ||
322 | /* Optimize here for direct hit, only listening connections can | ||
323 | * have wildcards anyways. | ||
324 | */ | ||
325 | const int hash = inet6_ehashfn(daddr, hnum, saddr, sport, | ||
326 | hashinfo->ehash_size); | ||
327 | struct inet_ehash_bucket *head = &hashinfo->ehash[hash]; | ||
328 | |||
329 | read_lock(&head->lock); | ||
330 | sk_for_each(sk, node, &head->chain) { | ||
331 | /* For IPV6 do the cheaper port and family tests first. */ | ||
332 | if (INET6_MATCH(sk, saddr, daddr, ports, dif)) | ||
333 | goto hit; /* You sunk my battleship! */ | ||
334 | } | ||
335 | /* Must check for a TIME_WAIT'er before going to listener hash. */ | ||
336 | sk_for_each(sk, node, &(head + hashinfo->ehash_size)->chain) { | ||
337 | const struct inet_timewait_sock *tw = inet_twsk(sk); | ||
338 | |||
339 | if(*((__u32 *)&(tw->tw_dport)) == ports && | ||
340 | sk->sk_family == PF_INET6) { | ||
341 | const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk); | ||
342 | |||
343 | if (ipv6_addr_equal(&tcp6tw->tw_v6_daddr, saddr) && | ||
344 | ipv6_addr_equal(&tcp6tw->tw_v6_rcv_saddr, daddr) && | ||
345 | (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif)) | ||
346 | goto hit; | ||
347 | } | ||
348 | } | ||
349 | read_unlock(&head->lock); | ||
350 | return NULL; | ||
351 | |||
352 | hit: | ||
353 | sock_hold(sk); | ||
354 | read_unlock(&head->lock); | ||
355 | return sk; | ||
356 | } | ||
357 | |||
358 | |||
359 | static inline struct sock *__inet6_lookup(struct inet_hashinfo *hashinfo, | ||
360 | const struct in6_addr *saddr, | ||
361 | const u16 sport, | ||
362 | const struct in6_addr *daddr, | ||
363 | const u16 hnum, | ||
364 | const int dif) | ||
365 | { | ||
366 | struct sock *sk = __inet6_lookup_established(hashinfo, saddr, sport, | ||
367 | daddr, hnum, dif); | ||
368 | if (sk) | ||
369 | return sk; | ||
370 | |||
371 | return inet6_lookup_listener(hashinfo, daddr, hnum, dif); | ||
372 | } | ||
373 | |||
374 | inline struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, | ||
375 | const struct in6_addr *saddr, const u16 sport, | ||
376 | const struct in6_addr *daddr, const u16 dport, | ||
377 | const int dif) | ||
378 | { | ||
379 | struct sock *sk; | ||
380 | |||
381 | local_bh_disable(); | ||
382 | sk = __inet6_lookup(hashinfo, saddr, sport, daddr, ntohs(dport), dif); | ||
383 | local_bh_enable(); | ||
384 | |||
385 | return sk; | ||
386 | } | ||
387 | |||
388 | EXPORT_SYMBOL_GPL(inet6_lookup); | ||
389 | |||
390 | |||
391 | /* | 239 | /* |
392 | * Open request hash tables. | 240 | * Open request hash tables. |
393 | */ | 241 | */ |