1f95d4633SHajimu UMEMOTO /* $KAME: ip6opt.c,v 1.13 2003/06/06 10:08:20 suz Exp $ */ 2f95d4633SHajimu UMEMOTO 305a244b4SYoshinobu Inoue /* 405a244b4SYoshinobu Inoue * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 505a244b4SYoshinobu Inoue * All rights reserved. 605a244b4SYoshinobu Inoue * 705a244b4SYoshinobu Inoue * Redistribution and use in source and binary forms, with or without 805a244b4SYoshinobu Inoue * modification, are permitted provided that the following conditions 905a244b4SYoshinobu Inoue * are met: 1005a244b4SYoshinobu Inoue * 1. Redistributions of source code must retain the above copyright 1105a244b4SYoshinobu Inoue * notice, this list of conditions and the following disclaimer. 1205a244b4SYoshinobu Inoue * 2. Redistributions in binary form must reproduce the above copyright 1305a244b4SYoshinobu Inoue * notice, this list of conditions and the following disclaimer in the 1405a244b4SYoshinobu Inoue * documentation and/or other materials provided with the distribution. 1505a244b4SYoshinobu Inoue * 3. Neither the name of the project nor the names of its contributors 1605a244b4SYoshinobu Inoue * may be used to endorse or promote products derived from this software 1705a244b4SYoshinobu Inoue * without specific prior written permission. 1805a244b4SYoshinobu Inoue * 1905a244b4SYoshinobu Inoue * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2005a244b4SYoshinobu Inoue * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2105a244b4SYoshinobu Inoue * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2205a244b4SYoshinobu Inoue * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2305a244b4SYoshinobu Inoue * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2405a244b4SYoshinobu Inoue * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2505a244b4SYoshinobu Inoue * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2605a244b4SYoshinobu Inoue * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2705a244b4SYoshinobu Inoue * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2805a244b4SYoshinobu Inoue * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2905a244b4SYoshinobu Inoue * SUCH DAMAGE. 3005a244b4SYoshinobu Inoue */ 3105a244b4SYoshinobu Inoue 32333fc21eSDavid E. O'Brien #include <sys/cdefs.h> 33333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 34333fc21eSDavid E. O'Brien 3505a244b4SYoshinobu Inoue #include <sys/param.h> 3605a244b4SYoshinobu Inoue #include <sys/types.h> 3705a244b4SYoshinobu Inoue #include <sys/socket.h> 3805a244b4SYoshinobu Inoue 3905a244b4SYoshinobu Inoue #include <netinet/in.h> 4005a244b4SYoshinobu Inoue #include <netinet/ip6.h> 4105a244b4SYoshinobu Inoue 4205a244b4SYoshinobu Inoue #include <string.h> 4305a244b4SYoshinobu Inoue #include <stdio.h> 4405a244b4SYoshinobu Inoue 4505a244b4SYoshinobu Inoue static int ip6optlen(u_int8_t *opt, u_int8_t *lim); 4605a244b4SYoshinobu Inoue static void inet6_insert_padopt(u_char *p, int len); 4705a244b4SYoshinobu Inoue 4805a244b4SYoshinobu Inoue /* 4905a244b4SYoshinobu Inoue * This function returns the number of bytes required to hold an option 5005a244b4SYoshinobu Inoue * when it is stored as ancillary data, including the cmsghdr structure 5105a244b4SYoshinobu Inoue * at the beginning, and any padding at the end (to make its size a 5205a244b4SYoshinobu Inoue * multiple of 8 bytes). The argument is the size of the structure 5305a244b4SYoshinobu Inoue * defining the option, which must include any pad bytes at the 5405a244b4SYoshinobu Inoue * beginning (the value y in the alignment term "xn + y"), the type 5505a244b4SYoshinobu Inoue * byte, the length byte, and the option data. 5605a244b4SYoshinobu Inoue */ 5705a244b4SYoshinobu Inoue int 5805a244b4SYoshinobu Inoue inet6_option_space(nbytes) 5905a244b4SYoshinobu Inoue int nbytes; 6005a244b4SYoshinobu Inoue { 6105a244b4SYoshinobu Inoue nbytes += 2; /* we need space for nxt-hdr and length fields */ 6205a244b4SYoshinobu Inoue return(CMSG_SPACE((nbytes + 7) & ~7)); 6305a244b4SYoshinobu Inoue } 6405a244b4SYoshinobu Inoue 6505a244b4SYoshinobu Inoue /* 6605a244b4SYoshinobu Inoue * This function is called once per ancillary data object that will 6705a244b4SYoshinobu Inoue * contain either Hop-by-Hop or Destination options. It returns 0 on 6805a244b4SYoshinobu Inoue * success or -1 on an error. 6905a244b4SYoshinobu Inoue */ 7005a244b4SYoshinobu Inoue int 7105a244b4SYoshinobu Inoue inet6_option_init(bp, cmsgp, type) 7205a244b4SYoshinobu Inoue void *bp; 7305a244b4SYoshinobu Inoue struct cmsghdr **cmsgp; 7405a244b4SYoshinobu Inoue int type; 7505a244b4SYoshinobu Inoue { 768fb3f3f6SDavid E. O'Brien struct cmsghdr *ch = (struct cmsghdr *)bp; 7705a244b4SYoshinobu Inoue 7805a244b4SYoshinobu Inoue /* argument validation */ 7905a244b4SYoshinobu Inoue if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS) 8005a244b4SYoshinobu Inoue return(-1); 8105a244b4SYoshinobu Inoue 8205a244b4SYoshinobu Inoue ch->cmsg_level = IPPROTO_IPV6; 8305a244b4SYoshinobu Inoue ch->cmsg_type = type; 8405a244b4SYoshinobu Inoue ch->cmsg_len = CMSG_LEN(0); 8505a244b4SYoshinobu Inoue 8605a244b4SYoshinobu Inoue *cmsgp = ch; 8705a244b4SYoshinobu Inoue return(0); 8805a244b4SYoshinobu Inoue } 8905a244b4SYoshinobu Inoue 9005a244b4SYoshinobu Inoue /* 9105a244b4SYoshinobu Inoue * This function appends a Hop-by-Hop option or a Destination option 9205a244b4SYoshinobu Inoue * into an ancillary data object that has been initialized by 9305a244b4SYoshinobu Inoue * inet6_option_init(). This function returns 0 if it succeeds or -1 on 9405a244b4SYoshinobu Inoue * an error. 9505a244b4SYoshinobu Inoue * multx is the value x in the alignment term "xn + y" described 9605a244b4SYoshinobu Inoue * earlier. It must have a value of 1, 2, 4, or 8. 9705a244b4SYoshinobu Inoue * plusy is the value y in the alignment term "xn + y" described 9805a244b4SYoshinobu Inoue * earlier. It must have a value between 0 and 7, inclusive. 9905a244b4SYoshinobu Inoue */ 10005a244b4SYoshinobu Inoue int 10105a244b4SYoshinobu Inoue inet6_option_append(cmsg, typep, multx, plusy) 10205a244b4SYoshinobu Inoue struct cmsghdr *cmsg; 10305a244b4SYoshinobu Inoue const u_int8_t *typep; 10405a244b4SYoshinobu Inoue int multx; 10505a244b4SYoshinobu Inoue int plusy; 10605a244b4SYoshinobu Inoue { 10705a244b4SYoshinobu Inoue int padlen, optlen, off; 1088fb3f3f6SDavid E. O'Brien u_char *bp = (u_char *)cmsg + cmsg->cmsg_len; 10905a244b4SYoshinobu Inoue struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 11005a244b4SYoshinobu Inoue 11105a244b4SYoshinobu Inoue /* argument validation */ 11205a244b4SYoshinobu Inoue if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 11305a244b4SYoshinobu Inoue return(-1); 11405a244b4SYoshinobu Inoue if (plusy < 0 || plusy > 7) 11505a244b4SYoshinobu Inoue return(-1); 11605a244b4SYoshinobu Inoue 11705a244b4SYoshinobu Inoue /* 11805a244b4SYoshinobu Inoue * If this is the first option, allocate space for the 11905a244b4SYoshinobu Inoue * first 2 bytes(for next header and length fields) of 12005a244b4SYoshinobu Inoue * the option header. 12105a244b4SYoshinobu Inoue */ 12205a244b4SYoshinobu Inoue if (bp == (u_char *)eh) { 12305a244b4SYoshinobu Inoue bp += 2; 12405a244b4SYoshinobu Inoue cmsg->cmsg_len += 2; 12505a244b4SYoshinobu Inoue } 12605a244b4SYoshinobu Inoue 12705a244b4SYoshinobu Inoue /* calculate pad length before the option. */ 12805a244b4SYoshinobu Inoue off = bp - (u_char *)eh; 12905a244b4SYoshinobu Inoue padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 13005a244b4SYoshinobu Inoue (off % multx); 13105a244b4SYoshinobu Inoue padlen += plusy; 132f95d4633SHajimu UMEMOTO padlen %= multx; /* keep the pad as short as possible */ 13305a244b4SYoshinobu Inoue /* insert padding */ 13405a244b4SYoshinobu Inoue inet6_insert_padopt(bp, padlen); 13505a244b4SYoshinobu Inoue cmsg->cmsg_len += padlen; 13605a244b4SYoshinobu Inoue bp += padlen; 13705a244b4SYoshinobu Inoue 13805a244b4SYoshinobu Inoue /* copy the option */ 13905a244b4SYoshinobu Inoue if (typep[0] == IP6OPT_PAD1) 14005a244b4SYoshinobu Inoue optlen = 1; 14105a244b4SYoshinobu Inoue else 14205a244b4SYoshinobu Inoue optlen = typep[1] + 2; 14305a244b4SYoshinobu Inoue memcpy(bp, typep, optlen); 14405a244b4SYoshinobu Inoue bp += optlen; 14505a244b4SYoshinobu Inoue cmsg->cmsg_len += optlen; 14605a244b4SYoshinobu Inoue 14705a244b4SYoshinobu Inoue /* calculate pad length after the option and insert the padding */ 14805a244b4SYoshinobu Inoue off = bp - (u_char *)eh; 14905a244b4SYoshinobu Inoue padlen = ((off + 7) & ~7) - off; 15005a244b4SYoshinobu Inoue inet6_insert_padopt(bp, padlen); 15105a244b4SYoshinobu Inoue bp += padlen; 15205a244b4SYoshinobu Inoue cmsg->cmsg_len += padlen; 15305a244b4SYoshinobu Inoue 15405a244b4SYoshinobu Inoue /* update the length field of the ip6 option header */ 15505a244b4SYoshinobu Inoue eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 15605a244b4SYoshinobu Inoue 15705a244b4SYoshinobu Inoue return(0); 15805a244b4SYoshinobu Inoue } 15905a244b4SYoshinobu Inoue 16005a244b4SYoshinobu Inoue /* 16105a244b4SYoshinobu Inoue * This function appends a Hop-by-Hop option or a Destination option 16205a244b4SYoshinobu Inoue * into an ancillary data object that has been initialized by 16305a244b4SYoshinobu Inoue * inet6_option_init(). This function returns a pointer to the 8-bit 16405a244b4SYoshinobu Inoue * option type field that starts the option on success, or NULL on an 16505a244b4SYoshinobu Inoue * error. 16605a244b4SYoshinobu Inoue * The difference between this function and inet6_option_append() is 16705a244b4SYoshinobu Inoue * that the latter copies the contents of a previously built option into 16805a244b4SYoshinobu Inoue * the ancillary data object while the current function returns a 16905a244b4SYoshinobu Inoue * pointer to the space in the data object where the option's TLV must 17005a244b4SYoshinobu Inoue * then be built by the caller. 17105a244b4SYoshinobu Inoue * 17205a244b4SYoshinobu Inoue */ 17305a244b4SYoshinobu Inoue u_int8_t * 17405a244b4SYoshinobu Inoue inet6_option_alloc(cmsg, datalen, multx, plusy) 17505a244b4SYoshinobu Inoue struct cmsghdr *cmsg; 17605a244b4SYoshinobu Inoue int datalen; 17705a244b4SYoshinobu Inoue int multx; 17805a244b4SYoshinobu Inoue int plusy; 17905a244b4SYoshinobu Inoue { 18005a244b4SYoshinobu Inoue int padlen, off; 1818fb3f3f6SDavid E. O'Brien u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len; 18205a244b4SYoshinobu Inoue u_int8_t *retval; 18305a244b4SYoshinobu Inoue struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 18405a244b4SYoshinobu Inoue 18505a244b4SYoshinobu Inoue /* argument validation */ 18605a244b4SYoshinobu Inoue if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 18705a244b4SYoshinobu Inoue return(NULL); 18805a244b4SYoshinobu Inoue if (plusy < 0 || plusy > 7) 18905a244b4SYoshinobu Inoue return(NULL); 19005a244b4SYoshinobu Inoue 19105a244b4SYoshinobu Inoue /* 19205a244b4SYoshinobu Inoue * If this is the first option, allocate space for the 19305a244b4SYoshinobu Inoue * first 2 bytes(for next header and length fields) of 19405a244b4SYoshinobu Inoue * the option header. 19505a244b4SYoshinobu Inoue */ 19605a244b4SYoshinobu Inoue if (bp == (u_char *)eh) { 19705a244b4SYoshinobu Inoue bp += 2; 19805a244b4SYoshinobu Inoue cmsg->cmsg_len += 2; 19905a244b4SYoshinobu Inoue } 20005a244b4SYoshinobu Inoue 20105a244b4SYoshinobu Inoue /* calculate pad length before the option. */ 20205a244b4SYoshinobu Inoue off = bp - (u_char *)eh; 20305a244b4SYoshinobu Inoue padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 20405a244b4SYoshinobu Inoue (off % multx); 20505a244b4SYoshinobu Inoue padlen += plusy; 206f95d4633SHajimu UMEMOTO padlen %= multx; /* keep the pad as short as possible */ 20705a244b4SYoshinobu Inoue /* insert padding */ 20805a244b4SYoshinobu Inoue inet6_insert_padopt(bp, padlen); 20905a244b4SYoshinobu Inoue cmsg->cmsg_len += padlen; 21005a244b4SYoshinobu Inoue bp += padlen; 21105a244b4SYoshinobu Inoue 21205a244b4SYoshinobu Inoue /* keep space to store specified length of data */ 21305a244b4SYoshinobu Inoue retval = bp; 21405a244b4SYoshinobu Inoue bp += datalen; 21505a244b4SYoshinobu Inoue cmsg->cmsg_len += datalen; 21605a244b4SYoshinobu Inoue 21705a244b4SYoshinobu Inoue /* calculate pad length after the option and insert the padding */ 21805a244b4SYoshinobu Inoue off = bp - (u_char *)eh; 21905a244b4SYoshinobu Inoue padlen = ((off + 7) & ~7) - off; 22005a244b4SYoshinobu Inoue inet6_insert_padopt(bp, padlen); 22105a244b4SYoshinobu Inoue bp += padlen; 22205a244b4SYoshinobu Inoue cmsg->cmsg_len += padlen; 22305a244b4SYoshinobu Inoue 22405a244b4SYoshinobu Inoue /* update the length field of the ip6 option header */ 22505a244b4SYoshinobu Inoue eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 22605a244b4SYoshinobu Inoue 22705a244b4SYoshinobu Inoue return(retval); 22805a244b4SYoshinobu Inoue } 22905a244b4SYoshinobu Inoue 23005a244b4SYoshinobu Inoue /* 23105a244b4SYoshinobu Inoue * This function processes the next Hop-by-Hop option or Destination 23205a244b4SYoshinobu Inoue * option in an ancillary data object. If another option remains to be 23305a244b4SYoshinobu Inoue * processed, the return value of the function is 0 and *tptrp points to 23405a244b4SYoshinobu Inoue * the 8-bit option type field (which is followed by the 8-bit option 23505a244b4SYoshinobu Inoue * data length, followed by the option data). If no more options remain 23605a244b4SYoshinobu Inoue * to be processed, the return value is -1 and *tptrp is NULL. If an 23705a244b4SYoshinobu Inoue * error occurs, the return value is -1 and *tptrp is not NULL. 23805a244b4SYoshinobu Inoue * (RFC 2292, 6.3.5) 23905a244b4SYoshinobu Inoue */ 24005a244b4SYoshinobu Inoue int 24105a244b4SYoshinobu Inoue inet6_option_next(cmsg, tptrp) 24205a244b4SYoshinobu Inoue const struct cmsghdr *cmsg; 24305a244b4SYoshinobu Inoue u_int8_t **tptrp; 24405a244b4SYoshinobu Inoue { 24505a244b4SYoshinobu Inoue struct ip6_ext *ip6e; 24605a244b4SYoshinobu Inoue int hdrlen, optlen; 24705a244b4SYoshinobu Inoue u_int8_t *lim; 24805a244b4SYoshinobu Inoue 24905a244b4SYoshinobu Inoue if (cmsg->cmsg_level != IPPROTO_IPV6 || 25005a244b4SYoshinobu Inoue (cmsg->cmsg_type != IPV6_HOPOPTS && 25105a244b4SYoshinobu Inoue cmsg->cmsg_type != IPV6_DSTOPTS)) 25205a244b4SYoshinobu Inoue return(-1); 25305a244b4SYoshinobu Inoue 25405a244b4SYoshinobu Inoue /* message length validation */ 25505a244b4SYoshinobu Inoue if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 25605a244b4SYoshinobu Inoue return(-1); 25705a244b4SYoshinobu Inoue ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 25805a244b4SYoshinobu Inoue hdrlen = (ip6e->ip6e_len + 1) << 3; 25905a244b4SYoshinobu Inoue if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 26005a244b4SYoshinobu Inoue return(-1); 26105a244b4SYoshinobu Inoue 26205a244b4SYoshinobu Inoue /* 26305a244b4SYoshinobu Inoue * If the caller does not specify the starting point, 26405a244b4SYoshinobu Inoue * simply return the 1st option. 26505a244b4SYoshinobu Inoue * Otherwise, search the option list for the next option. 26605a244b4SYoshinobu Inoue */ 26705a244b4SYoshinobu Inoue lim = (u_int8_t *)ip6e + hdrlen; 26805a244b4SYoshinobu Inoue if (*tptrp == NULL) 26905a244b4SYoshinobu Inoue *tptrp = (u_int8_t *)(ip6e + 1); 27005a244b4SYoshinobu Inoue else { 27105a244b4SYoshinobu Inoue if ((optlen = ip6optlen(*tptrp, lim)) == 0) 27205a244b4SYoshinobu Inoue return(-1); 27305a244b4SYoshinobu Inoue 27405a244b4SYoshinobu Inoue *tptrp = *tptrp + optlen; 27505a244b4SYoshinobu Inoue } 27605a244b4SYoshinobu Inoue if (*tptrp >= lim) { /* there is no option */ 27705a244b4SYoshinobu Inoue *tptrp = NULL; 27805a244b4SYoshinobu Inoue return(-1); 27905a244b4SYoshinobu Inoue } 28005a244b4SYoshinobu Inoue /* 28105a244b4SYoshinobu Inoue * Finally, checks if the next option is safely stored in the 28205a244b4SYoshinobu Inoue * cmsg data. 28305a244b4SYoshinobu Inoue */ 28405a244b4SYoshinobu Inoue if (ip6optlen(*tptrp, lim) == 0) 28505a244b4SYoshinobu Inoue return(-1); 28605a244b4SYoshinobu Inoue else 28705a244b4SYoshinobu Inoue return(0); 28805a244b4SYoshinobu Inoue } 28905a244b4SYoshinobu Inoue 29005a244b4SYoshinobu Inoue /* 29105a244b4SYoshinobu Inoue * This function is similar to the inet6_option_next() function, 29205a244b4SYoshinobu Inoue * except this function lets the caller specify the option type to be 29305a244b4SYoshinobu Inoue * searched for, instead of always returning the next option in the 29405a244b4SYoshinobu Inoue * ancillary data object. 29505a244b4SYoshinobu Inoue * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think 29605a244b4SYoshinobu Inoue * it's a typo. The variable should be type of u_int8_t **. 29705a244b4SYoshinobu Inoue */ 29805a244b4SYoshinobu Inoue int 29905a244b4SYoshinobu Inoue inet6_option_find(cmsg, tptrp, type) 30005a244b4SYoshinobu Inoue const struct cmsghdr *cmsg; 30105a244b4SYoshinobu Inoue u_int8_t **tptrp; 30205a244b4SYoshinobu Inoue int type; 30305a244b4SYoshinobu Inoue { 30405a244b4SYoshinobu Inoue struct ip6_ext *ip6e; 30505a244b4SYoshinobu Inoue int hdrlen, optlen; 30605a244b4SYoshinobu Inoue u_int8_t *optp, *lim; 30705a244b4SYoshinobu Inoue 30805a244b4SYoshinobu Inoue if (cmsg->cmsg_level != IPPROTO_IPV6 || 30905a244b4SYoshinobu Inoue (cmsg->cmsg_type != IPV6_HOPOPTS && 31005a244b4SYoshinobu Inoue cmsg->cmsg_type != IPV6_DSTOPTS)) 31105a244b4SYoshinobu Inoue return(-1); 31205a244b4SYoshinobu Inoue 31305a244b4SYoshinobu Inoue /* message length validation */ 31405a244b4SYoshinobu Inoue if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 31505a244b4SYoshinobu Inoue return(-1); 31605a244b4SYoshinobu Inoue ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 31705a244b4SYoshinobu Inoue hdrlen = (ip6e->ip6e_len + 1) << 3; 31805a244b4SYoshinobu Inoue if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 31905a244b4SYoshinobu Inoue return(-1); 32005a244b4SYoshinobu Inoue 32105a244b4SYoshinobu Inoue /* 32205a244b4SYoshinobu Inoue * If the caller does not specify the starting point, 32305a244b4SYoshinobu Inoue * search from the beginning of the option list. 32405a244b4SYoshinobu Inoue * Otherwise, search from *the next option* of the specified point. 32505a244b4SYoshinobu Inoue */ 32605a244b4SYoshinobu Inoue lim = (u_int8_t *)ip6e + hdrlen; 32705a244b4SYoshinobu Inoue if (*tptrp == NULL) 32805a244b4SYoshinobu Inoue *tptrp = (u_int8_t *)(ip6e + 1); 32905a244b4SYoshinobu Inoue else { 33005a244b4SYoshinobu Inoue if ((optlen = ip6optlen(*tptrp, lim)) == 0) 33105a244b4SYoshinobu Inoue return(-1); 33205a244b4SYoshinobu Inoue 33305a244b4SYoshinobu Inoue *tptrp = *tptrp + optlen; 33405a244b4SYoshinobu Inoue } 33505a244b4SYoshinobu Inoue for (optp = *tptrp; optp < lim; optp += optlen) { 33605a244b4SYoshinobu Inoue if (*optp == type) { 33705a244b4SYoshinobu Inoue *tptrp = optp; 33805a244b4SYoshinobu Inoue return(0); 33905a244b4SYoshinobu Inoue } 34005a244b4SYoshinobu Inoue if ((optlen = ip6optlen(optp, lim)) == 0) 34105a244b4SYoshinobu Inoue return(-1); 34205a244b4SYoshinobu Inoue } 34305a244b4SYoshinobu Inoue 34405a244b4SYoshinobu Inoue /* search failed */ 34505a244b4SYoshinobu Inoue *tptrp = NULL; 34605a244b4SYoshinobu Inoue return(-1); 34705a244b4SYoshinobu Inoue } 34805a244b4SYoshinobu Inoue 34905a244b4SYoshinobu Inoue /* 35005a244b4SYoshinobu Inoue * Calculate the length of a given IPv6 option. Also checks 35105a244b4SYoshinobu Inoue * if the option is safely stored in user's buffer according to the 35205a244b4SYoshinobu Inoue * calculated length and the limitation of the buffer. 35305a244b4SYoshinobu Inoue */ 35405a244b4SYoshinobu Inoue static int 35505a244b4SYoshinobu Inoue ip6optlen(opt, lim) 35605a244b4SYoshinobu Inoue u_int8_t *opt, *lim; 35705a244b4SYoshinobu Inoue { 35805a244b4SYoshinobu Inoue int optlen; 35905a244b4SYoshinobu Inoue 36005a244b4SYoshinobu Inoue if (*opt == IP6OPT_PAD1) 36105a244b4SYoshinobu Inoue optlen = 1; 36205a244b4SYoshinobu Inoue else { 36305a244b4SYoshinobu Inoue /* is there enough space to store type and len? */ 36405a244b4SYoshinobu Inoue if (opt + 2 > lim) 36505a244b4SYoshinobu Inoue return(0); 36605a244b4SYoshinobu Inoue optlen = *(opt + 1) + 2; 36705a244b4SYoshinobu Inoue } 36805a244b4SYoshinobu Inoue if (opt + optlen <= lim) 36905a244b4SYoshinobu Inoue return(optlen); 37005a244b4SYoshinobu Inoue 37105a244b4SYoshinobu Inoue return(0); 37205a244b4SYoshinobu Inoue } 37305a244b4SYoshinobu Inoue 37405a244b4SYoshinobu Inoue static void 37505a244b4SYoshinobu Inoue inet6_insert_padopt(u_char *p, int len) 37605a244b4SYoshinobu Inoue { 37705a244b4SYoshinobu Inoue switch(len) { 37805a244b4SYoshinobu Inoue case 0: 37905a244b4SYoshinobu Inoue return; 38005a244b4SYoshinobu Inoue case 1: 38105a244b4SYoshinobu Inoue p[0] = IP6OPT_PAD1; 38205a244b4SYoshinobu Inoue return; 38305a244b4SYoshinobu Inoue default: 38405a244b4SYoshinobu Inoue p[0] = IP6OPT_PADN; 38505a244b4SYoshinobu Inoue p[1] = len - 2; 38605a244b4SYoshinobu Inoue memset(&p[2], 0, len - 2); 38705a244b4SYoshinobu Inoue return; 38805a244b4SYoshinobu Inoue } 38905a244b4SYoshinobu Inoue } 390f95d4633SHajimu UMEMOTO 391f95d4633SHajimu UMEMOTO /* 392f95d4633SHajimu UMEMOTO * The following functions are defined in a successor of RFC2292, aka 393f95d4633SHajimu UMEMOTO * rfc2292bis. 394f95d4633SHajimu UMEMOTO */ 395f95d4633SHajimu UMEMOTO 396f95d4633SHajimu UMEMOTO int 397f95d4633SHajimu UMEMOTO inet6_opt_init(void *extbuf, socklen_t extlen) 398f95d4633SHajimu UMEMOTO { 399f95d4633SHajimu UMEMOTO struct ip6_ext *ext = (struct ip6_ext *)extbuf; 400f95d4633SHajimu UMEMOTO 401f95d4633SHajimu UMEMOTO if (extlen < 0 || (extlen % 8)) 402f95d4633SHajimu UMEMOTO return(-1); 403f95d4633SHajimu UMEMOTO 404f95d4633SHajimu UMEMOTO if (ext) { 405f95d4633SHajimu UMEMOTO if (extlen == 0) 406f95d4633SHajimu UMEMOTO return(-1); 407f95d4633SHajimu UMEMOTO ext->ip6e_len = (extlen >> 3) - 1; 408f95d4633SHajimu UMEMOTO } 409f95d4633SHajimu UMEMOTO 410f95d4633SHajimu UMEMOTO return(2); /* sizeof the next and the length fields */ 411f95d4633SHajimu UMEMOTO } 412f95d4633SHajimu UMEMOTO 413f95d4633SHajimu UMEMOTO int 414f95d4633SHajimu UMEMOTO inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 415f95d4633SHajimu UMEMOTO socklen_t len, u_int8_t align, void **databufp) 416f95d4633SHajimu UMEMOTO { 417f95d4633SHajimu UMEMOTO int currentlen = offset, padlen = 0; 418f95d4633SHajimu UMEMOTO 419f95d4633SHajimu UMEMOTO /* 420f95d4633SHajimu UMEMOTO * The option type must have a value from 2 to 255, inclusive. 421f95d4633SHajimu UMEMOTO * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) 422f95d4633SHajimu UMEMOTO */ 4232daa2369SHajimu UMEMOTO if (type < 2) 424f95d4633SHajimu UMEMOTO return(-1); 425f95d4633SHajimu UMEMOTO 426f95d4633SHajimu UMEMOTO /* 427f95d4633SHajimu UMEMOTO * The option data length must have a value between 0 and 255, 428f95d4633SHajimu UMEMOTO * inclusive, and is the length of the option data that follows. 429f95d4633SHajimu UMEMOTO */ 430f95d4633SHajimu UMEMOTO if (len < 0 || len > 255) 431f95d4633SHajimu UMEMOTO return(-1); 432f95d4633SHajimu UMEMOTO 433f95d4633SHajimu UMEMOTO /* 434f95d4633SHajimu UMEMOTO * The align parameter must have a value of 1, 2, 4, or 8. 435f95d4633SHajimu UMEMOTO * The align value can not exceed the value of len. 436f95d4633SHajimu UMEMOTO */ 437f95d4633SHajimu UMEMOTO if (align != 1 && align != 2 && align != 4 && align != 8) 438f95d4633SHajimu UMEMOTO return(-1); 439f95d4633SHajimu UMEMOTO if (align > len) 440f95d4633SHajimu UMEMOTO return(-1); 441f95d4633SHajimu UMEMOTO 442f95d4633SHajimu UMEMOTO /* Calculate the padding length. */ 443f95d4633SHajimu UMEMOTO currentlen += 2 + len; /* 2 means "type + len" */ 444f95d4633SHajimu UMEMOTO if (currentlen % align) 445f95d4633SHajimu UMEMOTO padlen = align - (currentlen % align); 446f95d4633SHajimu UMEMOTO 447f95d4633SHajimu UMEMOTO /* The option must fit in the extension header buffer. */ 448f95d4633SHajimu UMEMOTO currentlen += padlen; 449f95d4633SHajimu UMEMOTO if (extlen && /* XXX: right? */ 450f95d4633SHajimu UMEMOTO currentlen > extlen) 451f95d4633SHajimu UMEMOTO return(-1); 452f95d4633SHajimu UMEMOTO 453f95d4633SHajimu UMEMOTO if (extbuf) { 454f95d4633SHajimu UMEMOTO u_int8_t *optp = (u_int8_t *)extbuf + offset; 455f95d4633SHajimu UMEMOTO 456f95d4633SHajimu UMEMOTO if (padlen == 1) { 457f95d4633SHajimu UMEMOTO /* insert a Pad1 option */ 458f95d4633SHajimu UMEMOTO *optp = IP6OPT_PAD1; 459f95d4633SHajimu UMEMOTO optp++; 460f95d4633SHajimu UMEMOTO } 461f95d4633SHajimu UMEMOTO else if (padlen > 0) { 462f95d4633SHajimu UMEMOTO /* insert a PadN option for alignment */ 463f95d4633SHajimu UMEMOTO *optp++ = IP6OPT_PADN; 464f95d4633SHajimu UMEMOTO *optp++ = padlen - 2; 465f95d4633SHajimu UMEMOTO memset(optp, 0, padlen - 2); 466f95d4633SHajimu UMEMOTO optp += (padlen - 2); 467f95d4633SHajimu UMEMOTO } 468f95d4633SHajimu UMEMOTO 469f95d4633SHajimu UMEMOTO *optp++ = type; 470f95d4633SHajimu UMEMOTO *optp++ = len; 471f95d4633SHajimu UMEMOTO 472f95d4633SHajimu UMEMOTO *databufp = optp; 473f95d4633SHajimu UMEMOTO } 474f95d4633SHajimu UMEMOTO 475f95d4633SHajimu UMEMOTO return(currentlen); 476f95d4633SHajimu UMEMOTO } 477f95d4633SHajimu UMEMOTO 478f95d4633SHajimu UMEMOTO int 479f95d4633SHajimu UMEMOTO inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) 480f95d4633SHajimu UMEMOTO { 481f95d4633SHajimu UMEMOTO int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;; 482f95d4633SHajimu UMEMOTO 483f95d4633SHajimu UMEMOTO if (extbuf) { 484f95d4633SHajimu UMEMOTO u_int8_t *padp; 485f95d4633SHajimu UMEMOTO int padlen = updatelen - offset; 486f95d4633SHajimu UMEMOTO 487f95d4633SHajimu UMEMOTO if (updatelen > extlen) 488f95d4633SHajimu UMEMOTO return(-1); 489f95d4633SHajimu UMEMOTO 490f95d4633SHajimu UMEMOTO padp = (u_int8_t *)extbuf + offset; 491f95d4633SHajimu UMEMOTO if (padlen == 1) 492f95d4633SHajimu UMEMOTO *padp = IP6OPT_PAD1; 493f95d4633SHajimu UMEMOTO else if (padlen > 0) { 494f95d4633SHajimu UMEMOTO *padp++ = IP6OPT_PADN; 495f95d4633SHajimu UMEMOTO *padp++ = (padlen - 2); 496f95d4633SHajimu UMEMOTO memset(padp, 0, padlen - 2); 497f95d4633SHajimu UMEMOTO } 498f95d4633SHajimu UMEMOTO } 499f95d4633SHajimu UMEMOTO 500f95d4633SHajimu UMEMOTO return(updatelen); 501f95d4633SHajimu UMEMOTO } 502f95d4633SHajimu UMEMOTO 503f95d4633SHajimu UMEMOTO int 504f95d4633SHajimu UMEMOTO inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) 505f95d4633SHajimu UMEMOTO { 506f95d4633SHajimu UMEMOTO 507f95d4633SHajimu UMEMOTO memcpy((u_int8_t *)databuf + offset, val, vallen); 508f95d4633SHajimu UMEMOTO return(offset + vallen); 509f95d4633SHajimu UMEMOTO } 510f95d4633SHajimu UMEMOTO 511f95d4633SHajimu UMEMOTO int 512f95d4633SHajimu UMEMOTO inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, 5138827557eSHajimu UMEMOTO socklen_t *lenp, void **databufp) 514f95d4633SHajimu UMEMOTO { 515f95d4633SHajimu UMEMOTO u_int8_t *optp, *lim; 516f95d4633SHajimu UMEMOTO int optlen; 517f95d4633SHajimu UMEMOTO 518f95d4633SHajimu UMEMOTO /* Validate extlen. XXX: is the variable really necessary?? */ 519f95d4633SHajimu UMEMOTO if (extlen == 0 || (extlen % 8)) 520f95d4633SHajimu UMEMOTO return(-1); 521f95d4633SHajimu UMEMOTO lim = (u_int8_t *)extbuf + extlen; 522f95d4633SHajimu UMEMOTO 523f95d4633SHajimu UMEMOTO /* 524f95d4633SHajimu UMEMOTO * If this is the first time this function called for this options 525f95d4633SHajimu UMEMOTO * header, simply return the 1st option. 526f95d4633SHajimu UMEMOTO * Otherwise, search the option list for the next option. 527f95d4633SHajimu UMEMOTO */ 528f95d4633SHajimu UMEMOTO if (offset == 0) { 529f95d4633SHajimu UMEMOTO optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 530f95d4633SHajimu UMEMOTO } 531f95d4633SHajimu UMEMOTO else 532f95d4633SHajimu UMEMOTO optp = (u_int8_t *)extbuf + offset; 533f95d4633SHajimu UMEMOTO 534f95d4633SHajimu UMEMOTO /* Find the next option skipping any padding options. */ 535f95d4633SHajimu UMEMOTO while(optp < lim) { 536f95d4633SHajimu UMEMOTO switch(*optp) { 537f95d4633SHajimu UMEMOTO case IP6OPT_PAD1: 538f95d4633SHajimu UMEMOTO optp++; 539f95d4633SHajimu UMEMOTO break; 540f95d4633SHajimu UMEMOTO case IP6OPT_PADN: 541f95d4633SHajimu UMEMOTO if ((optlen = ip6optlen(optp, lim)) == 0) 542f95d4633SHajimu UMEMOTO goto optend; 543f95d4633SHajimu UMEMOTO optp += optlen; 544f95d4633SHajimu UMEMOTO break; 545f95d4633SHajimu UMEMOTO default: /* found */ 546f95d4633SHajimu UMEMOTO if ((optlen = ip6optlen(optp, lim)) == 0) 547f95d4633SHajimu UMEMOTO goto optend; 548f95d4633SHajimu UMEMOTO *typep = *optp; 549f95d4633SHajimu UMEMOTO *lenp = optlen - 2; 550f95d4633SHajimu UMEMOTO *databufp = optp + 2; 551f95d4633SHajimu UMEMOTO return(optp + optlen - (u_int8_t *)extbuf); 552f95d4633SHajimu UMEMOTO } 553f95d4633SHajimu UMEMOTO } 554f95d4633SHajimu UMEMOTO 555f95d4633SHajimu UMEMOTO optend: 556f95d4633SHajimu UMEMOTO *databufp = NULL; /* for safety */ 557f95d4633SHajimu UMEMOTO return(-1); 558f95d4633SHajimu UMEMOTO } 559f95d4633SHajimu UMEMOTO 560f95d4633SHajimu UMEMOTO int 561f95d4633SHajimu UMEMOTO inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 562f95d4633SHajimu UMEMOTO socklen_t *lenp, void **databufp) 563f95d4633SHajimu UMEMOTO { 564f95d4633SHajimu UMEMOTO u_int8_t *optp, *lim; 565f95d4633SHajimu UMEMOTO int optlen; 566f95d4633SHajimu UMEMOTO 567f95d4633SHajimu UMEMOTO /* Validate extlen. XXX: is the variable really necessary?? */ 568f95d4633SHajimu UMEMOTO if (extlen == 0 || (extlen % 8)) 569f95d4633SHajimu UMEMOTO return(-1); 570f95d4633SHajimu UMEMOTO lim = (u_int8_t *)extbuf + extlen; 571f95d4633SHajimu UMEMOTO 572f95d4633SHajimu UMEMOTO /* 573f95d4633SHajimu UMEMOTO * If this is the first time this function called for this options 574f95d4633SHajimu UMEMOTO * header, simply return the 1st option. 575f95d4633SHajimu UMEMOTO * Otherwise, search the option list for the next option. 576f95d4633SHajimu UMEMOTO */ 577f95d4633SHajimu UMEMOTO if (offset == 0) { 578f95d4633SHajimu UMEMOTO optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 579f95d4633SHajimu UMEMOTO } 580f95d4633SHajimu UMEMOTO else 581f95d4633SHajimu UMEMOTO optp = (u_int8_t *)extbuf + offset; 582f95d4633SHajimu UMEMOTO 583f95d4633SHajimu UMEMOTO /* Find the specified option */ 584f95d4633SHajimu UMEMOTO while(optp < lim) { 585f95d4633SHajimu UMEMOTO if ((optlen = ip6optlen(optp, lim)) == 0) 586f95d4633SHajimu UMEMOTO goto optend; 587f95d4633SHajimu UMEMOTO 588f95d4633SHajimu UMEMOTO if (*optp == type) { /* found */ 589f95d4633SHajimu UMEMOTO *lenp = optlen - 2; 590f95d4633SHajimu UMEMOTO *databufp = optp + 2; 591f95d4633SHajimu UMEMOTO return(optp + optlen - (u_int8_t *)extbuf); 592f95d4633SHajimu UMEMOTO } 593f95d4633SHajimu UMEMOTO 594f95d4633SHajimu UMEMOTO optp += optlen; 595f95d4633SHajimu UMEMOTO } 596f95d4633SHajimu UMEMOTO 597f95d4633SHajimu UMEMOTO optend: 598f95d4633SHajimu UMEMOTO *databufp = NULL; /* for safety */ 599f95d4633SHajimu UMEMOTO return(-1); 600f95d4633SHajimu UMEMOTO } 601f95d4633SHajimu UMEMOTO 602f95d4633SHajimu UMEMOTO int 603f95d4633SHajimu UMEMOTO inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) 604f95d4633SHajimu UMEMOTO { 605f95d4633SHajimu UMEMOTO 606f95d4633SHajimu UMEMOTO /* we can't assume alignment here */ 607f95d4633SHajimu UMEMOTO memcpy(val, (u_int8_t *)databuf + offset, vallen); 608f95d4633SHajimu UMEMOTO 609f95d4633SHajimu UMEMOTO return(offset + vallen); 610f95d4633SHajimu UMEMOTO } 611