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