ip6opt.c revision 1.13 1 /* $NetBSD: ip6opt.c,v 1.13 2012/03/13 21:13:41 christos 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: ip6opt.c,v 1.13 2012/03/13 21:13:41 christos 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 <stddef.h>
47 #include <string.h>
48 #include <stdio.h>
49
50 #ifdef __weak_alias
51 __weak_alias(inet6_option_alloc,_inet6_option_alloc)
52 __weak_alias(inet6_option_append,_inet6_option_append)
53 __weak_alias(inet6_option_find,_inet6_option_find)
54 __weak_alias(inet6_option_init,_inet6_option_init)
55 __weak_alias(inet6_option_next,_inet6_option_next)
56 __weak_alias(inet6_option_space,_inet6_option_space)
57 __weak_alias(inet6_opt_init, _inet6_opt_init)
58 __weak_alias(inet6_opt_append, _inet6_opt_append)
59 __weak_alias(inet6_opt_finish, _inet6_opt_finish)
60 __weak_alias(inet6_opt_set_val, _inet6_opt_set_val)
61 __weak_alias(inet6_opt_next, _inet6_opt_next)
62 __weak_alias(inet6_opt_find, _inet6_opt_find)
63 __weak_alias(inet6_opt_get_val, _inet6_opt_get_val)
64 #endif
65
66 static int ip6optlen(u_int8_t *opt, u_int8_t *lim);
67 static void inet6_insert_padopt(u_char *p, size_t len);
68
69 /*
70 * This function returns the number of bytes required to hold an option
71 * when it is stored as ancillary data, including the cmsghdr structure
72 * at the beginning, and any padding at the end (to make its size a
73 * multiple of 8 bytes). The argument is the size of the structure
74 * defining the option, which must include any pad bytes at the
75 * beginning (the value y in the alignment term "xn + y"), the type
76 * byte, the length byte, and the option data.
77 */
78 int
79 inet6_option_space(nbytes)
80 int nbytes;
81 {
82 size_t sp;
83 nbytes += 2; /* we need space for nxt-hdr and length fields */
84 sp = CMSG_SPACE((nbytes + 7) & ~7);
85 _DIAGASSERT(__type_fit(int, sp));
86 return (int)sp;
87 }
88
89 /*
90 * This function is called once per ancillary data object that will
91 * contain either Hop-by-Hop or Destination options. It returns 0 on
92 * success or -1 on an error.
93 */
94 int
95 inet6_option_init(bp, cmsgp, type)
96 void *bp;
97 struct cmsghdr **cmsgp;
98 int type;
99 {
100 register struct cmsghdr *ch;
101
102 _DIAGASSERT(bp != NULL);
103 _DIAGASSERT(cmsgp != NULL);
104
105 ch = (struct cmsghdr *)bp;
106
107 /* argument validation */
108 if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS)
109 return(-1);
110
111 ch->cmsg_level = IPPROTO_IPV6;
112 ch->cmsg_type = type;
113 ch->cmsg_len = CMSG_LEN(0);
114
115 *cmsgp = ch;
116 return(0);
117 }
118
119 /*
120 * This function appends a Hop-by-Hop option or a Destination option
121 * into an ancillary data object that has been initialized by
122 * inet6_option_init(). This function returns 0 if it succeeds or -1 on
123 * an error.
124 * multx is the value x in the alignment term "xn + y" described
125 * earlier. It must have a value of 1, 2, 4, or 8.
126 * plusy is the value y in the alignment term "xn + y" described
127 * earlier. It must have a value between 0 and 7, inclusive.
128 */
129 int
130 inet6_option_append(cmsg, typep, multx, plusy)
131 struct cmsghdr *cmsg;
132 const u_int8_t *typep;
133 int multx;
134 int plusy;
135 {
136 size_t padlen, optlen, off;
137 register u_char *bp;
138 struct ip6_ext *eh;
139
140 _DIAGASSERT(cmsg != NULL);
141 _DIAGASSERT(typep != NULL);
142
143 bp = (u_char *)(void *)cmsg + cmsg->cmsg_len;
144 eh = (struct ip6_ext *)(void *)CMSG_DATA(cmsg);
145
146 /* argument validation */
147 if (multx != 1 && multx != 2 && multx != 4 && multx != 8)
148 return(-1);
149 if (plusy < 0 || plusy > 7)
150 return(-1);
151
152 /*
153 * If this is the first option, allocate space for the
154 * first 2 bytes(for next header and length fields) of
155 * the option header.
156 */
157 if (bp == (u_char *)(void *)eh) {
158 bp += 2;
159 cmsg->cmsg_len += 2;
160 }
161
162 /* calculate pad length before the option. */
163 off = bp - (u_char *)(void *)eh;
164 padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
165 (off % multx);
166 padlen += plusy;
167 padlen %= multx; /* keep the pad as short as possible */
168 /* insert padding */
169 inet6_insert_padopt(bp, padlen);
170 _DIAGASSERT(__type_fit(socklen_t, padlen + cmsg->cmsg_len));
171 cmsg->cmsg_len += (socklen_t)padlen;
172 bp += padlen;
173
174 /* copy the option */
175 if (typep[0] == IP6OPT_PAD1)
176 optlen = 1;
177 else
178 optlen = typep[1] + 2;
179 memcpy(bp, typep, (size_t)optlen);
180 bp += optlen;
181 _DIAGASSERT(__type_fit(socklen_t, optlen + cmsg->cmsg_len));
182 cmsg->cmsg_len += (socklen_t)optlen;
183
184 /* calculate pad length after the option and insert the padding */
185 off = bp - (u_char *)(void *)eh;
186 padlen = ((off + 7) & ~7) - off;
187 inet6_insert_padopt(bp, padlen);
188 bp += padlen;
189 _DIAGASSERT(__type_fit(socklen_t, padlen + cmsg->cmsg_len));
190 cmsg->cmsg_len += (socklen_t)padlen;
191
192 /* update the length field of the ip6 option header */
193 off = bp - (u_char *)(void *)eh;
194 _DIAGASSERT(__type_fit(uint8_t, (off >> 3) - 1));
195 eh->ip6e_len = (uint8_t)((off >> 3) - 1);
196
197 return(0);
198 }
199
200 /*
201 * This function appends a Hop-by-Hop option or a Destination option
202 * into an ancillary data object that has been initialized by
203 * inet6_option_init(). This function returns a pointer to the 8-bit
204 * option type field that starts the option on success, or NULL on an
205 * error.
206 * The difference between this function and inet6_option_append() is
207 * that the latter copies the contents of a previously built option into
208 * the ancillary data object while the current function returns a
209 * pointer to the space in the data object where the option's TLV must
210 * then be built by the caller.
211 *
212 */
213 u_int8_t *
214 inet6_option_alloc(cmsg, datalen, multx, plusy)
215 struct cmsghdr *cmsg;
216 int datalen;
217 int multx;
218 int plusy;
219 {
220 size_t padlen, off;
221 register u_int8_t *bp;
222 u_int8_t *retval;
223 struct ip6_ext *eh;
224
225 _DIAGASSERT(cmsg != NULL);
226
227 bp = (u_char *)(void *)cmsg + cmsg->cmsg_len;
228 eh = (struct ip6_ext *)(void *)CMSG_DATA(cmsg);
229
230 /* argument validation */
231 if (multx != 1 && multx != 2 && multx != 4 && multx != 8)
232 return(NULL);
233 if (plusy < 0 || plusy > 7)
234 return(NULL);
235
236 /*
237 * If this is the first option, allocate space for the
238 * first 2 bytes(for next header and length fields) of
239 * the option header.
240 */
241 if (bp == (u_char *)(void *)eh) {
242 bp += 2;
243 cmsg->cmsg_len += 2;
244 }
245
246 /* calculate pad length before the option. */
247 off = bp - (u_char *)(void *)eh;
248 padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
249 (off % multx);
250 padlen += plusy;
251 padlen %= multx; /* keep the pad as short as possible */
252 /* insert padding */
253 inet6_insert_padopt(bp, padlen);
254 cmsg->cmsg_len += (socklen_t)padlen;
255 bp += padlen;
256
257 /* keep space to store specified length of data */
258 retval = bp;
259 bp += datalen;
260 cmsg->cmsg_len += datalen;
261
262 /* calculate pad length after the option and insert the padding */
263 off = bp - (u_char *)(void *)eh;
264 padlen = ((off + 7) & ~7) - off;
265 inet6_insert_padopt(bp, padlen);
266 bp += padlen;
267 _DIAGASSERT(__type_fit(socklen_t, padlen + cmsg->cmsg_len));
268 cmsg->cmsg_len += (socklen_t)padlen;
269
270 /* update the length field of the ip6 option header */
271 off = bp - (u_char *)(void *)eh;
272 _DIAGASSERT(__type_fit(uint8_t, (off >> 3) - 1));
273 eh->ip6e_len = (uint8_t)((off >> 3) - 1);
274
275 return(retval);
276 }
277
278 /*
279 * This function processes the next Hop-by-Hop option or Destination
280 * option in an ancillary data object. If another option remains to be
281 * processed, the return value of the function is 0 and *tptrp points to
282 * the 8-bit option type field (which is followed by the 8-bit option
283 * data length, followed by the option data). If no more options remain
284 * to be processed, the return value is -1 and *tptrp is NULL. If an
285 * error occurs, the return value is -1 and *tptrp is not NULL.
286 * (RFC 2292, 6.3.5)
287 */
288 int
289 inet6_option_next(cmsg, tptrp)
290 const struct cmsghdr *cmsg;
291 u_int8_t **tptrp;
292 {
293 struct ip6_ext *ip6e;
294 int hdrlen, optlen;
295 u_int8_t *lim;
296
297 _DIAGASSERT(cmsg != NULL);
298 _DIAGASSERT(tptrp != NULL);
299
300 if (cmsg->cmsg_level != IPPROTO_IPV6 ||
301 (cmsg->cmsg_type != IPV6_HOPOPTS &&
302 cmsg->cmsg_type != IPV6_DSTOPTS))
303 return(-1);
304
305 /* message length validation */
306 if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext)))
307 return(-1);
308 ip6e = __UNCONST(CCMSG_DATA(cmsg));
309 hdrlen = (ip6e->ip6e_len + 1) << 3;
310 if (cmsg->cmsg_len < CMSG_SPACE(hdrlen))
311 return(-1);
312
313 /*
314 * If the caller does not specify the starting point,
315 * simply return the 1st option.
316 * Otherwise, search the option list for the next option.
317 */
318 lim = (u_int8_t *)(void *)ip6e + hdrlen;
319 if (*tptrp == NULL)
320 *tptrp = (u_int8_t *)(void *)(ip6e + 1);
321 else {
322 if ((optlen = ip6optlen(*tptrp, lim)) == 0)
323 return(-1);
324
325 *tptrp = *tptrp + optlen;
326 }
327 if (*tptrp >= lim) { /* there is no option */
328 *tptrp = NULL;
329 return(-1);
330 }
331 /*
332 * Finally, checks if the next option is safely stored in the
333 * cmsg data.
334 */
335 if (ip6optlen(*tptrp, lim) == 0)
336 return(-1);
337 else
338 return(0);
339 }
340
341 /*
342 * This function is similar to the inet6_option_next() function,
343 * except this function lets the caller specify the option type to be
344 * searched for, instead of always returning the next option in the
345 * ancillary data object.
346 * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think
347 * it's a typo. The variable should be type of u_int8_t **.
348 */
349 int
350 inet6_option_find(cmsg, tptrp, type)
351 const struct cmsghdr *cmsg;
352 u_int8_t **tptrp;
353 int type;
354 {
355 struct ip6_ext *ip6e;
356 int hdrlen, optlen;
357 u_int8_t *optp, *lim;
358
359 _DIAGASSERT(cmsg != NULL);
360 _DIAGASSERT(tptrp != NULL);
361
362 if (cmsg->cmsg_level != IPPROTO_IPV6 ||
363 (cmsg->cmsg_type != IPV6_HOPOPTS &&
364 cmsg->cmsg_type != IPV6_DSTOPTS))
365 return(-1);
366
367 /* message length validation */
368 if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext)))
369 return(-1);
370 ip6e = __UNCONST(CCMSG_DATA(cmsg));
371 hdrlen = (ip6e->ip6e_len + 1) << 3;
372 if (cmsg->cmsg_len < CMSG_SPACE(hdrlen))
373 return(-1);
374
375 /*
376 * If the caller does not specify the starting point,
377 * search from the beginning of the option list.
378 * Otherwise, search from *the next option* of the specified point.
379 */
380 lim = (u_int8_t *)(void *)ip6e + hdrlen;
381 if (*tptrp == NULL)
382 *tptrp = (u_int8_t *)(void *)(ip6e + 1);
383 else {
384 if ((optlen = ip6optlen(*tptrp, lim)) == 0)
385 return(-1);
386
387 *tptrp = *tptrp + optlen;
388 }
389 for (optp = *tptrp; optp < lim; optp += optlen) {
390 if (*optp == type) {
391 *tptrp = optp;
392 return(0);
393 }
394 if ((optlen = ip6optlen(optp, lim)) == 0)
395 return(-1);
396 }
397
398 /* search failed */
399 *tptrp = NULL;
400 return(-1);
401 }
402
403 /*
404 * Calculate the length of a given IPv6 option. Also checks
405 * if the option is safely stored in user's buffer according to the
406 * calculated length and the limitation of the buffer.
407 */
408 static int
409 ip6optlen(opt, lim)
410 u_int8_t *opt, *lim;
411 {
412 int optlen;
413
414 _DIAGASSERT(opt != NULL);
415 _DIAGASSERT(lim != NULL);
416
417 if (*opt == IP6OPT_PAD1)
418 optlen = 1;
419 else {
420 /* is there enough space to store type and len? */
421 if (opt + 2 > lim)
422 return(0);
423 optlen = *(opt + 1) + 2;
424 }
425 if (opt + optlen <= lim)
426 return(optlen);
427
428 return(0);
429 }
430
431 static void
432 inet6_insert_padopt(u_char *p, size_t len)
433 {
434
435 _DIAGASSERT(p != NULL);
436
437 switch(len) {
438 case 0:
439 return;
440 case 1:
441 p[0] = IP6OPT_PAD1;
442 return;
443 default:
444 p[0] = IP6OPT_PADN;
445 _DIAGASSERT(__type_fit(u_char, len - 2));
446 p[1] = (u_char)(len - 2);
447 memset(&p[2], 0, len - 2);
448 return;
449 }
450 }
451
452 /*
453 * The following functions are defined in RFC3542, which is a successor
454 * of RFC2292.
455 */
456
457 int
458 inet6_opt_init(void *extbuf, socklen_t extlen)
459 {
460 struct ip6_ext *ext = (struct ip6_ext *)extbuf;
461
462 if (extlen % 8)
463 return (-1);
464
465 if (ext) {
466 if (extlen == 0)
467 return (-1);
468 ext->ip6e_len = (extlen >> 3) - 1;
469 }
470
471 return (2); /* sizeof the next and the length fields */
472 }
473
474 int
475 inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
476 socklen_t len, u_int8_t align, void **databufp)
477 {
478 int currentlen = offset;
479 size_t padlen = 0;
480
481 /*
482 * The option type must have a value from 2 to 255, inclusive.
483 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
484 */
485 if (type < 2)
486 return (-1);
487
488 /*
489 * The option data length must have a value between 0 and 255,
490 * inclusive, and is the length of the option data that follows.
491 */
492 if (len > 255)
493 return (-1);
494
495 /*
496 * The align parameter must have a value of 1, 2, 4, or 8.
497 * The align value can not exceed the value of len.
498 */
499 if (align != 1 && align != 2 && align != 4 && align != 8)
500 return (-1);
501 if (align > len)
502 return (-1);
503
504 /* Calculate the padding length. */
505 currentlen += 2 + len; /* 2 means "type + len" */
506 if (currentlen % align)
507 padlen = align - (currentlen % align);
508
509 /* The option must fit in the extension header buffer. */
510 _DIAGASSERT(__type_fit(int, currentlen + padlen));
511 currentlen += (int)padlen;
512 if (extlen && /* XXX: right? */
513 (socklen_t)currentlen > extlen)
514 return (-1);
515
516 if (extbuf) {
517 u_int8_t *optp = (u_int8_t *)extbuf + offset;
518
519 if (padlen == 1) {
520 /* insert a Pad1 option */
521 *optp = IP6OPT_PAD1;
522 optp++;
523 } else if (padlen > 0) {
524 /* insert a PadN option for alignment */
525 *optp++ = IP6OPT_PADN;
526 _DIAGASSERT(__type_fit(uint8_t, padlen - 2));
527 *optp++ = (uint8_t)(padlen - 2);
528 memset(optp, 0, padlen - 2);
529 optp += (padlen - 2);
530 }
531
532 *optp++ = type;
533 *optp++ = len;
534
535 *databufp = optp;
536 }
537
538 return (currentlen);
539 }
540
541 int
542 inet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
543 {
544 int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;
545
546 if (extbuf) {
547 u_int8_t *padp;
548 size_t padlen = updatelen - offset;
549
550 if ((socklen_t)updatelen > extlen || padlen >= 256 + 2)
551 return (-1);
552
553 padp = (u_int8_t *)extbuf + offset;
554 if (padlen == 1)
555 *padp = IP6OPT_PAD1;
556 else if (padlen > 0) {
557 *padp++ = IP6OPT_PADN;
558 *padp++ = (uint8_t)(padlen - 2);
559 memset(padp, 0, padlen - 2);
560 }
561 }
562
563 return (updatelen);
564 }
565
566 int
567 inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
568 {
569
570 memcpy((u_int8_t *)databuf + offset, val, vallen);
571 return (offset + vallen);
572 }
573
574 int
575 inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep,
576 socklen_t *lenp, void **databufp)
577 {
578 u_int8_t *optp, *lim;
579 int optlen;
580
581 /* Validate extlen. XXX: is the variable really necessary?? */
582 if (extlen == 0 || (extlen % 8))
583 return (-1);
584 lim = (u_int8_t *)extbuf + extlen;
585
586 /*
587 * If this is the first time this function called for this options
588 * header, simply return the 1st option.
589 * Otherwise, search the option list for the next option.
590 */
591 if (offset == 0)
592 optp = (u_int8_t *)(void *)((struct ip6_hbh *)extbuf + 1);
593 else
594 optp = (u_int8_t *)extbuf + offset;
595
596 /* Find the next option skipping any padding options. */
597 while (optp < lim) {
598 ptrdiff_t rv;
599 switch(*optp) {
600 case IP6OPT_PAD1:
601 optp++;
602 break;
603 case IP6OPT_PADN:
604 if ((optlen = ip6optlen(optp, lim)) == 0)
605 goto optend;
606 optp += optlen;
607 break;
608 default: /* found */
609 if ((optlen = ip6optlen(optp, lim)) == 0)
610 goto optend;
611 *typep = *optp;
612 *lenp = optlen - 2;
613 *databufp = optp + 2;
614 rv = optp + optlen - (u_int8_t *)extbuf;
615 _DIAGASSERT(__type_fit(int, rv));
616 return (int)rv;
617 }
618 }
619
620 optend:
621 *databufp = NULL; /* for safety */
622 return (-1);
623 }
624
625 int
626 inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
627 socklen_t *lenp, void **databufp)
628 {
629 u_int8_t *optp, *lim;
630 int optlen;
631
632 /* Validate extlen. XXX: is the variable really necessary?? */
633 if (extlen == 0 || (extlen % 8))
634 return (-1);
635 lim = (u_int8_t *)extbuf + extlen;
636
637 /*
638 * If this is the first time this function called for this options
639 * header, simply return the 1st option.
640 * Otherwise, search the option list for the next option.
641 */
642 if (offset == 0)
643 optp = (u_int8_t *)(void *)((struct ip6_hbh *)extbuf + 1);
644 else
645 optp = (u_int8_t *)extbuf + offset;
646
647 /* Find the specified option */
648 while (optp < lim) {
649 if ((optlen = ip6optlen(optp, lim)) == 0)
650 goto optend;
651
652 if (*optp == type) { /* found */
653 ptrdiff_t td;
654 *lenp = optlen - 2;
655 *databufp = optp + 2;
656 td = optp + optlen - (u_int8_t *)extbuf;
657 _DIAGASSERT(__type_fit(int, td));
658 return (int)td;
659 }
660
661 optp += optlen;
662 }
663
664 optend:
665 *databufp = NULL; /* for safety */
666 return (-1);
667 }
668
669 int
670 inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
671 {
672
673 /* we can't assume alignment here */
674 memcpy(val, (u_int8_t *)databuf + offset, vallen);
675
676 return (offset + vallen);
677 }
678