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