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 4854696Sshin/* 4954696Sshin * This function returns the number of bytes required to hold an option 5054696Sshin * when it is stored as ancillary data, including the cmsghdr structure 5154696Sshin * at the beginning, and any padding at the end (to make its size a 5254696Sshin * multiple of 8 bytes). The argument is the size of the structure 5354696Sshin * defining the option, which must include any pad bytes at the 5454696Sshin * beginning (the value y in the alignment term "xn + y"), the type 5554696Sshin * byte, the length byte, and the option data. 5654696Sshin */ 5754696Sshinint 58199188Sumeinet6_option_space(int nbytes) 5954696Sshin{ 6054696Sshin nbytes += 2; /* we need space for nxt-hdr and length fields */ 6154696Sshin return(CMSG_SPACE((nbytes + 7) & ~7)); 6254696Sshin} 6354696Sshin 6454696Sshin/* 6554696Sshin * This function is called once per ancillary data object that will 6654696Sshin * contain either Hop-by-Hop or Destination options. It returns 0 on 6754696Sshin * success or -1 on an error. 6854696Sshin */ 6954696Sshinint 70199188Sumeinet6_option_init(void *bp, struct cmsghdr **cmsgp, int type) 7154696Sshin{ 7292889Sobrien struct cmsghdr *ch = (struct cmsghdr *)bp; 7354696Sshin 7454696Sshin /* argument validation */ 7554696Sshin if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS) 7654696Sshin return(-1); 7754696Sshin 7854696Sshin ch->cmsg_level = IPPROTO_IPV6; 7954696Sshin ch->cmsg_type = type; 8054696Sshin ch->cmsg_len = CMSG_LEN(0); 8154696Sshin 8254696Sshin *cmsgp = ch; 8354696Sshin return(0); 8454696Sshin} 8554696Sshin 8654696Sshin/* 8754696Sshin * This function appends a Hop-by-Hop option or a Destination option 8854696Sshin * into an ancillary data object that has been initialized by 8954696Sshin * inet6_option_init(). This function returns 0 if it succeeds or -1 on 9054696Sshin * an error. 9154696Sshin * multx is the value x in the alignment term "xn + y" described 9254696Sshin * earlier. It must have a value of 1, 2, 4, or 8. 9354696Sshin * plusy is the value y in the alignment term "xn + y" described 9454696Sshin * earlier. It must have a value between 0 and 7, inclusive. 9554696Sshin */ 9654696Sshinint 97199188Sumeinet6_option_append(struct cmsghdr *cmsg, const u_int8_t *typep, int multx, 98199188Sume int plusy) 9954696Sshin{ 10054696Sshin int padlen, optlen, off; 10192889Sobrien u_char *bp = (u_char *)cmsg + cmsg->cmsg_len; 10254696Sshin struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 10354696Sshin 10454696Sshin /* argument validation */ 10554696Sshin if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 10654696Sshin return(-1); 10754696Sshin if (plusy < 0 || plusy > 7) 10854696Sshin return(-1); 10954696Sshin 11054696Sshin /* 11154696Sshin * If this is the first option, allocate space for the 11254696Sshin * first 2 bytes(for next header and length fields) of 11354696Sshin * the option header. 11454696Sshin */ 11554696Sshin if (bp == (u_char *)eh) { 11654696Sshin bp += 2; 11754696Sshin cmsg->cmsg_len += 2; 11854696Sshin } 11954696Sshin 12054696Sshin /* calculate pad length before the option. */ 12154696Sshin off = bp - (u_char *)eh; 12254696Sshin padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 12354696Sshin (off % multx); 12454696Sshin padlen += plusy; 125121472Sume padlen %= multx; /* keep the pad as short as possible */ 12654696Sshin /* insert padding */ 12754696Sshin inet6_insert_padopt(bp, padlen); 12854696Sshin cmsg->cmsg_len += padlen; 12954696Sshin bp += padlen; 13054696Sshin 13154696Sshin /* copy the option */ 13254696Sshin if (typep[0] == IP6OPT_PAD1) 13354696Sshin optlen = 1; 13454696Sshin else 13554696Sshin optlen = typep[1] + 2; 13654696Sshin memcpy(bp, typep, optlen); 13754696Sshin bp += optlen; 13854696Sshin cmsg->cmsg_len += optlen; 13954696Sshin 14054696Sshin /* calculate pad length after the option and insert the padding */ 14154696Sshin off = bp - (u_char *)eh; 14254696Sshin padlen = ((off + 7) & ~7) - off; 14354696Sshin inet6_insert_padopt(bp, padlen); 14454696Sshin bp += padlen; 14554696Sshin cmsg->cmsg_len += padlen; 14654696Sshin 14754696Sshin /* update the length field of the ip6 option header */ 14854696Sshin eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 14954696Sshin 15054696Sshin return(0); 15154696Sshin} 15254696Sshin 15354696Sshin/* 15454696Sshin * This function appends a Hop-by-Hop option or a Destination option 15554696Sshin * into an ancillary data object that has been initialized by 15654696Sshin * inet6_option_init(). This function returns a pointer to the 8-bit 15754696Sshin * option type field that starts the option on success, or NULL on an 15854696Sshin * error. 15954696Sshin * The difference between this function and inet6_option_append() is 16054696Sshin * that the latter copies the contents of a previously built option into 16154696Sshin * the ancillary data object while the current function returns a 16254696Sshin * pointer to the space in the data object where the option's TLV must 16354696Sshin * then be built by the caller. 16454696Sshin * 16554696Sshin */ 16654696Sshinu_int8_t * 167199188Sumeinet6_option_alloc(struct cmsghdr *cmsg, int datalen, int multx, int plusy) 16854696Sshin{ 16954696Sshin int padlen, off; 17092889Sobrien u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len; 17154696Sshin u_int8_t *retval; 17254696Sshin struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 17354696Sshin 17454696Sshin /* argument validation */ 17554696Sshin if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 17654696Sshin return(NULL); 17754696Sshin if (plusy < 0 || plusy > 7) 17854696Sshin return(NULL); 17954696Sshin 18054696Sshin /* 18154696Sshin * If this is the first option, allocate space for the 18254696Sshin * first 2 bytes(for next header and length fields) of 18354696Sshin * the option header. 18454696Sshin */ 18554696Sshin if (bp == (u_char *)eh) { 18654696Sshin bp += 2; 18754696Sshin cmsg->cmsg_len += 2; 18854696Sshin } 18954696Sshin 19054696Sshin /* calculate pad length before the option. */ 19154696Sshin off = bp - (u_char *)eh; 19254696Sshin padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 19354696Sshin (off % multx); 19454696Sshin padlen += plusy; 195121472Sume padlen %= multx; /* keep the pad as short as possible */ 19654696Sshin /* insert padding */ 19754696Sshin inet6_insert_padopt(bp, padlen); 19854696Sshin cmsg->cmsg_len += padlen; 19954696Sshin bp += padlen; 20054696Sshin 20154696Sshin /* keep space to store specified length of data */ 20254696Sshin retval = bp; 20354696Sshin bp += datalen; 20454696Sshin cmsg->cmsg_len += datalen; 20554696Sshin 20654696Sshin /* calculate pad length after the option and insert the padding */ 20754696Sshin off = bp - (u_char *)eh; 20854696Sshin padlen = ((off + 7) & ~7) - off; 20954696Sshin inet6_insert_padopt(bp, padlen); 21054696Sshin bp += padlen; 21154696Sshin cmsg->cmsg_len += padlen; 21254696Sshin 21354696Sshin /* update the length field of the ip6 option header */ 21454696Sshin eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 21554696Sshin 21654696Sshin return(retval); 21754696Sshin} 21854696Sshin 21954696Sshin/* 22054696Sshin * This function processes the next Hop-by-Hop option or Destination 22154696Sshin * option in an ancillary data object. If another option remains to be 22254696Sshin * processed, the return value of the function is 0 and *tptrp points to 22354696Sshin * the 8-bit option type field (which is followed by the 8-bit option 22454696Sshin * data length, followed by the option data). If no more options remain 22554696Sshin * to be processed, the return value is -1 and *tptrp is NULL. If an 22654696Sshin * error occurs, the return value is -1 and *tptrp is not NULL. 22754696Sshin * (RFC 2292, 6.3.5) 22854696Sshin */ 22954696Sshinint 230199188Sumeinet6_option_next(const struct cmsghdr *cmsg, u_int8_t **tptrp) 23154696Sshin{ 23254696Sshin struct ip6_ext *ip6e; 23354696Sshin int hdrlen, optlen; 23454696Sshin u_int8_t *lim; 23554696Sshin 23654696Sshin if (cmsg->cmsg_level != IPPROTO_IPV6 || 23754696Sshin (cmsg->cmsg_type != IPV6_HOPOPTS && 23854696Sshin cmsg->cmsg_type != IPV6_DSTOPTS)) 23954696Sshin return(-1); 24054696Sshin 24154696Sshin /* message length validation */ 24254696Sshin if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 24354696Sshin return(-1); 24454696Sshin ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 24554696Sshin hdrlen = (ip6e->ip6e_len + 1) << 3; 24654696Sshin if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 24754696Sshin return(-1); 24854696Sshin 24954696Sshin /* 25054696Sshin * If the caller does not specify the starting point, 25154696Sshin * simply return the 1st option. 25254696Sshin * Otherwise, search the option list for the next option. 25354696Sshin */ 25454696Sshin lim = (u_int8_t *)ip6e + hdrlen; 25554696Sshin if (*tptrp == NULL) 25654696Sshin *tptrp = (u_int8_t *)(ip6e + 1); 25754696Sshin else { 25854696Sshin if ((optlen = ip6optlen(*tptrp, lim)) == 0) 25954696Sshin return(-1); 26054696Sshin 26154696Sshin *tptrp = *tptrp + optlen; 26254696Sshin } 26354696Sshin if (*tptrp >= lim) { /* there is no option */ 26454696Sshin *tptrp = NULL; 26554696Sshin return(-1); 26654696Sshin } 26754696Sshin /* 26854696Sshin * Finally, checks if the next option is safely stored in the 26954696Sshin * cmsg data. 27054696Sshin */ 27154696Sshin if (ip6optlen(*tptrp, lim) == 0) 27254696Sshin return(-1); 27354696Sshin else 27454696Sshin return(0); 27554696Sshin} 27654696Sshin 27754696Sshin/* 27854696Sshin * This function is similar to the inet6_option_next() function, 27954696Sshin * except this function lets the caller specify the option type to be 28054696Sshin * searched for, instead of always returning the next option in the 28154696Sshin * ancillary data object. 28254696Sshin * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think 28354696Sshin * it's a typo. The variable should be type of u_int8_t **. 28454696Sshin */ 28554696Sshinint 286199188Sumeinet6_option_find(const struct cmsghdr *cmsg, u_int8_t **tptrp, int type) 28754696Sshin{ 28854696Sshin struct ip6_ext *ip6e; 28954696Sshin int hdrlen, optlen; 29054696Sshin u_int8_t *optp, *lim; 29154696Sshin 29254696Sshin if (cmsg->cmsg_level != IPPROTO_IPV6 || 29354696Sshin (cmsg->cmsg_type != IPV6_HOPOPTS && 29454696Sshin cmsg->cmsg_type != IPV6_DSTOPTS)) 29554696Sshin return(-1); 29654696Sshin 29754696Sshin /* message length validation */ 29854696Sshin if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 29954696Sshin return(-1); 30054696Sshin ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 30154696Sshin hdrlen = (ip6e->ip6e_len + 1) << 3; 30254696Sshin if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 30354696Sshin return(-1); 30454696Sshin 30554696Sshin /* 30654696Sshin * If the caller does not specify the starting point, 30754696Sshin * search from the beginning of the option list. 30854696Sshin * Otherwise, search from *the next option* of the specified point. 30954696Sshin */ 31054696Sshin lim = (u_int8_t *)ip6e + hdrlen; 31154696Sshin if (*tptrp == NULL) 31254696Sshin *tptrp = (u_int8_t *)(ip6e + 1); 31354696Sshin else { 31454696Sshin if ((optlen = ip6optlen(*tptrp, lim)) == 0) 31554696Sshin return(-1); 31654696Sshin 31754696Sshin *tptrp = *tptrp + optlen; 31854696Sshin } 31954696Sshin for (optp = *tptrp; optp < lim; optp += optlen) { 32054696Sshin if (*optp == type) { 32154696Sshin *tptrp = optp; 32254696Sshin return(0); 32354696Sshin } 32454696Sshin if ((optlen = ip6optlen(optp, lim)) == 0) 32554696Sshin return(-1); 32654696Sshin } 32754696Sshin 32854696Sshin /* search failed */ 32954696Sshin *tptrp = NULL; 33054696Sshin return(-1); 33154696Sshin} 33254696Sshin 33354696Sshin/* 33454696Sshin * Calculate the length of a given IPv6 option. Also checks 33554696Sshin * if the option is safely stored in user's buffer according to the 33654696Sshin * calculated length and the limitation of the buffer. 33754696Sshin */ 33854696Sshinstatic int 339199188Sumeip6optlen(u_int8_t *opt, u_int8_t *lim) 34054696Sshin{ 34154696Sshin int optlen; 34254696Sshin 34354696Sshin if (*opt == IP6OPT_PAD1) 34454696Sshin optlen = 1; 34554696Sshin else { 34654696Sshin /* is there enough space to store type and len? */ 34754696Sshin if (opt + 2 > lim) 34854696Sshin return(0); 34954696Sshin optlen = *(opt + 1) + 2; 35054696Sshin } 35154696Sshin if (opt + optlen <= lim) 35254696Sshin return(optlen); 35354696Sshin 35454696Sshin return(0); 35554696Sshin} 35654696Sshin 35754696Sshinstatic void 35854696Sshininet6_insert_padopt(u_char *p, int len) 35954696Sshin{ 36054696Sshin switch(len) { 36154696Sshin case 0: 36254696Sshin return; 36354696Sshin case 1: 36454696Sshin p[0] = IP6OPT_PAD1; 36554696Sshin return; 36654696Sshin default: 36754696Sshin p[0] = IP6OPT_PADN; 36854696Sshin p[1] = len - 2; 36954696Sshin memset(&p[2], 0, len - 2); 37054696Sshin return; 37154696Sshin } 37254696Sshin} 373121472Sume 374121472Sume/* 375148160Sume * The following functions are defined in RFC3542, which is a successor 376148160Sume * of RFC2292. 377121472Sume */ 378121472Sume 379121472Sumeint 380121472Sumeinet6_opt_init(void *extbuf, socklen_t extlen) 381121472Sume{ 382121472Sume struct ip6_ext *ext = (struct ip6_ext *)extbuf; 383121472Sume 384121472Sume if (extlen < 0 || (extlen % 8)) 385121472Sume return(-1); 386121472Sume 387121472Sume if (ext) { 388121472Sume if (extlen == 0) 389121472Sume return(-1); 390121472Sume ext->ip6e_len = (extlen >> 3) - 1; 391121472Sume } 392121472Sume 393121472Sume return(2); /* sizeof the next and the length fields */ 394121472Sume} 395121472Sume 396121472Sumeint 397121472Sumeinet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 398121472Sume socklen_t len, u_int8_t align, void **databufp) 399121472Sume{ 400121472Sume int currentlen = offset, padlen = 0; 401121472Sume 402121472Sume /* 403121472Sume * The option type must have a value from 2 to 255, inclusive. 404121472Sume * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) 405121472Sume */ 406122682Sume if (type < 2) 407121472Sume return(-1); 408121472Sume 409121472Sume /* 410121472Sume * The option data length must have a value between 0 and 255, 411121472Sume * inclusive, and is the length of the option data that follows. 412121472Sume */ 413121472Sume if (len < 0 || len > 255) 414121472Sume return(-1); 415121472Sume 416121472Sume /* 417121472Sume * The align parameter must have a value of 1, 2, 4, or 8. 418121472Sume * The align value can not exceed the value of len. 419121472Sume */ 420121472Sume if (align != 1 && align != 2 && align != 4 && align != 8) 421121472Sume return(-1); 422121472Sume if (align > len) 423121472Sume return(-1); 424121472Sume 425121472Sume /* Calculate the padding length. */ 426121472Sume currentlen += 2 + len; /* 2 means "type + len" */ 427121472Sume if (currentlen % align) 428121472Sume padlen = align - (currentlen % align); 429121472Sume 430121472Sume /* The option must fit in the extension header buffer. */ 431121472Sume currentlen += padlen; 432121472Sume if (extlen && /* XXX: right? */ 433121472Sume currentlen > extlen) 434121472Sume return(-1); 435121472Sume 436121472Sume if (extbuf) { 437121472Sume u_int8_t *optp = (u_int8_t *)extbuf + offset; 438121472Sume 439121472Sume if (padlen == 1) { 440121472Sume /* insert a Pad1 option */ 441121472Sume *optp = IP6OPT_PAD1; 442121472Sume optp++; 443121472Sume } 444121472Sume else if (padlen > 0) { 445121472Sume /* insert a PadN option for alignment */ 446121472Sume *optp++ = IP6OPT_PADN; 447121472Sume *optp++ = padlen - 2; 448121472Sume memset(optp, 0, padlen - 2); 449121472Sume optp += (padlen - 2); 450121472Sume } 451121472Sume 452121472Sume *optp++ = type; 453121472Sume *optp++ = len; 454121472Sume 455121472Sume *databufp = optp; 456121472Sume } 457121472Sume 458121472Sume return(currentlen); 459121472Sume} 460121472Sume 461121472Sumeint 462121472Sumeinet6_opt_finish(void *extbuf, socklen_t extlen, int offset) 463121472Sume{ 464242544Seadler int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0; 465121472Sume 466121472Sume if (extbuf) { 467121472Sume u_int8_t *padp; 468121472Sume int padlen = updatelen - offset; 469121472Sume 470121472Sume if (updatelen > extlen) 471121472Sume return(-1); 472121472Sume 473121472Sume padp = (u_int8_t *)extbuf + offset; 474121472Sume if (padlen == 1) 475121472Sume *padp = IP6OPT_PAD1; 476121472Sume else if (padlen > 0) { 477121472Sume *padp++ = IP6OPT_PADN; 478121472Sume *padp++ = (padlen - 2); 479121472Sume memset(padp, 0, padlen - 2); 480121472Sume } 481121472Sume } 482121472Sume 483121472Sume return(updatelen); 484121472Sume} 485121472Sume 486121472Sumeint 487121472Sumeinet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) 488121472Sume{ 489121472Sume 490121472Sume memcpy((u_int8_t *)databuf + offset, val, vallen); 491121472Sume return(offset + vallen); 492121472Sume} 493121472Sume 494121472Sumeint 495121472Sumeinet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, 496121496Sume socklen_t *lenp, void **databufp) 497121472Sume{ 498121472Sume u_int8_t *optp, *lim; 499121472Sume int optlen; 500121472Sume 501121472Sume /* Validate extlen. XXX: is the variable really necessary?? */ 502121472Sume if (extlen == 0 || (extlen % 8)) 503121472Sume return(-1); 504121472Sume lim = (u_int8_t *)extbuf + extlen; 505121472Sume 506121472Sume /* 507121472Sume * If this is the first time this function called for this options 508121472Sume * header, simply return the 1st option. 509121472Sume * Otherwise, search the option list for the next option. 510121472Sume */ 511121472Sume if (offset == 0) { 512121472Sume optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 513121472Sume } 514121472Sume else 515121472Sume optp = (u_int8_t *)extbuf + offset; 516121472Sume 517121472Sume /* Find the next option skipping any padding options. */ 518121472Sume while(optp < lim) { 519121472Sume switch(*optp) { 520121472Sume case IP6OPT_PAD1: 521121472Sume optp++; 522121472Sume break; 523121472Sume case IP6OPT_PADN: 524121472Sume if ((optlen = ip6optlen(optp, lim)) == 0) 525121472Sume goto optend; 526121472Sume optp += optlen; 527121472Sume break; 528121472Sume default: /* found */ 529121472Sume if ((optlen = ip6optlen(optp, lim)) == 0) 530121472Sume goto optend; 531121472Sume *typep = *optp; 532121472Sume *lenp = optlen - 2; 533121472Sume *databufp = optp + 2; 534121472Sume return(optp + optlen - (u_int8_t *)extbuf); 535121472Sume } 536121472Sume } 537121472Sume 538121472Sume optend: 539121472Sume *databufp = NULL; /* for safety */ 540121472Sume return(-1); 541121472Sume} 542121472Sume 543121472Sumeint 544121472Sumeinet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 545121472Sume socklen_t *lenp, void **databufp) 546121472Sume{ 547121472Sume u_int8_t *optp, *lim; 548121472Sume int optlen; 549121472Sume 550121472Sume /* Validate extlen. XXX: is the variable really necessary?? */ 551121472Sume if (extlen == 0 || (extlen % 8)) 552121472Sume return(-1); 553121472Sume lim = (u_int8_t *)extbuf + extlen; 554121472Sume 555121472Sume /* 556121472Sume * If this is the first time this function called for this options 557121472Sume * header, simply return the 1st option. 558121472Sume * Otherwise, search the option list for the next option. 559121472Sume */ 560121472Sume if (offset == 0) { 561121472Sume optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 562121472Sume } 563121472Sume else 564121472Sume optp = (u_int8_t *)extbuf + offset; 565121472Sume 566121472Sume /* Find the specified option */ 567121472Sume while(optp < lim) { 568121472Sume if ((optlen = ip6optlen(optp, lim)) == 0) 569121472Sume goto optend; 570121472Sume 571121472Sume if (*optp == type) { /* found */ 572121472Sume *lenp = optlen - 2; 573121472Sume *databufp = optp + 2; 574121472Sume return(optp + optlen - (u_int8_t *)extbuf); 575121472Sume } 576121472Sume 577121472Sume optp += optlen; 578121472Sume } 579121472Sume 580121472Sume optend: 581121472Sume *databufp = NULL; /* for safety */ 582121472Sume return(-1); 583121472Sume} 584121472Sume 585121472Sumeint 586121472Sumeinet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) 587121472Sume{ 588121472Sume 589121472Sume /* we can't assume alignment here */ 590121472Sume memcpy(val, (u_int8_t *)databuf + offset, vallen); 591121472Sume 592121472Sume return(offset + vallen); 593121472Sume} 594