aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/inet6_hashtables.h106
-rw-r--r--net/ipv4/Kconfig4
-rw-r--r--net/ipv6/Makefile2
-rw-r--r--net/ipv6/inet6_hashtables.c81
-rw-r--r--net/ipv6/tcp_ipv6.c154
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
19struct in6_addr; 24#include <net/ipv6.h>
25
20struct inet_hashinfo; 26struct inet_hashinfo;
21 27
28/* I have no idea if this is a good hash for v6 or not. -DaveM */
29static 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
41static 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 */
58static 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
99hit:
100 sock_hold(sk);
101 read_unlock(&head->lock);
102 return sk;
103}
104
105extern 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
110static 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
22extern struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, 125extern 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/
23obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o 23obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
24 24
25obj-y += exthdrs_core.o 25obj-y += exthdrs_core.o
26
27obj-$(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
24struct 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
65EXPORT_SYMBOL_GPL(inet6_lookup_listener);
66
67struct 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
81EXPORT_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);
75static struct tcp_func ipv6_mapped; 76static struct tcp_func ipv6_mapped;
76static struct tcp_func ipv6_specific; 77static struct tcp_func ipv6_specific;
77 78
78/* I have no idea if this is a good hash for v6 or not. -DaveM */
79static 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
91static 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
102static inline int tcp_v6_bind_conflict(const struct sock *sk, 79static 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
262static 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
311static 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
352hit:
353 sock_hold(sk);
354 read_unlock(&head->lock);
355 return sk;
356}
357
358
359static 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
374inline 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
388EXPORT_SYMBOL_GPL(inet6_lookup);
389
390
391/* 239/*
392 * Open request hash tables. 240 * Open request hash tables.
393 */ 241 */