rthdr.c revision 1.16.16.1 1 /* $NetBSD: rthdr.c,v 1.16.16.1 2008/02/22 02:53:32 keiichi Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: rthdr.c,v 1.16.16.1 2008/02/22 02:53:32 keiichi Exp $");
35 #endif /* LIBC_SCCS and not lint */
36
37 #include "namespace.h"
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41
42 #include <netinet/in.h>
43 #include <netinet/ip6.h>
44
45 #include <assert.h>
46 #include <string.h>
47 #include <stdio.h>
48
49 #ifdef __weak_alias
50 __weak_alias(inet6_rthdr_add,_inet6_rthdr_add)
51 __weak_alias(inet6_rthdr_getaddr,_inet6_rthdr_getaddr)
52 __weak_alias(inet6_rthdr_getflags,_inet6_rthdr_getflags)
53 __weak_alias(inet6_rthdr_init,_inet6_rthdr_init)
54 __weak_alias(inet6_rthdr_lasthop,_inet6_rthdr_lasthop)
55 __weak_alias(inet6_rthdr_segments,_inet6_rthdr_segments)
56 __weak_alias(inet6_rthdr_space,_inet6_rthdr_space)
57 __weak_alias(inet6_rth_space, _inet6_rth_space)
58 __weak_alias(inet6_rth_init, _inet6_rth_init)
59 __weak_alias(inet6_rth_add, _inet6_rth_add)
60 __weak_alias(inet6_rth_reverse, _inet6_rth_reverse)
61 __weak_alias(inet6_rth_segments, _inet6_rth_segments)
62 __weak_alias(inet6_rth_getaddr, _inet6_rth_getaddr)
63 #endif
64
65 /*
66 * RFC2292 API
67 */
68
69 size_t
70 inet6_rthdr_space(type, seg)
71 int type, seg;
72 {
73 switch (type) {
74 case IPV6_RTHDR_TYPE_0:
75 if (seg < 1 || seg > 23)
76 return (0);
77 return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
78 sizeof(struct ip6_rthdr0)));
79 default:
80 return (0);
81 }
82 }
83
84 struct cmsghdr *
85 inet6_rthdr_init(bp, type)
86 void *bp;
87 int type;
88 {
89 struct cmsghdr *ch;
90 struct ip6_rthdr *rthdr;
91
92 _DIAGASSERT(bp != NULL);
93
94 ch = (struct cmsghdr *)bp;
95 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(ch);
96
97 ch->cmsg_level = IPPROTO_IPV6;
98 ch->cmsg_type = IPV6_RTHDR;
99
100 switch (type) {
101 case IPV6_RTHDR_TYPE_0:
102 #ifdef COMPAT_RFC2292
103 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
104 sizeof(struct in6_addr));
105 #else
106 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
107 #endif
108 (void)memset(rthdr, 0, sizeof(struct ip6_rthdr0));
109 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
110 return (ch);
111 default:
112 return (NULL);
113 }
114 }
115
116 int
117 inet6_rthdr_add(cmsg, addr, flags)
118 struct cmsghdr *cmsg;
119 const struct in6_addr *addr;
120 u_int flags;
121 {
122 struct ip6_rthdr *rthdr;
123
124 _DIAGASSERT(cmsg != NULL);
125 _DIAGASSERT(addr != NULL);
126
127 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
128
129 switch (rthdr->ip6r_type) {
130 case IPV6_RTHDR_TYPE_0:
131 {
132 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
133 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
134 return (-1);
135 if (rt0->ip6r0_segleft == 23)
136 return (-1);
137 if (flags != IPV6_RTHDR_LOOSE)
138 return (-1);
139 rt0->ip6r0_segleft++;
140 (void)memcpy(((caddr_t)(void *)rt0) +
141 ((rt0->ip6r0_len + 1) << 3), addr, sizeof(struct in6_addr));
142 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
143 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
144 break;
145 }
146 default:
147 return (-1);
148 }
149
150 return (0);
151 }
152
153 int
154 inet6_rthdr_lasthop(cmsg, flags)
155 struct cmsghdr *cmsg;
156 unsigned int flags;
157 {
158 struct ip6_rthdr *rthdr;
159
160 _DIAGASSERT(cmsg != NULL);
161
162 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
163
164 switch (rthdr->ip6r_type) {
165 case IPV6_RTHDR_TYPE_0:
166 {
167 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
168 if (rt0->ip6r0_segleft > 23)
169 return (-1);
170 if (flags != IPV6_RTHDR_LOOSE)
171 return (-1);
172 break;
173 }
174 default:
175 return (-1);
176 }
177
178 return (0);
179 }
180
181 #if 0
182 int
183 inet6_rthdr_reverse(in, out)
184 const struct cmsghdr *in;
185 struct cmsghdr *out;
186 {
187
188 return (-1);
189 }
190 #endif
191
192 int
193 inet6_rthdr_segments(cmsg)
194 const struct cmsghdr *cmsg;
195 {
196 const struct ip6_rthdr *rthdr;
197
198 _DIAGASSERT(cmsg != NULL);
199
200 rthdr = __UNCONST(CCMSG_DATA(cmsg));
201
202 switch (rthdr->ip6r_type) {
203 case IPV6_RTHDR_TYPE_0:
204 {
205 const struct ip6_rthdr0 *rt0 =
206 (const struct ip6_rthdr0 *)(const void *)rthdr;
207
208 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
209 return (-1);
210
211 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
212 }
213
214 default:
215 return (-1);
216 }
217 }
218
219 struct in6_addr *
220 inet6_rthdr_getaddr(cmsg, idx)
221 struct cmsghdr *cmsg;
222 int idx;
223 {
224 struct ip6_rthdr *rthdr;
225
226 _DIAGASSERT(cmsg != NULL);
227
228 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
229
230 switch (rthdr->ip6r_type) {
231 case IPV6_RTHDR_TYPE_0:
232 {
233 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
234 int naddr;
235
236 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
237 return NULL;
238 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
239 if (idx <= 0 || naddr < idx)
240 return NULL;
241 #ifdef COMPAT_RFC2292
242 return ((struct in6_addr *)(void *)(rt0 + 1)) + idx - 1;
243 #else
244 return ((struct in6_addr *)(void *)(rt0 + 1)) + idx;
245 #endif
246 }
247
248 default:
249 return NULL;
250 }
251 }
252
253 int
254 inet6_rthdr_getflags(cmsg, idx)
255 const struct cmsghdr *cmsg;
256 int idx;
257 {
258 const struct ip6_rthdr *rthdr;
259
260 _DIAGASSERT(cmsg != NULL);
261
262 rthdr = __UNCONST(CCMSG_DATA(cmsg));
263
264 switch (rthdr->ip6r_type) {
265 case IPV6_RTHDR_TYPE_0:
266 {
267 const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)
268 (const void *)rthdr;
269 int naddr;
270
271 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
272 return (-1);
273 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
274 if (idx < 0 || naddr < idx)
275 return (-1);
276 return IPV6_RTHDR_LOOSE;
277 }
278
279 default:
280 return (-1);
281 }
282 }
283
284 /*
285 * RFC3542 (2292bis) API
286 */
287
288 socklen_t
289 inet6_rth_space(int type, int segments)
290 {
291 switch (type) {
292 case IPV6_RTHDR_TYPE_0:
293 return (((segments * 2) + 1) << 3);
294 case IPV6_RTHDR_TYPE_2:
295 if (segments != 1)
296 return (0); /* XXX is 0 OK? */
297 return (((segments * 2) + 1) << 3);
298 default:
299 return (0); /* type not suppported */
300 }
301 }
302
303 void *
304 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
305 {
306 struct ip6_rthdr *rth;
307 struct ip6_rthdr0 *rth0;
308 struct ip6_rthdr2 *rth2;
309
310 _DIAGASSERT(bp != NULL);
311
312 rth = (struct ip6_rthdr *)bp;
313
314 switch (type) {
315 case IPV6_RTHDR_TYPE_0:
316 /* length validation */
317 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
318 return (NULL);
319
320 memset(bp, 0, bp_len);
321 rth0 = (struct ip6_rthdr0 *)(void *)rth;
322 rth0->ip6r0_len = segments * 2;
323 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
324 rth0->ip6r0_segleft = 0;
325 rth0->ip6r0_reserved = 0;
326 break;
327 case IPV6_RTHDR_TYPE_2:
328 if (segments != 1)
329 return (NULL); /* segments must be 1. */
330
331 /* length validation */
332 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_2, segments))
333 return (NULL);
334
335 memset(bp, 0, bp_len);
336 rth2 = (struct ip6_rthdr2 *)(void *)rth;
337 rth2->ip6r2_len = segments * 2;
338 rth2->ip6r2_type = IPV6_RTHDR_TYPE_2;
339 rth2->ip6r2_segleft = 0;
340 rth2->ip6r2_reserved = 0;
341 break;
342 default:
343 return (NULL); /* type not supported */
344 }
345
346 return (bp);
347 }
348
349 int
350 inet6_rth_add(void *bp, const struct in6_addr *addr)
351 {
352 struct ip6_rthdr *rth;
353 struct ip6_rthdr0 *rth0;
354 struct ip6_rthdr2 *rth2;
355 struct in6_addr *nextaddr;
356
357 _DIAGASSERT(bp != NULL);
358
359 rth = (struct ip6_rthdr *)bp;
360
361 switch (rth->ip6r_type) {
362 case IPV6_RTHDR_TYPE_0:
363 rth0 = (struct ip6_rthdr0 *)(void *)rth;
364 nextaddr = (struct in6_addr *)(void *)(rth0 + 1)
365 + rth0->ip6r0_segleft;
366 *nextaddr = *addr;
367 rth0->ip6r0_segleft++;
368 break;
369 case IPV6_RTHDR_TYPE_2:
370 rth2 = (struct ip6_rthdr2 *)(void *)rth;
371 if (rth2->ip6r2_segleft != 0)
372 return (-1); /* rthdr2 can contain just one address. */
373 nextaddr = (struct in6_addr *)(void *)(rth2 + 1) + rth2->ip6r2_segleft;
374 *nextaddr = *addr;
375 rth2->ip6r2_segleft++;
376 break;
377 default:
378 return (-1); /* type not supported */
379 }
380
381 return (0);
382 }
383
384 int
385 inet6_rth_reverse(const void *in, void *out)
386 {
387 const struct ip6_rthdr *rth_in;
388 const struct ip6_rthdr0 *rth0_in;
389 struct ip6_rthdr0 *rth0_out;
390 int i, segments;
391
392 _DIAGASSERT(in != NULL);
393 _DIAGASSERT(out != NULL);
394
395 rth_in = (const struct ip6_rthdr *)in;
396
397 switch (rth_in->ip6r_type) {
398 case IPV6_RTHDR_TYPE_0:
399 rth0_in = (const struct ip6_rthdr0 *)in;
400 rth0_out = (struct ip6_rthdr0 *)out;
401
402 /* parameter validation XXX too paranoid? */
403 if (rth0_in->ip6r0_len % 2)
404 return (-1);
405 segments = rth0_in->ip6r0_len / 2;
406
407 /* we can't use memcpy here, since in and out may overlap */
408 memmove((void *)rth0_out, (const void *)rth0_in,
409 (unsigned int)(((rth0_in->ip6r0_len) + 1) << 3));
410 rth0_out->ip6r0_segleft = segments;
411
412 /* reverse the addresses */
413 for (i = 0; i < segments / 2; i++) {
414 struct in6_addr addr_tmp, *addr1, *addr2;
415
416 addr1 = (struct in6_addr *)(void *)(rth0_out + 1) + i;
417 addr2 = (struct in6_addr *)(void *)(rth0_out + 1) +
418 (segments - i - 1);
419 addr_tmp = *addr1;
420 *addr1 = *addr2;
421 *addr2 = addr_tmp;
422 }
423
424 break;
425 case IPV6_RTHDR_TYPE_2:
426 /* reversing operation is not supported for type 2. */
427 return (-1);
428 default:
429 return (-1); /* type not supported */
430 }
431
432 return (0);
433 }
434
435 int
436 inet6_rth_segments(const void *bp)
437 {
438 const struct ip6_rthdr *rh;
439 const struct ip6_rthdr0 *rh0;
440 const struct ip6_rthdr2 *rh2;
441 unsigned int addrs;
442
443 _DIAGASSERT(bp != NULL);
444
445 rh = (const struct ip6_rthdr *)bp;
446
447 switch (rh->ip6r_type) {
448 case IPV6_RTHDR_TYPE_0:
449 rh0 = (const struct ip6_rthdr0 *)bp;
450
451 /*
452 * Validation for a type-0 routing header.
453 * Is this too strict?
454 */
455 if ((rh0->ip6r0_len % 2) != 0 ||
456 (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft)
457 return (-1);
458
459 return (addrs);
460 case IPV6_RTHDR_TYPE_2:
461 rh2 = (const struct ip6_rthdr2 *)bp;
462
463 /*
464 * Validation for a type-2 routing header.
465 */
466 if ((rh2->ip6r2_len % 2) != 0 ||
467 (addrs = (rh2->ip6r2_len / 2)) < rh2->ip6r2_segleft)
468 return (-1);
469
470 return (addrs);
471 default:
472 return (-1); /* unknown type */
473 }
474 }
475
476 struct in6_addr *
477 inet6_rth_getaddr(const void *bp, int idx)
478 {
479 const struct ip6_rthdr *rh;
480 const struct ip6_rthdr0 *rh0;
481 const struct ip6_rthdr2 *rh2;
482 unsigned int addrs;
483
484 _DIAGASSERT(bp != NULL);
485
486 rh = (const struct ip6_rthdr *)bp;
487
488 switch (rh->ip6r_type) {
489 case IPV6_RTHDR_TYPE_0:
490 rh0 = (const struct ip6_rthdr0 *)bp;
491
492 /*
493 * Validation for a type-0 routing header.
494 * Is this too strict?
495 */
496 if ((rh0->ip6r0_len % 2) != 0 ||
497 (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft)
498 return (NULL);
499
500 if (idx < 0 || addrs <= idx)
501 return (NULL);
502
503 return (((struct in6_addr *)(void *)__UNCONST(rh0 + 1)) + idx);
504 case IPV6_RTHDR_TYPE_2:
505 rh2 = (const struct ip6_rthdr2 *)bp;
506
507 /* rthdr2 contains just one address. */
508 if (idx != 1)
509 return (NULL); /* rthdr2 contains just one address. */
510
511 /*
512 * Validation for a type-2 routing header.
513 */
514 if ((rh2->ip6r2_len % 2) != 0 ||
515 (addrs = (rh2->ip6r2_len / 2)) < rh2->ip6r2_segleft)
516 return (NULL);
517
518 if (idx < 0 || addrs <= idx)
519 return (NULL);
520
521 return (((struct in6_addr *)(void *)__UNCONST(rh2 + 1)) + idx);
522 default:
523 return (NULL); /* unknown type */
524 }
525 }
526