1121472Sume/* $KAME: ip6opt.c,v 1.13 2003/06/06 10:08:20 suz Exp $ */ 2121472Sume 354696Sshin/* 454696Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 554696Sshin * All rights reserved. 654696Sshin * 754696Sshin * Redistribution and use in source and binary forms, with or without 854696Sshin * modification, are permitted provided that the following conditions 954696Sshin * are met: 1054696Sshin * 1. Redistributions of source code must retain the above copyright 1154696Sshin * notice, this list of conditions and the following disclaimer. 1254696Sshin * 2. Redistributions in binary form must reproduce the above copyright 1354696Sshin * notice, this list of conditions and the following disclaimer in the 1454696Sshin * documentation and/or other materials provided with the distribution. 1554696Sshin * 3. Neither the name of the project nor the names of its contributors 1654696Sshin * may be used to endorse or promote products derived from this software 1754696Sshin * without specific prior written permission. 1854696Sshin * 1954696Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2054696Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2154696Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2254696Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2354696Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2454696Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2554696Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2654696Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2754696Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2854696Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2954696Sshin * SUCH DAMAGE. 3054696Sshin */ 3154696Sshin 3292986Sobrien#include <sys/cdefs.h> 3392986Sobrien__FBSDID("$FreeBSD$"); 3492986Sobrien 3554696Sshin#include <sys/param.h> 3654696Sshin#include <sys/types.h> 3754696Sshin#include <sys/socket.h> 3854696Sshin 3954696Sshin#include <netinet/in.h> 4054696Sshin#include <netinet/ip6.h> 4154696Sshin 4254696Sshin#include <string.h> 4354696Sshin#include <stdio.h> 4454696Sshin 4554696Sshinstatic int ip6optlen(u_int8_t *opt, u_int8_t *lim); 4654696Sshinstatic void inet6_insert_padopt(u_char *p, int len); 4754696Sshin 48269454Smarcel#ifndef IPV6_2292HOPOPTS 49269454Smarcel#define IPV6_2292HOPOPTS 22 50269454Smarcel#endif 51269454Smarcel#ifndef IPV6_2292DSTOPTS 52269454Smarcel#define IPV6_2292DSTOPTS 23 53269454Smarcel#endif 54269454Smarcel 55269454Smarcel#define is_ipv6_hopopts(x) \ 56269454Smarcel ((x) == IPV6_HOPOPTS || (x) == IPV6_2292HOPOPTS) 57269454Smarcel#define is_ipv6_dstopts(x) \ 58269454Smarcel ((x) == IPV6_DSTOPTS || (x) == IPV6_2292DSTOPTS) 59269454Smarcel 6054696Sshin/* 6154696Sshin * This function returns the number of bytes required to hold an option 6254696Sshin * when it is stored as ancillary data, including the cmsghdr structure 6354696Sshin * at the beginning, and any padding at the end (to make its size a 6454696Sshin * multiple of 8 bytes). The argument is the size of the structure 6554696Sshin * defining the option, which must include any pad bytes at the 6654696Sshin * beginning (the value y in the alignment term "xn + y"), the type 6754696Sshin * byte, the length byte, and the option data. 6854696Sshin */ 6954696Sshinint 70199188Sumeinet6_option_space(int nbytes) 7154696Sshin{ 7254696Sshin nbytes += 2; /* we need space for nxt-hdr and length fields */ 7354696Sshin return(CMSG_SPACE((nbytes + 7) & ~7)); 7454696Sshin} 7554696Sshin 7654696Sshin/* 7754696Sshin * This function is called once per ancillary data object that will 7854696Sshin * contain either Hop-by-Hop or Destination options. It returns 0 on 7954696Sshin * success or -1 on an error. 8054696Sshin */ 8154696Sshinint 82199188Sumeinet6_option_init(void *bp, struct cmsghdr **cmsgp, int type) 8354696Sshin{ 8492889Sobrien struct cmsghdr *ch = (struct cmsghdr *)bp; 8554696Sshin 8654696Sshin /* argument validation */ 87269454Smarcel if (!is_ipv6_hopopts(type) && !is_ipv6_dstopts(type)) 8854696Sshin return(-1); 89269454Smarcel 9054696Sshin ch->cmsg_level = IPPROTO_IPV6; 9154696Sshin ch->cmsg_type = type; 9254696Sshin ch->cmsg_len = CMSG_LEN(0); 9354696Sshin 9454696Sshin *cmsgp = ch; 9554696Sshin return(0); 9654696Sshin} 9754696Sshin 9854696Sshin/* 9954696Sshin * This function appends a Hop-by-Hop option or a Destination option 10054696Sshin * into an ancillary data object that has been initialized by 10154696Sshin * inet6_option_init(). This function returns 0 if it succeeds or -1 on 10254696Sshin * an error. 10354696Sshin * multx is the value x in the alignment term "xn + y" described 10454696Sshin * earlier. It must have a value of 1, 2, 4, or 8. 10554696Sshin * plusy is the value y in the alignment term "xn + y" described 10654696Sshin * earlier. It must have a value between 0 and 7, inclusive. 10754696Sshin */ 10854696Sshinint 109199188Sumeinet6_option_append(struct cmsghdr *cmsg, const u_int8_t *typep, int multx, 110199188Sume int plusy) 11154696Sshin{ 11254696Sshin int padlen, optlen, off; 11392889Sobrien u_char *bp = (u_char *)cmsg + cmsg->cmsg_len; 11454696Sshin struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 11554696Sshin 11654696Sshin /* argument validation */ 11754696Sshin if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 11854696Sshin return(-1); 11954696Sshin if (plusy < 0 || plusy > 7) 12054696Sshin return(-1); 12154696Sshin 12254696Sshin /* 12354696Sshin * If this is the first option, allocate space for the 12454696Sshin * first 2 bytes(for next header and length fields) of 12554696Sshin * the option header. 12654696Sshin */ 12754696Sshin if (bp == (u_char *)eh) { 12854696Sshin bp += 2; 12954696Sshin cmsg->cmsg_len += 2; 13054696Sshin } 13154696Sshin 13254696Sshin /* calculate pad length before the option. */ 13354696Sshin off = bp - (u_char *)eh; 13454696Sshin padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 13554696Sshin (off % multx); 13654696Sshin padlen += plusy; 137121472Sume padlen %= multx; /* keep the pad as short as possible */ 13854696Sshin /* insert padding */ 13954696Sshin inet6_insert_padopt(bp, padlen); 14054696Sshin cmsg->cmsg_len += padlen; 14154696Sshin bp += padlen; 14254696Sshin 14354696Sshin /* copy the option */ 14454696Sshin if (typep[0] == IP6OPT_PAD1) 14554696Sshin optlen = 1; 14654696Sshin else 14754696Sshin optlen = typep[1] + 2; 14854696Sshin memcpy(bp, typep, optlen); 14954696Sshin bp += optlen; 15054696Sshin cmsg->cmsg_len += optlen; 15154696Sshin 15254696Sshin /* calculate pad length after the option and insert the padding */ 15354696Sshin off = bp - (u_char *)eh; 15454696Sshin padlen = ((off + 7) & ~7) - off; 15554696Sshin inet6_insert_padopt(bp, padlen); 15654696Sshin bp += padlen; 15754696Sshin cmsg->cmsg_len += padlen; 15854696Sshin 15954696Sshin /* update the length field of the ip6 option header */ 16054696Sshin eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 16154696Sshin 16254696Sshin return(0); 16354696Sshin} 16454696Sshin 16554696Sshin/* 16654696Sshin * This function appends a Hop-by-Hop option or a Destination option 16754696Sshin * into an ancillary data object that has been initialized by 16854696Sshin * inet6_option_init(). This function returns a pointer to the 8-bit 16954696Sshin * option type field that starts the option on success, or NULL on an 17054696Sshin * error. 17154696Sshin * The difference between this function and inet6_option_append() is 17254696Sshin * that the latter copies the contents of a previously built option into 17354696Sshin * the ancillary data object while the current function returns a 17454696Sshin * pointer to the space in the data object where the option's TLV must 17554696Sshin * then be built by the caller. 17654696Sshin * 17754696Sshin */ 17854696Sshinu_int8_t * 179199188Sumeinet6_option_alloc(struct cmsghdr *cmsg, int datalen, int multx, int plusy) 18054696Sshin{ 18154696Sshin int padlen, off; 18292889Sobrien u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len; 18354696Sshin u_int8_t *retval; 18454696Sshin struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 18554696Sshin 18654696Sshin /* argument validation */ 18754696Sshin if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 18854696Sshin return(NULL); 18954696Sshin if (plusy < 0 || plusy > 7) 19054696Sshin return(NULL); 19154696Sshin 19254696Sshin /* 19354696Sshin * If this is the first option, allocate space for the 19454696Sshin * first 2 bytes(for next header and length fields) of 19554696Sshin * the option header. 19654696Sshin */ 19754696Sshin if (bp == (u_char *)eh) { 19854696Sshin bp += 2; 19954696Sshin cmsg->cmsg_len += 2; 20054696Sshin } 20154696Sshin 20254696Sshin /* calculate pad length before the option. */ 20354696Sshin off = bp - (u_char *)eh; 20454696Sshin padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 20554696Sshin (off % multx); 20654696Sshin padlen += plusy; 207121472Sume padlen %= multx; /* keep the pad as short as possible */ 20854696Sshin /* insert padding */ 20954696Sshin inet6_insert_padopt(bp, padlen); 21054696Sshin cmsg->cmsg_len += padlen; 21154696Sshin bp += padlen; 21254696Sshin 21354696Sshin /* keep space to store specified length of data */ 21454696Sshin retval = bp; 21554696Sshin bp += datalen; 21654696Sshin cmsg->cmsg_len += datalen; 21754696Sshin 21854696Sshin /* calculate pad length after the option and insert the padding */ 21954696Sshin off = bp - (u_char *)eh; 22054696Sshin padlen = ((off + 7) & ~7) - off; 22154696Sshin inet6_insert_padopt(bp, padlen); 22254696Sshin bp += padlen; 22354696Sshin cmsg->cmsg_len += padlen; 22454696Sshin 22554696Sshin /* update the length field of the ip6 option header */ 22654696Sshin eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 22754696Sshin 22854696Sshin return(retval); 22954696Sshin} 23054696Sshin 23154696Sshin/* 23254696Sshin * This function processes the next Hop-by-Hop option or Destination 23354696Sshin * option in an ancillary data object. If another option remains to be 23454696Sshin * processed, the return value of the function is 0 and *tptrp points to 23554696Sshin * the 8-bit option type field (which is followed by the 8-bit option 23654696Sshin * data length, followed by the option data). If no more options remain 23754696Sshin * to be processed, the return value is -1 and *tptrp is NULL. If an 23854696Sshin * error occurs, the return value is -1 and *tptrp is not NULL. 23954696Sshin * (RFC 2292, 6.3.5) 24054696Sshin */ 24154696Sshinint 242199188Sumeinet6_option_next(const struct cmsghdr *cmsg, u_int8_t **tptrp) 24354696Sshin{ 24454696Sshin struct ip6_ext *ip6e; 24554696Sshin int hdrlen, optlen; 24654696Sshin u_int8_t *lim; 24754696Sshin 24854696Sshin if (cmsg->cmsg_level != IPPROTO_IPV6 || 249269454Smarcel (!is_ipv6_hopopts(cmsg->cmsg_type) && 250269454Smarcel !is_ipv6_dstopts(cmsg->cmsg_type))) 25154696Sshin return(-1); 25254696Sshin 25354696Sshin /* message length validation */ 25454696Sshin if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 25554696Sshin return(-1); 25654696Sshin ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 25754696Sshin hdrlen = (ip6e->ip6e_len + 1) << 3; 25854696Sshin if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 25954696Sshin return(-1); 26054696Sshin 26154696Sshin /* 26254696Sshin * If the caller does not specify the starting point, 26354696Sshin * simply return the 1st option. 26454696Sshin * Otherwise, search the option list for the next option. 26554696Sshin */ 26654696Sshin lim = (u_int8_t *)ip6e + hdrlen; 26754696Sshin if (*tptrp == NULL) 26854696Sshin *tptrp = (u_int8_t *)(ip6e + 1); 26954696Sshin else { 27054696Sshin if ((optlen = ip6optlen(*tptrp, lim)) == 0) 27154696Sshin return(-1); 27254696Sshin 27354696Sshin *tptrp = *tptrp + optlen; 27454696Sshin } 27554696Sshin if (*tptrp >= lim) { /* there is no option */ 27654696Sshin *tptrp = NULL; 27754696Sshin return(-1); 27854696Sshin } 27954696Sshin /* 28054696Sshin * Finally, checks if the next option is safely stored in the 28154696Sshin * cmsg data. 28254696Sshin */ 28354696Sshin if (ip6optlen(*tptrp, lim) == 0) 28454696Sshin return(-1); 28554696Sshin else 28654696Sshin return(0); 28754696Sshin} 28854696Sshin 28954696Sshin/* 29054696Sshin * This function is similar to the inet6_option_next() function, 29154696Sshin * except this function lets the caller specify the option type to be 29254696Sshin * searched for, instead of always returning the next option in the 29354696Sshin * ancillary data object. 29454696Sshin * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think 29554696Sshin * it's a typo. The variable should be type of u_int8_t **. 29654696Sshin */ 29754696Sshinint 298199188Sumeinet6_option_find(const struct cmsghdr *cmsg, u_int8_t **tptrp, int type) 29954696Sshin{ 30054696Sshin struct ip6_ext *ip6e; 30154696Sshin int hdrlen, optlen; 30254696Sshin u_int8_t *optp, *lim; 30354696Sshin 30454696Sshin if (cmsg->cmsg_level != IPPROTO_IPV6 || 305269454Smarcel (!is_ipv6_hopopts(cmsg->cmsg_type) && 306269454Smarcel !is_ipv6_dstopts(cmsg->cmsg_type))) 30754696Sshin return(-1); 30854696Sshin 30954696Sshin /* message length validation */ 31054696Sshin if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 31154696Sshin return(-1); 31254696Sshin ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 31354696Sshin hdrlen = (ip6e->ip6e_len + 1) << 3; 31454696Sshin if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 31554696Sshin return(-1); 31654696Sshin 31754696Sshin /* 31854696Sshin * If the caller does not specify the starting point, 31954696Sshin * search from the beginning of the option list. 32054696Sshin * Otherwise, search from *the next option* of the specified point. 32154696Sshin */ 32254696Sshin lim = (u_int8_t *)ip6e + hdrlen; 32354696Sshin if (*tptrp == NULL) 32454696Sshin *tptrp = (u_int8_t *)(ip6e + 1); 32554696Sshin else { 32654696Sshin if ((optlen = ip6optlen(*tptrp, lim)) == 0) 32754696Sshin return(-1); 32854696Sshin 32954696Sshin *tptrp = *tptrp + optlen; 33054696Sshin } 33154696Sshin for (optp = *tptrp; optp < lim; optp += optlen) { 33254696Sshin if (*optp == type) { 33354696Sshin *tptrp = optp; 33454696Sshin return(0); 33554696Sshin } 33654696Sshin if ((optlen = ip6optlen(optp, lim)) == 0) 33754696Sshin return(-1); 33854696Sshin } 33954696Sshin 34054696Sshin /* search failed */ 34154696Sshin *tptrp = NULL; 34254696Sshin return(-1); 34354696Sshin} 34454696Sshin 34554696Sshin/* 34654696Sshin * Calculate the length of a given IPv6 option. Also checks 34754696Sshin * if the option is safely stored in user's buffer according to the 34854696Sshin * calculated length and the limitation of the buffer. 34954696Sshin */ 35054696Sshinstatic int 351199188Sumeip6optlen(u_int8_t *opt, u_int8_t *lim) 35254696Sshin{ 35354696Sshin int optlen; 35454696Sshin 35554696Sshin if (*opt == IP6OPT_PAD1) 35654696Sshin optlen = 1; 35754696Sshin else { 35854696Sshin /* is there enough space to store type and len? */ 35954696Sshin if (opt + 2 > lim) 36054696Sshin return(0); 36154696Sshin optlen = *(opt + 1) + 2; 36254696Sshin } 36354696Sshin if (opt + optlen <= lim) 36454696Sshin return(optlen); 36554696Sshin 36654696Sshin return(0); 36754696Sshin} 36854696Sshin 36954696Sshinstatic void 37054696Sshininet6_insert_padopt(u_char *p, int len) 37154696Sshin{ 37254696Sshin switch(len) { 37354696Sshin case 0: 37454696Sshin return; 37554696Sshin case 1: 37654696Sshin p[0] = IP6OPT_PAD1; 37754696Sshin return; 37854696Sshin default: 37954696Sshin p[0] = IP6OPT_PADN; 38054696Sshin p[1] = len - 2; 38154696Sshin memset(&p[2], 0, len - 2); 38254696Sshin return; 38354696Sshin } 38454696Sshin} 385121472Sume 386121472Sume/* 387148160Sume * The following functions are defined in RFC3542, which is a successor 388148160Sume * of RFC2292. 389121472Sume */ 390121472Sume 391121472Sumeint 392121472Sumeinet6_opt_init(void *extbuf, socklen_t extlen) 393121472Sume{ 394121472Sume struct ip6_ext *ext = (struct ip6_ext *)extbuf; 395121472Sume 396121472Sume if (extlen < 0 || (extlen % 8)) 397121472Sume return(-1); 398121472Sume 399121472Sume if (ext) { 400121472Sume if (extlen == 0) 401121472Sume return(-1); 402121472Sume ext->ip6e_len = (extlen >> 3) - 1; 403121472Sume } 404121472Sume 405121472Sume return(2); /* sizeof the next and the length fields */ 406121472Sume} 407121472Sume 408121472Sumeint 409121472Sumeinet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 410121472Sume socklen_t len, u_int8_t align, void **databufp) 411121472Sume{ 412121472Sume int currentlen = offset, padlen = 0; 413121472Sume 414121472Sume /* 415121472Sume * The option type must have a value from 2 to 255, inclusive. 416121472Sume * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) 417121472Sume */ 418122682Sume if (type < 2) 419121472Sume return(-1); 420121472Sume 421121472Sume /* 422121472Sume * The option data length must have a value between 0 and 255, 423121472Sume * inclusive, and is the length of the option data that follows. 424121472Sume */ 425121472Sume if (len < 0 || len > 255) 426121472Sume return(-1); 427121472Sume 428121472Sume /* 429121472Sume * The align parameter must have a value of 1, 2, 4, or 8. 430121472Sume * The align value can not exceed the value of len. 431121472Sume */ 432121472Sume if (align != 1 && align != 2 && align != 4 && align != 8) 433121472Sume return(-1); 434121472Sume if (align > len) 435121472Sume return(-1); 436121472Sume 437121472Sume /* Calculate the padding length. */ 438121472Sume currentlen += 2 + len; /* 2 means "type + len" */ 439121472Sume if (currentlen % align) 440121472Sume padlen = align - (currentlen % align); 441121472Sume 442121472Sume /* The option must fit in the extension header buffer. */ 443121472Sume currentlen += padlen; 444121472Sume if (extlen && /* XXX: right? */ 445121472Sume currentlen > extlen) 446121472Sume return(-1); 447121472Sume 448121472Sume if (extbuf) { 449121472Sume u_int8_t *optp = (u_int8_t *)extbuf + offset; 450121472Sume 451121472Sume if (padlen == 1) { 452121472Sume /* insert a Pad1 option */ 453121472Sume *optp = IP6OPT_PAD1; 454121472Sume optp++; 455121472Sume } 456121472Sume else if (padlen > 0) { 457121472Sume /* insert a PadN option for alignment */ 458121472Sume *optp++ = IP6OPT_PADN; 459121472Sume *optp++ = padlen - 2; 460121472Sume memset(optp, 0, padlen - 2); 461121472Sume optp += (padlen - 2); 462121472Sume } 463121472Sume 464121472Sume *optp++ = type; 465121472Sume *optp++ = len; 466121472Sume 467121472Sume *databufp = optp; 468121472Sume } 469121472Sume 470121472Sume return(currentlen); 471121472Sume} 472121472Sume 473121472Sumeint 474121472Sumeinet6_opt_finish(void *extbuf, socklen_t extlen, int offset) 475121472Sume{ 476241844Seadler int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0; 477121472Sume 478121472Sume if (extbuf) { 479121472Sume u_int8_t *padp; 480121472Sume int padlen = updatelen - offset; 481121472Sume 482121472Sume if (updatelen > extlen) 483121472Sume return(-1); 484121472Sume 485121472Sume padp = (u_int8_t *)extbuf + offset; 486121472Sume if (padlen == 1) 487121472Sume *padp = IP6OPT_PAD1; 488121472Sume else if (padlen > 0) { 489121472Sume *padp++ = IP6OPT_PADN; 490121472Sume *padp++ = (padlen - 2); 491121472Sume memset(padp, 0, padlen - 2); 492121472Sume } 493121472Sume } 494121472Sume 495121472Sume return(updatelen); 496121472Sume} 497121472Sume 498121472Sumeint 499121472Sumeinet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) 500121472Sume{ 501121472Sume 502121472Sume memcpy((u_int8_t *)databuf + offset, val, vallen); 503121472Sume return(offset + vallen); 504121472Sume} 505121472Sume 506121472Sumeint 507121472Sumeinet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, 508121496Sume socklen_t *lenp, void **databufp) 509121472Sume{ 510121472Sume u_int8_t *optp, *lim; 511121472Sume int optlen; 512121472Sume 513121472Sume /* Validate extlen. XXX: is the variable really necessary?? */ 514121472Sume if (extlen == 0 || (extlen % 8)) 515121472Sume return(-1); 516121472Sume lim = (u_int8_t *)extbuf + extlen; 517121472Sume 518121472Sume /* 519121472Sume * If this is the first time this function called for this options 520121472Sume * header, simply return the 1st option. 521121472Sume * Otherwise, search the option list for the next option. 522121472Sume */ 523121472Sume if (offset == 0) { 524121472Sume optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 525121472Sume } 526121472Sume else 527121472Sume optp = (u_int8_t *)extbuf + offset; 528121472Sume 529121472Sume /* Find the next option skipping any padding options. */ 530121472Sume while(optp < lim) { 531121472Sume switch(*optp) { 532121472Sume case IP6OPT_PAD1: 533121472Sume optp++; 534121472Sume break; 535121472Sume case IP6OPT_PADN: 536121472Sume if ((optlen = ip6optlen(optp, lim)) == 0) 537121472Sume goto optend; 538121472Sume optp += optlen; 539121472Sume break; 540121472Sume default: /* found */ 541121472Sume if ((optlen = ip6optlen(optp, lim)) == 0) 542121472Sume goto optend; 543121472Sume *typep = *optp; 544121472Sume *lenp = optlen - 2; 545121472Sume *databufp = optp + 2; 546121472Sume return(optp + optlen - (u_int8_t *)extbuf); 547121472Sume } 548121472Sume } 549121472Sume 550121472Sume optend: 551121472Sume *databufp = NULL; /* for safety */ 552121472Sume return(-1); 553121472Sume} 554121472Sume 555121472Sumeint 556121472Sumeinet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 557121472Sume socklen_t *lenp, void **databufp) 558121472Sume{ 559121472Sume u_int8_t *optp, *lim; 560121472Sume int optlen; 561121472Sume 562121472Sume /* Validate extlen. XXX: is the variable really necessary?? */ 563121472Sume if (extlen == 0 || (extlen % 8)) 564121472Sume return(-1); 565121472Sume lim = (u_int8_t *)extbuf + extlen; 566121472Sume 567121472Sume /* 568121472Sume * If this is the first time this function called for this options 569121472Sume * header, simply return the 1st option. 570121472Sume * Otherwise, search the option list for the next option. 571121472Sume */ 572121472Sume if (offset == 0) { 573121472Sume optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 574121472Sume } 575121472Sume else 576121472Sume optp = (u_int8_t *)extbuf + offset; 577121472Sume 578121472Sume /* Find the specified option */ 579121472Sume while(optp < lim) { 580121472Sume if ((optlen = ip6optlen(optp, lim)) == 0) 581121472Sume goto optend; 582121472Sume 583121472Sume if (*optp == type) { /* found */ 584121472Sume *lenp = optlen - 2; 585121472Sume *databufp = optp + 2; 586121472Sume return(optp + optlen - (u_int8_t *)extbuf); 587121472Sume } 588121472Sume 589121472Sume optp += optlen; 590121472Sume } 591121472Sume 592121472Sume optend: 593121472Sume *databufp = NULL; /* for safety */ 594121472Sume return(-1); 595121472Sume} 596121472Sume 597121472Sumeint 598121472Sumeinet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) 599121472Sume{ 600121472Sume 601121472Sume /* we can't assume alignment here */ 602121472Sume memcpy(val, (u_int8_t *)databuf + offset, vallen); 603121472Sume 604121472Sume return(offset + vallen); 605121472Sume} 606