aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@mandriva.com>2005-12-14 02:25:44 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-01-03 16:10:56 -0500
commitd8313f5ca2b1f86b7df6c99fc4b3fffa1f84e92b (patch)
tree1ee41d265c7790e4389bf4d123b2b60975ad2967 /net/ipv6
parenta7f5e7f164788a22eb5d3de8e2d3cee1bf58fdca (diff)
[INET6]: Generalise tcp_v6_hash_connect
Renaming it to inet6_hash_connect, making it possible to ditch dccp_v6_hash_connect and share the same code with TCP instead. Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/inet6_hashtables.c183
-rw-r--r--net/ipv6/tcp_ipv6.c173
2 files changed, 182 insertions, 174 deletions
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 01d5f46d4e40..4154f3a8b6cf 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -5,7 +5,8 @@
5 * 5 *
6 * Generic INET6 transport hashtables 6 * Generic INET6 transport hashtables
7 * 7 *
8 * Authors: Lotsa people, from code originally in tcp 8 * Authors: Lotsa people, from code originally in tcp, generalised here
9 * by Arnaldo Carvalho de Melo <acme@mandriva.com>
9 * 10 *
10 * This program is free software; you can redistribute it and/or 11 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License 12 * modify it under the terms of the GNU General Public License
@@ -14,12 +15,13 @@
14 */ 15 */
15 16
16#include <linux/config.h> 17#include <linux/config.h>
17
18#include <linux/module.h> 18#include <linux/module.h>
19#include <linux/random.h>
19 20
20#include <net/inet_connection_sock.h> 21#include <net/inet_connection_sock.h>
21#include <net/inet_hashtables.h> 22#include <net/inet_hashtables.h>
22#include <net/inet6_hashtables.h> 23#include <net/inet6_hashtables.h>
24#include <net/ip.h>
23 25
24struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo, 26struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
25 const struct in6_addr *daddr, 27 const struct in6_addr *daddr,
@@ -79,3 +81,180 @@ struct sock *inet6_lookup(struct inet_hashinfo *hashinfo,
79} 81}
80 82
81EXPORT_SYMBOL_GPL(inet6_lookup); 83EXPORT_SYMBOL_GPL(inet6_lookup);
84
85static int __inet6_check_established(struct inet_timewait_death_row *death_row,
86 struct sock *sk, const __u16 lport,
87 struct inet_timewait_sock **twp)
88{
89 struct inet_hashinfo *hinfo = death_row->hashinfo;
90 const struct inet_sock *inet = inet_sk(sk);
91 const struct ipv6_pinfo *np = inet6_sk(sk);
92 const struct in6_addr *daddr = &np->rcv_saddr;
93 const struct in6_addr *saddr = &np->daddr;
94 const int dif = sk->sk_bound_dev_if;
95 const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
96 const unsigned int hash = inet6_ehashfn(daddr, inet->num, saddr,
97 inet->dport);
98 struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
99 struct sock *sk2;
100 const struct hlist_node *node;
101 struct inet_timewait_sock *tw;
102
103 prefetch(head->chain.first);
104 write_lock(&head->lock);
105
106 /* Check TIME-WAIT sockets first. */
107 sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) {
108 const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
109
110 tw = inet_twsk(sk2);
111
112 if(*((__u32 *)&(tw->tw_dport)) == ports &&
113 sk2->sk_family == PF_INET6 &&
114 ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
115 ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
116 sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
117 if (twsk_unique(sk, sk2, twp))
118 goto unique;
119 else
120 goto not_unique;
121 }
122 }
123 tw = NULL;
124
125 /* And established part... */
126 sk_for_each(sk2, node, &head->chain) {
127 if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
128 goto not_unique;
129 }
130
131unique:
132 BUG_TRAP(sk_unhashed(sk));
133 __sk_add_node(sk, &head->chain);
134 sk->sk_hash = hash;
135 sock_prot_inc_use(sk->sk_prot);
136 write_unlock(&head->lock);
137
138 if (twp != NULL) {
139 *twp = tw;
140 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
141 } else if (tw != NULL) {
142 /* Silly. Should hash-dance instead... */
143 inet_twsk_deschedule(tw, death_row);
144 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
145
146 inet_twsk_put(tw);
147 }
148 return 0;
149
150not_unique:
151 write_unlock(&head->lock);
152 return -EADDRNOTAVAIL;
153}
154
155static inline u32 inet6_sk_port_offset(const struct sock *sk)
156{
157 const struct inet_sock *inet = inet_sk(sk);
158 const struct ipv6_pinfo *np = inet6_sk(sk);
159 return secure_ipv6_port_ephemeral(np->rcv_saddr.s6_addr32,
160 np->daddr.s6_addr32,
161 inet->dport);
162}
163
164int inet6_hash_connect(struct inet_timewait_death_row *death_row,
165 struct sock *sk)
166{
167 struct inet_hashinfo *hinfo = death_row->hashinfo;
168 const unsigned short snum = inet_sk(sk)->num;
169 struct inet_bind_hashbucket *head;
170 struct inet_bind_bucket *tb;
171 int ret;
172
173 if (snum == 0) {
174 const int low = sysctl_local_port_range[0];
175 const int high = sysctl_local_port_range[1];
176 const int range = high - low;
177 int i, port;
178 static u32 hint;
179 const u32 offset = hint + inet6_sk_port_offset(sk);
180 struct hlist_node *node;
181 struct inet_timewait_sock *tw = NULL;
182
183 local_bh_disable();
184 for (i = 1; i <= range; i++) {
185 port = low + (i + offset) % range;
186 head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
187 spin_lock(&head->lock);
188
189 /* Does not bother with rcv_saddr checks,
190 * because the established check is already
191 * unique enough.
192 */
193 inet_bind_bucket_for_each(tb, node, &head->chain) {
194 if (tb->port == port) {
195 BUG_TRAP(!hlist_empty(&tb->owners));
196 if (tb->fastreuse >= 0)
197 goto next_port;
198 if (!__inet6_check_established(death_row,
199 sk, port,
200 &tw))
201 goto ok;
202 goto next_port;
203 }
204 }
205
206 tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
207 head, port);
208 if (!tb) {
209 spin_unlock(&head->lock);
210 break;
211 }
212 tb->fastreuse = -1;
213 goto ok;
214
215 next_port:
216 spin_unlock(&head->lock);
217 }
218 local_bh_enable();
219
220 return -EADDRNOTAVAIL;
221
222ok:
223 hint += i;
224
225 /* Head lock still held and bh's disabled */
226 inet_bind_hash(sk, tb, port);
227 if (sk_unhashed(sk)) {
228 inet_sk(sk)->sport = htons(port);
229 __inet6_hash(hinfo, sk);
230 }
231 spin_unlock(&head->lock);
232
233 if (tw) {
234 inet_twsk_deschedule(tw, death_row);
235 inet_twsk_put(tw);
236 }
237
238 ret = 0;
239 goto out;
240 }
241
242 head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
243 tb = inet_csk(sk)->icsk_bind_hash;
244 spin_lock_bh(&head->lock);
245
246 if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) {
247 __inet6_hash(hinfo, sk);
248 spin_unlock_bh(&head->lock);
249 return 0;
250 } else {
251 spin_unlock(&head->lock);
252 /* No definite answer... Walk to established hash table */
253 ret = __inet6_check_established(death_row, sk, snum, NULL);
254out:
255 local_bh_enable();
256 return ret;
257 }
258}
259
260EXPORT_SYMBOL_GPL(inet6_hash_connect);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 514b57bb80b7..a682eb9093e1 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -119,177 +119,6 @@ static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
119 } 119 }
120} 120}
121 121
122static int __tcp_v6_check_established(struct sock *sk, const __u16 lport,
123 struct inet_timewait_sock **twp)
124{
125 struct inet_sock *inet = inet_sk(sk);
126 const struct ipv6_pinfo *np = inet6_sk(sk);
127 const struct in6_addr *daddr = &np->rcv_saddr;
128 const struct in6_addr *saddr = &np->daddr;
129 const int dif = sk->sk_bound_dev_if;
130 const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
131 unsigned int hash = inet6_ehashfn(daddr, inet->num, saddr, inet->dport);
132 struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash);
133 struct sock *sk2;
134 const struct hlist_node *node;
135 struct inet_timewait_sock *tw;
136
137 prefetch(head->chain.first);
138 write_lock(&head->lock);
139
140 /* Check TIME-WAIT sockets first. */
141 sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
142 const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
143
144 tw = inet_twsk(sk2);
145
146 if(*((__u32 *)&(tw->tw_dport)) == ports &&
147 sk2->sk_family == PF_INET6 &&
148 ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
149 ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
150 sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
151 if (twsk_unique(sk, sk2, twp))
152 goto unique;
153 else
154 goto not_unique;
155 }
156 }
157 tw = NULL;
158
159 /* And established part... */
160 sk_for_each(sk2, node, &head->chain) {
161 if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
162 goto not_unique;
163 }
164
165unique:
166 BUG_TRAP(sk_unhashed(sk));
167 __sk_add_node(sk, &head->chain);
168 sk->sk_hash = hash;
169 sock_prot_inc_use(sk->sk_prot);
170 write_unlock(&head->lock);
171
172 if (twp) {
173 *twp = tw;
174 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
175 } else if (tw) {
176 /* Silly. Should hash-dance instead... */
177 inet_twsk_deschedule(tw, &tcp_death_row);
178 NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
179
180 inet_twsk_put(tw);
181 }
182 return 0;
183
184not_unique:
185 write_unlock(&head->lock);
186 return -EADDRNOTAVAIL;
187}
188
189static inline u32 tcpv6_port_offset(const struct sock *sk)
190{
191 const struct inet_sock *inet = inet_sk(sk);
192 const struct ipv6_pinfo *np = inet6_sk(sk);
193
194 return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
195 np->daddr.s6_addr32,
196 inet->dport);
197}
198
199static int tcp_v6_hash_connect(struct sock *sk)
200{
201 unsigned short snum = inet_sk(sk)->num;
202 struct inet_bind_hashbucket *head;
203 struct inet_bind_bucket *tb;
204 int ret;
205
206 if (!snum) {
207 int low = sysctl_local_port_range[0];
208 int high = sysctl_local_port_range[1];
209 int range = high - low;
210 int i;
211 int port;
212 static u32 hint;
213 u32 offset = hint + tcpv6_port_offset(sk);
214 struct hlist_node *node;
215 struct inet_timewait_sock *tw = NULL;
216
217 local_bh_disable();
218 for (i = 1; i <= range; i++) {
219 port = low + (i + offset) % range;
220 head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
221 spin_lock(&head->lock);
222
223 /* Does not bother with rcv_saddr checks,
224 * because the established check is already
225 * unique enough.
226 */
227 inet_bind_bucket_for_each(tb, node, &head->chain) {
228 if (tb->port == port) {
229 BUG_TRAP(!hlist_empty(&tb->owners));
230 if (tb->fastreuse >= 0)
231 goto next_port;
232 if (!__tcp_v6_check_established(sk,
233 port,
234 &tw))
235 goto ok;
236 goto next_port;
237 }
238 }
239
240 tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
241 if (!tb) {
242 spin_unlock(&head->lock);
243 break;
244 }
245 tb->fastreuse = -1;
246 goto ok;
247
248 next_port:
249 spin_unlock(&head->lock);
250 }
251 local_bh_enable();
252
253 return -EADDRNOTAVAIL;
254
255ok:
256 hint += i;
257
258 /* Head lock still held and bh's disabled */
259 inet_bind_hash(sk, tb, port);
260 if (sk_unhashed(sk)) {
261 inet_sk(sk)->sport = htons(port);
262 __inet6_hash(&tcp_hashinfo, sk);
263 }
264 spin_unlock(&head->lock);
265
266 if (tw) {
267 inet_twsk_deschedule(tw, &tcp_death_row);
268 inet_twsk_put(tw);
269 }
270
271 ret = 0;
272 goto out;
273 }
274
275 head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
276 tb = inet_csk(sk)->icsk_bind_hash;
277 spin_lock_bh(&head->lock);
278
279 if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
280 __inet6_hash(&tcp_hashinfo, sk);
281 spin_unlock_bh(&head->lock);
282 return 0;
283 } else {
284 spin_unlock(&head->lock);
285 /* No definite answer... Walk to established hash table */
286 ret = __tcp_v6_check_established(sk, snum, NULL);
287out:
288 local_bh_enable();
289 return ret;
290 }
291}
292
293static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, 122static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
294 int addr_len) 123 int addr_len)
295{ 124{
@@ -450,7 +279,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
450 inet->dport = usin->sin6_port; 279 inet->dport = usin->sin6_port;
451 280
452 tcp_set_state(sk, TCP_SYN_SENT); 281 tcp_set_state(sk, TCP_SYN_SENT);
453 err = tcp_v6_hash_connect(sk); 282 err = inet6_hash_connect(&tcp_death_row, sk);
454 if (err) 283 if (err)
455 goto late_failure; 284 goto late_failure;
456 285