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