diff options
Diffstat (limited to 'net/ipv6/inet6_hashtables.c')
-rw-r--r-- | net/ipv6/inet6_hashtables.c | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index bb8ffb8a14c5..2ae84c961678 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c | |||
@@ -23,6 +23,86 @@ | |||
23 | #include <net/inet6_hashtables.h> | 23 | #include <net/inet6_hashtables.h> |
24 | #include <net/ip.h> | 24 | #include <net/ip.h> |
25 | 25 | ||
26 | void __inet6_hash(struct inet_hashinfo *hashinfo, | ||
27 | struct sock *sk) | ||
28 | { | ||
29 | struct hlist_head *list; | ||
30 | rwlock_t *lock; | ||
31 | |||
32 | BUG_TRAP(sk_unhashed(sk)); | ||
33 | |||
34 | if (sk->sk_state == TCP_LISTEN) { | ||
35 | list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)]; | ||
36 | lock = &hashinfo->lhash_lock; | ||
37 | inet_listen_wlock(hashinfo); | ||
38 | } else { | ||
39 | unsigned int hash; | ||
40 | sk->sk_hash = hash = inet6_sk_ehashfn(sk); | ||
41 | hash &= (hashinfo->ehash_size - 1); | ||
42 | list = &hashinfo->ehash[hash].chain; | ||
43 | lock = &hashinfo->ehash[hash].lock; | ||
44 | write_lock(lock); | ||
45 | } | ||
46 | |||
47 | __sk_add_node(sk, list); | ||
48 | sock_prot_inc_use(sk->sk_prot); | ||
49 | write_unlock(lock); | ||
50 | } | ||
51 | EXPORT_SYMBOL(__inet6_hash); | ||
52 | |||
53 | /* | ||
54 | * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so | ||
55 | * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM | ||
56 | * | ||
57 | * The sockhash lock must be held as a reader here. | ||
58 | */ | ||
59 | struct sock *__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 | unsigned int hash = inet6_ehashfn(daddr, hnum, saddr, sport); | ||
73 | struct inet_ehash_bucket *head = inet_ehash_bucket(hashinfo, hash); | ||
74 | |||
75 | prefetch(head->chain.first); | ||
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, hash, 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 inet6_timewait_sock *tw6 = inet6_twsk(sk); | ||
89 | |||
90 | if (ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) && | ||
91 | ipv6_addr_equal(&tw6->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 | EXPORT_SYMBOL(__inet6_lookup_established); | ||
105 | |||
26 | struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, | 106 | struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, |
27 | const struct in6_addr *daddr, | 107 | const struct in6_addr *daddr, |
28 | const unsigned short hnum, const int dif) | 108 | const unsigned short hnum, const int dif) |