diff options
Diffstat (limited to 'net/sctp/bind_addr.c')
-rw-r--r-- | net/sctp/bind_addr.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c new file mode 100644 index 000000000000..f90eadfb60a2 --- /dev/null +++ b/net/sctp/bind_addr.c | |||
@@ -0,0 +1,417 @@ | |||
1 | /* SCTP kernel reference Implementation | ||
2 | * (C) Copyright IBM Corp. 2001, 2003 | ||
3 | * Copyright (c) Cisco 1999,2000 | ||
4 | * Copyright (c) Motorola 1999,2000,2001 | ||
5 | * Copyright (c) La Monte H.P. Yarroll 2001 | ||
6 | * | ||
7 | * This file is part of the SCTP kernel reference implementation. | ||
8 | * | ||
9 | * A collection class to handle the storage of transport addresses. | ||
10 | * | ||
11 | * The SCTP reference implementation is free software; | ||
12 | * you can redistribute it and/or modify it under the terms of | ||
13 | * the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2, or (at your option) | ||
15 | * any later version. | ||
16 | * | ||
17 | * The SCTP reference implementation is distributed in the hope that it | ||
18 | * will be useful, but WITHOUT ANY WARRANTY; without even the implied | ||
19 | * ************************ | ||
20 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
21 | * See the GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with GNU CC; see the file COPYING. If not, write to | ||
25 | * the Free Software Foundation, 59 Temple Place - Suite 330, | ||
26 | * Boston, MA 02111-1307, USA. | ||
27 | * | ||
28 | * Please send any bug reports or fixes you make to the | ||
29 | * email address(es): | ||
30 | * lksctp developers <lksctp-developers@lists.sourceforge.net> | ||
31 | * | ||
32 | * Or submit a bug report through the following website: | ||
33 | * http://www.sf.net/projects/lksctp | ||
34 | * | ||
35 | * Written or modified by: | ||
36 | * La Monte H.P. Yarroll <piggy@acm.org> | ||
37 | * Karl Knutson <karl@athena.chicago.il.us> | ||
38 | * Jon Grimm <jgrimm@us.ibm.com> | ||
39 | * Daisy Chang <daisyc@us.ibm.com> | ||
40 | * | ||
41 | * Any bugs reported given to us we will try to fix... any fixes shared will | ||
42 | * be incorporated into the next SCTP release. | ||
43 | */ | ||
44 | |||
45 | #include <linux/types.h> | ||
46 | #include <linux/sched.h> | ||
47 | #include <linux/in.h> | ||
48 | #include <net/sock.h> | ||
49 | #include <net/ipv6.h> | ||
50 | #include <net/if_inet6.h> | ||
51 | #include <net/sctp/sctp.h> | ||
52 | #include <net/sctp/sm.h> | ||
53 | |||
54 | /* Forward declarations for internal helpers. */ | ||
55 | static int sctp_copy_one_addr(struct sctp_bind_addr *, union sctp_addr *, | ||
56 | sctp_scope_t scope, int gfp, int flags); | ||
57 | static void sctp_bind_addr_clean(struct sctp_bind_addr *); | ||
58 | |||
59 | /* First Level Abstractions. */ | ||
60 | |||
61 | /* Copy 'src' to 'dest' taking 'scope' into account. Omit addresses | ||
62 | * in 'src' which have a broader scope than 'scope'. | ||
63 | */ | ||
64 | int sctp_bind_addr_copy(struct sctp_bind_addr *dest, | ||
65 | const struct sctp_bind_addr *src, | ||
66 | sctp_scope_t scope, int gfp, int flags) | ||
67 | { | ||
68 | struct sctp_sockaddr_entry *addr; | ||
69 | struct list_head *pos; | ||
70 | int error = 0; | ||
71 | |||
72 | /* All addresses share the same port. */ | ||
73 | dest->port = src->port; | ||
74 | |||
75 | /* Extract the addresses which are relevant for this scope. */ | ||
76 | list_for_each(pos, &src->address_list) { | ||
77 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
78 | error = sctp_copy_one_addr(dest, &addr->a, scope, | ||
79 | gfp, flags); | ||
80 | if (error < 0) | ||
81 | goto out; | ||
82 | } | ||
83 | |||
84 | /* If there are no addresses matching the scope and | ||
85 | * this is global scope, try to get a link scope address, with | ||
86 | * the assumption that we must be sitting behind a NAT. | ||
87 | */ | ||
88 | if (list_empty(&dest->address_list) && (SCTP_SCOPE_GLOBAL == scope)) { | ||
89 | list_for_each(pos, &src->address_list) { | ||
90 | addr = list_entry(pos, struct sctp_sockaddr_entry, | ||
91 | list); | ||
92 | error = sctp_copy_one_addr(dest, &addr->a, | ||
93 | SCTP_SCOPE_LINK, gfp, | ||
94 | flags); | ||
95 | if (error < 0) | ||
96 | goto out; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | out: | ||
101 | if (error) | ||
102 | sctp_bind_addr_clean(dest); | ||
103 | |||
104 | return error; | ||
105 | } | ||
106 | |||
107 | /* Initialize the SCTP_bind_addr structure for either an endpoint or | ||
108 | * an association. | ||
109 | */ | ||
110 | void sctp_bind_addr_init(struct sctp_bind_addr *bp, __u16 port) | ||
111 | { | ||
112 | bp->malloced = 0; | ||
113 | |||
114 | INIT_LIST_HEAD(&bp->address_list); | ||
115 | bp->port = port; | ||
116 | } | ||
117 | |||
118 | /* Dispose of the address list. */ | ||
119 | static void sctp_bind_addr_clean(struct sctp_bind_addr *bp) | ||
120 | { | ||
121 | struct sctp_sockaddr_entry *addr; | ||
122 | struct list_head *pos, *temp; | ||
123 | |||
124 | /* Empty the bind address list. */ | ||
125 | list_for_each_safe(pos, temp, &bp->address_list) { | ||
126 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
127 | list_del(pos); | ||
128 | kfree(addr); | ||
129 | SCTP_DBG_OBJCNT_DEC(addr); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | /* Dispose of an SCTP_bind_addr structure */ | ||
134 | void sctp_bind_addr_free(struct sctp_bind_addr *bp) | ||
135 | { | ||
136 | /* Empty the bind address list. */ | ||
137 | sctp_bind_addr_clean(bp); | ||
138 | |||
139 | if (bp->malloced) { | ||
140 | kfree(bp); | ||
141 | SCTP_DBG_OBJCNT_DEC(bind_addr); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /* Add an address to the bind address list in the SCTP_bind_addr structure. */ | ||
146 | int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, | ||
147 | int gfp) | ||
148 | { | ||
149 | struct sctp_sockaddr_entry *addr; | ||
150 | |||
151 | /* Add the address to the bind address list. */ | ||
152 | addr = t_new(struct sctp_sockaddr_entry, gfp); | ||
153 | if (!addr) | ||
154 | return -ENOMEM; | ||
155 | |||
156 | memcpy(&addr->a, new, sizeof(*new)); | ||
157 | |||
158 | /* Fix up the port if it has not yet been set. | ||
159 | * Both v4 and v6 have the port at the same offset. | ||
160 | */ | ||
161 | if (!addr->a.v4.sin_port) | ||
162 | addr->a.v4.sin_port = bp->port; | ||
163 | |||
164 | INIT_LIST_HEAD(&addr->list); | ||
165 | list_add_tail(&addr->list, &bp->address_list); | ||
166 | SCTP_DBG_OBJCNT_INC(addr); | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | /* Delete an address from the bind address list in the SCTP_bind_addr | ||
172 | * structure. | ||
173 | */ | ||
174 | int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr) | ||
175 | { | ||
176 | struct list_head *pos, *temp; | ||
177 | struct sctp_sockaddr_entry *addr; | ||
178 | |||
179 | list_for_each_safe(pos, temp, &bp->address_list) { | ||
180 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
181 | if (sctp_cmp_addr_exact(&addr->a, del_addr)) { | ||
182 | /* Found the exact match. */ | ||
183 | list_del(pos); | ||
184 | kfree(addr); | ||
185 | SCTP_DBG_OBJCNT_DEC(addr); | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | return -EINVAL; | ||
192 | } | ||
193 | |||
194 | /* Create a network byte-order representation of all the addresses | ||
195 | * formated as SCTP parameters. | ||
196 | * | ||
197 | * The second argument is the return value for the length. | ||
198 | */ | ||
199 | union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp, | ||
200 | int *addrs_len, int gfp) | ||
201 | { | ||
202 | union sctp_params addrparms; | ||
203 | union sctp_params retval; | ||
204 | int addrparms_len; | ||
205 | union sctp_addr_param rawaddr; | ||
206 | int len; | ||
207 | struct sctp_sockaddr_entry *addr; | ||
208 | struct list_head *pos; | ||
209 | struct sctp_af *af; | ||
210 | |||
211 | addrparms_len = 0; | ||
212 | len = 0; | ||
213 | |||
214 | /* Allocate enough memory at once. */ | ||
215 | list_for_each(pos, &bp->address_list) { | ||
216 | len += sizeof(union sctp_addr_param); | ||
217 | } | ||
218 | |||
219 | /* Don't even bother embedding an address if there | ||
220 | * is only one. | ||
221 | */ | ||
222 | if (len == sizeof(union sctp_addr_param)) { | ||
223 | retval.v = NULL; | ||
224 | goto end_raw; | ||
225 | } | ||
226 | |||
227 | retval.v = kmalloc(len, gfp); | ||
228 | if (!retval.v) | ||
229 | goto end_raw; | ||
230 | |||
231 | addrparms = retval; | ||
232 | |||
233 | list_for_each(pos, &bp->address_list) { | ||
234 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
235 | af = sctp_get_af_specific(addr->a.v4.sin_family); | ||
236 | len = af->to_addr_param(&addr->a, &rawaddr); | ||
237 | memcpy(addrparms.v, &rawaddr, len); | ||
238 | addrparms.v += len; | ||
239 | addrparms_len += len; | ||
240 | } | ||
241 | |||
242 | end_raw: | ||
243 | *addrs_len = addrparms_len; | ||
244 | return retval; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Create an address list out of the raw address list format (IPv4 and IPv6 | ||
249 | * address parameters). | ||
250 | */ | ||
251 | int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw_addr_list, | ||
252 | int addrs_len, __u16 port, int gfp) | ||
253 | { | ||
254 | union sctp_addr_param *rawaddr; | ||
255 | struct sctp_paramhdr *param; | ||
256 | union sctp_addr addr; | ||
257 | int retval = 0; | ||
258 | int len; | ||
259 | struct sctp_af *af; | ||
260 | |||
261 | /* Convert the raw address to standard address format */ | ||
262 | while (addrs_len) { | ||
263 | param = (struct sctp_paramhdr *)raw_addr_list; | ||
264 | rawaddr = (union sctp_addr_param *)raw_addr_list; | ||
265 | |||
266 | af = sctp_get_af_specific(param_type2af(param->type)); | ||
267 | if (unlikely(!af)) { | ||
268 | retval = -EINVAL; | ||
269 | sctp_bind_addr_clean(bp); | ||
270 | break; | ||
271 | } | ||
272 | |||
273 | af->from_addr_param(&addr, rawaddr, port, 0); | ||
274 | retval = sctp_add_bind_addr(bp, &addr, gfp); | ||
275 | if (retval) { | ||
276 | /* Can't finish building the list, clean up. */ | ||
277 | sctp_bind_addr_clean(bp); | ||
278 | break; | ||
279 | } | ||
280 | |||
281 | len = ntohs(param->length); | ||
282 | addrs_len -= len; | ||
283 | raw_addr_list += len; | ||
284 | } | ||
285 | |||
286 | return retval; | ||
287 | } | ||
288 | |||
289 | /******************************************************************** | ||
290 | * 2nd Level Abstractions | ||
291 | ********************************************************************/ | ||
292 | |||
293 | /* Does this contain a specified address? Allow wildcarding. */ | ||
294 | int sctp_bind_addr_match(struct sctp_bind_addr *bp, | ||
295 | const union sctp_addr *addr, | ||
296 | struct sctp_sock *opt) | ||
297 | { | ||
298 | struct sctp_sockaddr_entry *laddr; | ||
299 | struct list_head *pos; | ||
300 | |||
301 | list_for_each(pos, &bp->address_list) { | ||
302 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
303 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) | ||
304 | return 1; | ||
305 | } | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | /* Find the first address in the bind address list that is not present in | ||
311 | * the addrs packed array. | ||
312 | */ | ||
313 | union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, | ||
314 | const union sctp_addr *addrs, | ||
315 | int addrcnt, | ||
316 | struct sctp_sock *opt) | ||
317 | { | ||
318 | struct sctp_sockaddr_entry *laddr; | ||
319 | union sctp_addr *addr; | ||
320 | void *addr_buf; | ||
321 | struct sctp_af *af; | ||
322 | struct list_head *pos; | ||
323 | int i; | ||
324 | |||
325 | list_for_each(pos, &bp->address_list) { | ||
326 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
327 | |||
328 | addr_buf = (union sctp_addr *)addrs; | ||
329 | for (i = 0; i < addrcnt; i++) { | ||
330 | addr = (union sctp_addr *)addr_buf; | ||
331 | af = sctp_get_af_specific(addr->v4.sin_family); | ||
332 | if (!af) | ||
333 | return NULL; | ||
334 | |||
335 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) | ||
336 | break; | ||
337 | |||
338 | addr_buf += af->sockaddr_len; | ||
339 | } | ||
340 | if (i == addrcnt) | ||
341 | return &laddr->a; | ||
342 | } | ||
343 | |||
344 | return NULL; | ||
345 | } | ||
346 | |||
347 | /* Copy out addresses from the global local address list. */ | ||
348 | static int sctp_copy_one_addr(struct sctp_bind_addr *dest, | ||
349 | union sctp_addr *addr, | ||
350 | sctp_scope_t scope, int gfp, int flags) | ||
351 | { | ||
352 | int error = 0; | ||
353 | |||
354 | if (sctp_is_any(addr)) { | ||
355 | error = sctp_copy_local_addr_list(dest, scope, gfp, flags); | ||
356 | } else if (sctp_in_scope(addr, scope)) { | ||
357 | /* Now that the address is in scope, check to see if | ||
358 | * the address type is supported by local sock as | ||
359 | * well as the remote peer. | ||
360 | */ | ||
361 | if ((((AF_INET == addr->sa.sa_family) && | ||
362 | (flags & SCTP_ADDR4_PEERSUPP))) || | ||
363 | (((AF_INET6 == addr->sa.sa_family) && | ||
364 | (flags & SCTP_ADDR6_ALLOWED) && | ||
365 | (flags & SCTP_ADDR6_PEERSUPP)))) | ||
366 | error = sctp_add_bind_addr(dest, addr, gfp); | ||
367 | } | ||
368 | |||
369 | return error; | ||
370 | } | ||
371 | |||
372 | /* Is this a wildcard address? */ | ||
373 | int sctp_is_any(const union sctp_addr *addr) | ||
374 | { | ||
375 | struct sctp_af *af = sctp_get_af_specific(addr->sa.sa_family); | ||
376 | if (!af) | ||
377 | return 0; | ||
378 | return af->is_any(addr); | ||
379 | } | ||
380 | |||
381 | /* Is 'addr' valid for 'scope'? */ | ||
382 | int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope) | ||
383 | { | ||
384 | sctp_scope_t addr_scope = sctp_scope(addr); | ||
385 | |||
386 | /* The unusable SCTP addresses will not be considered with | ||
387 | * any defined scopes. | ||
388 | */ | ||
389 | if (SCTP_SCOPE_UNUSABLE == addr_scope) | ||
390 | return 0; | ||
391 | /* | ||
392 | * For INIT and INIT-ACK address list, let L be the level of | ||
393 | * of requested destination address, sender and receiver | ||
394 | * SHOULD include all of its addresses with level greater | ||
395 | * than or equal to L. | ||
396 | */ | ||
397 | if (addr_scope <= scope) | ||
398 | return 1; | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | /******************************************************************** | ||
404 | * 3rd Level Abstractions | ||
405 | ********************************************************************/ | ||
406 | |||
407 | /* What is the scope of 'addr'? */ | ||
408 | sctp_scope_t sctp_scope(const union sctp_addr *addr) | ||
409 | { | ||
410 | struct sctp_af *af; | ||
411 | |||
412 | af = sctp_get_af_specific(addr->sa.sa_family); | ||
413 | if (!af) | ||
414 | return SCTP_SCOPE_UNUSABLE; | ||
415 | |||
416 | return af->scope((union sctp_addr *)addr); | ||
417 | } | ||