diff options
Diffstat (limited to 'net/ipv6/inet6_hashtables.c')
-rw-r--r-- | net/ipv6/inet6_hashtables.c | 135 |
1 files changed, 17 insertions, 118 deletions
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index a66a7d8e2811..d325a9958909 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c | |||
@@ -54,7 +54,8 @@ EXPORT_SYMBOL(__inet6_hash); | |||
54 | * | 54 | * |
55 | * The sockhash lock must be held as a reader here. | 55 | * The sockhash lock must be held as a reader here. |
56 | */ | 56 | */ |
57 | struct sock *__inet6_lookup_established(struct inet_hashinfo *hashinfo, | 57 | struct sock *__inet6_lookup_established(struct net *net, |
58 | struct inet_hashinfo *hashinfo, | ||
58 | const struct in6_addr *saddr, | 59 | const struct in6_addr *saddr, |
59 | const __be16 sport, | 60 | const __be16 sport, |
60 | const struct in6_addr *daddr, | 61 | const struct in6_addr *daddr, |
@@ -75,22 +76,13 @@ struct sock *__inet6_lookup_established(struct inet_hashinfo *hashinfo, | |||
75 | read_lock(lock); | 76 | read_lock(lock); |
76 | sk_for_each(sk, node, &head->chain) { | 77 | sk_for_each(sk, node, &head->chain) { |
77 | /* For IPV6 do the cheaper port and family tests first. */ | 78 | /* For IPV6 do the cheaper port and family tests first. */ |
78 | if (INET6_MATCH(sk, hash, saddr, daddr, ports, dif)) | 79 | if (INET6_MATCH(sk, net, hash, saddr, daddr, ports, dif)) |
79 | goto hit; /* You sunk my battleship! */ | 80 | goto hit; /* You sunk my battleship! */ |
80 | } | 81 | } |
81 | /* Must check for a TIME_WAIT'er before going to listener hash. */ | 82 | /* Must check for a TIME_WAIT'er before going to listener hash. */ |
82 | sk_for_each(sk, node, &head->twchain) { | 83 | sk_for_each(sk, node, &head->twchain) { |
83 | const struct inet_timewait_sock *tw = inet_twsk(sk); | 84 | if (INET6_TW_MATCH(sk, net, hash, saddr, daddr, ports, dif)) |
84 | 85 | goto hit; | |
85 | if(*((__portpair *)&(tw->tw_dport)) == ports && | ||
86 | sk->sk_family == PF_INET6) { | ||
87 | const struct inet6_timewait_sock *tw6 = inet6_twsk(sk); | ||
88 | |||
89 | if (ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) && | ||
90 | ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) && | ||
91 | (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif)) | ||
92 | goto hit; | ||
93 | } | ||
94 | } | 86 | } |
95 | read_unlock(lock); | 87 | read_unlock(lock); |
96 | return NULL; | 88 | return NULL; |
@@ -102,9 +94,9 @@ hit: | |||
102 | } | 94 | } |
103 | EXPORT_SYMBOL(__inet6_lookup_established); | 95 | EXPORT_SYMBOL(__inet6_lookup_established); |
104 | 96 | ||
105 | struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, | 97 | struct sock *inet6_lookup_listener(struct net *net, |
106 | const struct in6_addr *daddr, | 98 | struct inet_hashinfo *hashinfo, const struct in6_addr *daddr, |
107 | const unsigned short hnum, const int dif) | 99 | const unsigned short hnum, const int dif) |
108 | { | 100 | { |
109 | struct sock *sk; | 101 | struct sock *sk; |
110 | const struct hlist_node *node; | 102 | const struct hlist_node *node; |
@@ -113,7 +105,8 @@ struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, | |||
113 | 105 | ||
114 | read_lock(&hashinfo->lhash_lock); | 106 | read_lock(&hashinfo->lhash_lock); |
115 | sk_for_each(sk, node, &hashinfo->listening_hash[inet_lhashfn(hnum)]) { | 107 | sk_for_each(sk, node, &hashinfo->listening_hash[inet_lhashfn(hnum)]) { |
116 | if (inet_sk(sk)->num == hnum && sk->sk_family == PF_INET6) { | 108 | if (sk->sk_net == net && inet_sk(sk)->num == hnum && |
109 | sk->sk_family == PF_INET6) { | ||
117 | const struct ipv6_pinfo *np = inet6_sk(sk); | 110 | const struct ipv6_pinfo *np = inet6_sk(sk); |
118 | 111 | ||
119 | score = 1; | 112 | score = 1; |
@@ -145,7 +138,7 @@ struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, | |||
145 | 138 | ||
146 | EXPORT_SYMBOL_GPL(inet6_lookup_listener); | 139 | EXPORT_SYMBOL_GPL(inet6_lookup_listener); |
147 | 140 | ||
148 | struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, | 141 | struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, |
149 | const struct in6_addr *saddr, const __be16 sport, | 142 | const struct in6_addr *saddr, const __be16 sport, |
150 | const struct in6_addr *daddr, const __be16 dport, | 143 | const struct in6_addr *daddr, const __be16 dport, |
151 | const int dif) | 144 | const int dif) |
@@ -153,7 +146,7 @@ struct sock *inet6_lookup(struct inet_hashinfo *hashinfo, | |||
153 | struct sock *sk; | 146 | struct sock *sk; |
154 | 147 | ||
155 | local_bh_disable(); | 148 | local_bh_disable(); |
156 | sk = __inet6_lookup(hashinfo, saddr, sport, daddr, ntohs(dport), dif); | 149 | sk = __inet6_lookup(net, hashinfo, saddr, sport, daddr, ntohs(dport), dif); |
157 | local_bh_enable(); | 150 | local_bh_enable(); |
158 | 151 | ||
159 | return sk; | 152 | return sk; |
@@ -179,21 +172,16 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row, | |||
179 | struct sock *sk2; | 172 | struct sock *sk2; |
180 | const struct hlist_node *node; | 173 | const struct hlist_node *node; |
181 | struct inet_timewait_sock *tw; | 174 | struct inet_timewait_sock *tw; |
175 | struct net *net = sk->sk_net; | ||
182 | 176 | ||
183 | prefetch(head->chain.first); | 177 | prefetch(head->chain.first); |
184 | write_lock(lock); | 178 | write_lock(lock); |
185 | 179 | ||
186 | /* Check TIME-WAIT sockets first. */ | 180 | /* Check TIME-WAIT sockets first. */ |
187 | sk_for_each(sk2, node, &head->twchain) { | 181 | sk_for_each(sk2, node, &head->twchain) { |
188 | const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2); | ||
189 | |||
190 | tw = inet_twsk(sk2); | 182 | tw = inet_twsk(sk2); |
191 | 183 | ||
192 | if(*((__portpair *)&(tw->tw_dport)) == ports && | 184 | if (INET6_TW_MATCH(sk2, net, hash, saddr, daddr, ports, dif)) { |
193 | sk2->sk_family == PF_INET6 && | ||
194 | ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) && | ||
195 | ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) && | ||
196 | (!sk2->sk_bound_dev_if || sk2->sk_bound_dev_if == dif)) { | ||
197 | if (twsk_unique(sk, sk2, twp)) | 185 | if (twsk_unique(sk, sk2, twp)) |
198 | goto unique; | 186 | goto unique; |
199 | else | 187 | else |
@@ -204,7 +192,7 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row, | |||
204 | 192 | ||
205 | /* And established part... */ | 193 | /* And established part... */ |
206 | sk_for_each(sk2, node, &head->chain) { | 194 | sk_for_each(sk2, node, &head->chain) { |
207 | if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif)) | 195 | if (INET6_MATCH(sk2, net, hash, saddr, daddr, ports, dif)) |
208 | goto not_unique; | 196 | goto not_unique; |
209 | } | 197 | } |
210 | 198 | ||
@@ -248,97 +236,8 @@ static inline u32 inet6_sk_port_offset(const struct sock *sk) | |||
248 | int inet6_hash_connect(struct inet_timewait_death_row *death_row, | 236 | int inet6_hash_connect(struct inet_timewait_death_row *death_row, |
249 | struct sock *sk) | 237 | struct sock *sk) |
250 | { | 238 | { |
251 | struct inet_hashinfo *hinfo = death_row->hashinfo; | 239 | return __inet_hash_connect(death_row, sk, |
252 | const unsigned short snum = inet_sk(sk)->num; | 240 | __inet6_check_established, __inet6_hash); |
253 | struct inet_bind_hashbucket *head; | ||
254 | struct inet_bind_bucket *tb; | ||
255 | int ret; | ||
256 | |||
257 | if (snum == 0) { | ||
258 | int i, port, low, high, remaining; | ||
259 | static u32 hint; | ||
260 | const u32 offset = hint + inet6_sk_port_offset(sk); | ||
261 | struct hlist_node *node; | ||
262 | struct inet_timewait_sock *tw = NULL; | ||
263 | |||
264 | inet_get_local_port_range(&low, &high); | ||
265 | remaining = (high - low) + 1; | ||
266 | |||
267 | local_bh_disable(); | ||
268 | for (i = 1; i <= remaining; i++) { | ||
269 | port = low + (i + offset) % remaining; | ||
270 | head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)]; | ||
271 | spin_lock(&head->lock); | ||
272 | |||
273 | /* Does not bother with rcv_saddr checks, | ||
274 | * because the established check is already | ||
275 | * unique enough. | ||
276 | */ | ||
277 | inet_bind_bucket_for_each(tb, node, &head->chain) { | ||
278 | if (tb->port == port) { | ||
279 | BUG_TRAP(!hlist_empty(&tb->owners)); | ||
280 | if (tb->fastreuse >= 0) | ||
281 | goto next_port; | ||
282 | if (!__inet6_check_established(death_row, | ||
283 | sk, port, | ||
284 | &tw)) | ||
285 | goto ok; | ||
286 | goto next_port; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, | ||
291 | head, port); | ||
292 | if (!tb) { | ||
293 | spin_unlock(&head->lock); | ||
294 | break; | ||
295 | } | ||
296 | tb->fastreuse = -1; | ||
297 | goto ok; | ||
298 | |||
299 | next_port: | ||
300 | spin_unlock(&head->lock); | ||
301 | } | ||
302 | local_bh_enable(); | ||
303 | |||
304 | return -EADDRNOTAVAIL; | ||
305 | |||
306 | ok: | ||
307 | hint += i; | ||
308 | |||
309 | /* Head lock still held and bh's disabled */ | ||
310 | inet_bind_hash(sk, tb, port); | ||
311 | if (sk_unhashed(sk)) { | ||
312 | inet_sk(sk)->sport = htons(port); | ||
313 | __inet6_hash(hinfo, sk); | ||
314 | } | ||
315 | spin_unlock(&head->lock); | ||
316 | |||
317 | if (tw) { | ||
318 | inet_twsk_deschedule(tw, death_row); | ||
319 | inet_twsk_put(tw); | ||
320 | } | ||
321 | |||
322 | ret = 0; | ||
323 | goto out; | ||
324 | } | ||
325 | |||
326 | head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)]; | ||
327 | tb = inet_csk(sk)->icsk_bind_hash; | ||
328 | spin_lock_bh(&head->lock); | ||
329 | |||
330 | if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) { | ||
331 | __inet6_hash(hinfo, sk); | ||
332 | spin_unlock_bh(&head->lock); | ||
333 | return 0; | ||
334 | } else { | ||
335 | spin_unlock(&head->lock); | ||
336 | /* No definite answer... Walk to established hash table */ | ||
337 | ret = __inet6_check_established(death_row, sk, snum, NULL); | ||
338 | out: | ||
339 | local_bh_enable(); | ||
340 | return ret; | ||
341 | } | ||
342 | } | 241 | } |
343 | 242 | ||
344 | EXPORT_SYMBOL_GPL(inet6_hash_connect); | 243 | EXPORT_SYMBOL_GPL(inet6_hash_connect); |