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); 116f95d4633SHajimu UMEMOTO if (typep[0] > 255) 117f95d4633SHajimu UMEMOTO return(-1); 11805a244b4SYoshinobu Inoue 11905a244b4SYoshinobu Inoue /* 12005a244b4SYoshinobu Inoue * If this is the first option, allocate space for the 12105a244b4SYoshinobu Inoue * first 2 bytes(for next header and length fields) of 12205a244b4SYoshinobu Inoue * the option header. 12305a244b4SYoshinobu Inoue */ 12405a244b4SYoshinobu Inoue if (bp == (u_char *)eh) { 12505a244b4SYoshinobu Inoue bp += 2; 12605a244b4SYoshinobu Inoue cmsg->cmsg_len += 2; 12705a244b4SYoshinobu Inoue } 12805a244b4SYoshinobu Inoue 12905a244b4SYoshinobu Inoue /* calculate pad length before the option. */ 13005a244b4SYoshinobu Inoue off = bp - (u_char *)eh; 13105a244b4SYoshinobu Inoue padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 13205a244b4SYoshinobu Inoue (off % multx); 13305a244b4SYoshinobu Inoue padlen += plusy; 134f95d4633SHajimu UMEMOTO padlen %= multx; /* keep the pad as short as possible */ 13505a244b4SYoshinobu Inoue /* insert padding */ 13605a244b4SYoshinobu Inoue inet6_insert_padopt(bp, padlen); 13705a244b4SYoshinobu Inoue cmsg->cmsg_len += padlen; 13805a244b4SYoshinobu Inoue bp += padlen; 13905a244b4SYoshinobu Inoue 14005a244b4SYoshinobu Inoue /* copy the option */ 14105a244b4SYoshinobu Inoue if (typep[0] == IP6OPT_PAD1) 14205a244b4SYoshinobu Inoue optlen = 1; 14305a244b4SYoshinobu Inoue else 14405a244b4SYoshinobu Inoue optlen = typep[1] + 2; 14505a244b4SYoshinobu Inoue memcpy(bp, typep, optlen); 14605a244b4SYoshinobu Inoue bp += optlen; 14705a244b4SYoshinobu Inoue cmsg->cmsg_len += optlen; 14805a244b4SYoshinobu Inoue 14905a244b4SYoshinobu Inoue /* calculate pad length after the option and insert the padding */ 15005a244b4SYoshinobu Inoue off = bp - (u_char *)eh; 15105a244b4SYoshinobu Inoue padlen = ((off + 7) & ~7) - off; 15205a244b4SYoshinobu Inoue inet6_insert_padopt(bp, padlen); 15305a244b4SYoshinobu Inoue bp += padlen; 15405a244b4SYoshinobu Inoue cmsg->cmsg_len += padlen; 15505a244b4SYoshinobu Inoue 15605a244b4SYoshinobu Inoue /* update the length field of the ip6 option header */ 15705a244b4SYoshinobu Inoue eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 15805a244b4SYoshinobu Inoue 15905a244b4SYoshinobu Inoue return(0); 16005a244b4SYoshinobu Inoue } 16105a244b4SYoshinobu Inoue 16205a244b4SYoshinobu Inoue /* 16305a244b4SYoshinobu Inoue * This function appends a Hop-by-Hop option or a Destination option 16405a244b4SYoshinobu Inoue * into an ancillary data object that has been initialized by 16505a244b4SYoshinobu Inoue * inet6_option_init(). This function returns a pointer to the 8-bit 16605a244b4SYoshinobu Inoue * option type field that starts the option on success, or NULL on an 16705a244b4SYoshinobu Inoue * error. 16805a244b4SYoshinobu Inoue * The difference between this function and inet6_option_append() is 16905a244b4SYoshinobu Inoue * that the latter copies the contents of a previously built option into 17005a244b4SYoshinobu Inoue * the ancillary data object while the current function returns a 17105a244b4SYoshinobu Inoue * pointer to the space in the data object where the option's TLV must 17205a244b4SYoshinobu Inoue * then be built by the caller. 17305a244b4SYoshinobu Inoue * 17405a244b4SYoshinobu Inoue */ 17505a244b4SYoshinobu Inoue u_int8_t * 17605a244b4SYoshinobu Inoue inet6_option_alloc(cmsg, datalen, multx, plusy) 17705a244b4SYoshinobu Inoue struct cmsghdr *cmsg; 17805a244b4SYoshinobu Inoue int datalen; 17905a244b4SYoshinobu Inoue int multx; 18005a244b4SYoshinobu Inoue int plusy; 18105a244b4SYoshinobu Inoue { 18205a244b4SYoshinobu Inoue int padlen, off; 1838fb3f3f6SDavid E. O'Brien u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len; 18405a244b4SYoshinobu Inoue u_int8_t *retval; 18505a244b4SYoshinobu Inoue struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 18605a244b4SYoshinobu Inoue 18705a244b4SYoshinobu Inoue /* argument validation */ 18805a244b4SYoshinobu Inoue if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 18905a244b4SYoshinobu Inoue return(NULL); 19005a244b4SYoshinobu Inoue if (plusy < 0 || plusy > 7) 19105a244b4SYoshinobu Inoue return(NULL); 19205a244b4SYoshinobu Inoue 19305a244b4SYoshinobu Inoue /* 19405a244b4SYoshinobu Inoue * If this is the first option, allocate space for the 19505a244b4SYoshinobu Inoue * first 2 bytes(for next header and length fields) of 19605a244b4SYoshinobu Inoue * the option header. 19705a244b4SYoshinobu Inoue */ 19805a244b4SYoshinobu Inoue if (bp == (u_char *)eh) { 19905a244b4SYoshinobu Inoue bp += 2; 20005a244b4SYoshinobu Inoue cmsg->cmsg_len += 2; 20105a244b4SYoshinobu Inoue } 20205a244b4SYoshinobu Inoue 20305a244b4SYoshinobu Inoue /* calculate pad length before the option. */ 20405a244b4SYoshinobu Inoue off = bp - (u_char *)eh; 20505a244b4SYoshinobu Inoue padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 20605a244b4SYoshinobu Inoue (off % multx); 20705a244b4SYoshinobu Inoue padlen += plusy; 208f95d4633SHajimu UMEMOTO padlen %= multx; /* keep the pad as short as possible */ 20905a244b4SYoshinobu Inoue /* insert padding */ 21005a244b4SYoshinobu Inoue inet6_insert_padopt(bp, padlen); 21105a244b4SYoshinobu Inoue cmsg->cmsg_len += padlen; 21205a244b4SYoshinobu Inoue bp += padlen; 21305a244b4SYoshinobu Inoue 21405a244b4SYoshinobu Inoue /* keep space to store specified length of data */ 21505a244b4SYoshinobu Inoue retval = bp; 21605a244b4SYoshinobu Inoue bp += datalen; 21705a244b4SYoshinobu Inoue cmsg->cmsg_len += datalen; 21805a244b4SYoshinobu Inoue 21905a244b4SYoshinobu Inoue /* calculate pad length after the option and insert the padding */ 22005a244b4SYoshinobu Inoue off = bp - (u_char *)eh; 22105a244b4SYoshinobu Inoue padlen = ((off + 7) & ~7) - off; 22205a244b4SYoshinobu Inoue inet6_insert_padopt(bp, padlen); 22305a244b4SYoshinobu Inoue bp += padlen; 22405a244b4SYoshinobu Inoue cmsg->cmsg_len += padlen; 22505a244b4SYoshinobu Inoue 22605a244b4SYoshinobu Inoue /* update the length field of the ip6 option header */ 22705a244b4SYoshinobu Inoue eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 22805a244b4SYoshinobu Inoue 22905a244b4SYoshinobu Inoue return(retval); 23005a244b4SYoshinobu Inoue } 23105a244b4SYoshinobu Inoue 23205a244b4SYoshinobu Inoue /* 23305a244b4SYoshinobu Inoue * This function processes the next Hop-by-Hop option or Destination 23405a244b4SYoshinobu Inoue * option in an ancillary data object. If another option remains to be 23505a244b4SYoshinobu Inoue * processed, the return value of the function is 0 and *tptrp points to 23605a244b4SYoshinobu Inoue * the 8-bit option type field (which is followed by the 8-bit option 23705a244b4SYoshinobu Inoue * data length, followed by the option data). If no more options remain 23805a244b4SYoshinobu Inoue * to be processed, the return value is -1 and *tptrp is NULL. If an 23905a244b4SYoshinobu Inoue * error occurs, the return value is -1 and *tptrp is not NULL. 24005a244b4SYoshinobu Inoue * (RFC 2292, 6.3.5) 24105a244b4SYoshinobu Inoue */ 24205a244b4SYoshinobu Inoue int 24305a244b4SYoshinobu Inoue inet6_option_next(cmsg, tptrp) 24405a244b4SYoshinobu Inoue const struct cmsghdr *cmsg; 24505a244b4SYoshinobu Inoue u_int8_t **tptrp; 24605a244b4SYoshinobu Inoue { 24705a244b4SYoshinobu Inoue struct ip6_ext *ip6e; 24805a244b4SYoshinobu Inoue int hdrlen, optlen; 24905a244b4SYoshinobu Inoue u_int8_t *lim; 25005a244b4SYoshinobu Inoue 25105a244b4SYoshinobu Inoue if (cmsg->cmsg_level != IPPROTO_IPV6 || 25205a244b4SYoshinobu Inoue (cmsg->cmsg_type != IPV6_HOPOPTS && 25305a244b4SYoshinobu Inoue cmsg->cmsg_type != IPV6_DSTOPTS)) 25405a244b4SYoshinobu Inoue return(-1); 25505a244b4SYoshinobu Inoue 25605a244b4SYoshinobu Inoue /* message length validation */ 25705a244b4SYoshinobu Inoue if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 25805a244b4SYoshinobu Inoue return(-1); 25905a244b4SYoshinobu Inoue ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 26005a244b4SYoshinobu Inoue hdrlen = (ip6e->ip6e_len + 1) << 3; 26105a244b4SYoshinobu Inoue if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 26205a244b4SYoshinobu Inoue return(-1); 26305a244b4SYoshinobu Inoue 26405a244b4SYoshinobu Inoue /* 26505a244b4SYoshinobu Inoue * If the caller does not specify the starting point, 26605a244b4SYoshinobu Inoue * simply return the 1st option. 26705a244b4SYoshinobu Inoue * Otherwise, search the option list for the next option. 26805a244b4SYoshinobu Inoue */ 26905a244b4SYoshinobu Inoue lim = (u_int8_t *)ip6e + hdrlen; 27005a244b4SYoshinobu Inoue if (*tptrp == NULL) 27105a244b4SYoshinobu Inoue *tptrp = (u_int8_t *)(ip6e + 1); 27205a244b4SYoshinobu Inoue else { 27305a244b4SYoshinobu Inoue if ((optlen = ip6optlen(*tptrp, lim)) == 0) 27405a244b4SYoshinobu Inoue return(-1); 27505a244b4SYoshinobu Inoue 27605a244b4SYoshinobu Inoue *tptrp = *tptrp + optlen; 27705a244b4SYoshinobu Inoue } 27805a244b4SYoshinobu Inoue if (*tptrp >= lim) { /* there is no option */ 27905a244b4SYoshinobu Inoue *tptrp = NULL; 28005a244b4SYoshinobu Inoue return(-1); 28105a244b4SYoshinobu Inoue } 28205a244b4SYoshinobu Inoue /* 28305a244b4SYoshinobu Inoue * Finally, checks if the next option is safely stored in the 28405a244b4SYoshinobu Inoue * cmsg data. 28505a244b4SYoshinobu Inoue */ 28605a244b4SYoshinobu Inoue if (ip6optlen(*tptrp, lim) == 0) 28705a244b4SYoshinobu Inoue return(-1); 28805a244b4SYoshinobu Inoue else 28905a244b4SYoshinobu Inoue return(0); 29005a244b4SYoshinobu Inoue } 29105a244b4SYoshinobu Inoue 29205a244b4SYoshinobu Inoue /* 29305a244b4SYoshinobu Inoue * This function is similar to the inet6_option_next() function, 29405a244b4SYoshinobu Inoue * except this function lets the caller specify the option type to be 29505a244b4SYoshinobu Inoue * searched for, instead of always returning the next option in the 29605a244b4SYoshinobu Inoue * ancillary data object. 29705a244b4SYoshinobu Inoue * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think 29805a244b4SYoshinobu Inoue * it's a typo. The variable should be type of u_int8_t **. 29905a244b4SYoshinobu Inoue */ 30005a244b4SYoshinobu Inoue int 30105a244b4SYoshinobu Inoue inet6_option_find(cmsg, tptrp, type) 30205a244b4SYoshinobu Inoue const struct cmsghdr *cmsg; 30305a244b4SYoshinobu Inoue u_int8_t **tptrp; 30405a244b4SYoshinobu Inoue int type; 30505a244b4SYoshinobu Inoue { 30605a244b4SYoshinobu Inoue struct ip6_ext *ip6e; 30705a244b4SYoshinobu Inoue int hdrlen, optlen; 30805a244b4SYoshinobu Inoue u_int8_t *optp, *lim; 30905a244b4SYoshinobu Inoue 31005a244b4SYoshinobu Inoue if (cmsg->cmsg_level != IPPROTO_IPV6 || 31105a244b4SYoshinobu Inoue (cmsg->cmsg_type != IPV6_HOPOPTS && 31205a244b4SYoshinobu Inoue cmsg->cmsg_type != IPV6_DSTOPTS)) 31305a244b4SYoshinobu Inoue return(-1); 31405a244b4SYoshinobu Inoue 31505a244b4SYoshinobu Inoue /* message length validation */ 31605a244b4SYoshinobu Inoue if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 31705a244b4SYoshinobu Inoue return(-1); 31805a244b4SYoshinobu Inoue ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 31905a244b4SYoshinobu Inoue hdrlen = (ip6e->ip6e_len + 1) << 3; 32005a244b4SYoshinobu Inoue if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 32105a244b4SYoshinobu Inoue return(-1); 32205a244b4SYoshinobu Inoue 32305a244b4SYoshinobu Inoue /* 32405a244b4SYoshinobu Inoue * If the caller does not specify the starting point, 32505a244b4SYoshinobu Inoue * search from the beginning of the option list. 32605a244b4SYoshinobu Inoue * Otherwise, search from *the next option* of the specified point. 32705a244b4SYoshinobu Inoue */ 32805a244b4SYoshinobu Inoue lim = (u_int8_t *)ip6e + hdrlen; 32905a244b4SYoshinobu Inoue if (*tptrp == NULL) 33005a244b4SYoshinobu Inoue *tptrp = (u_int8_t *)(ip6e + 1); 33105a244b4SYoshinobu Inoue else { 33205a244b4SYoshinobu Inoue if ((optlen = ip6optlen(*tptrp, lim)) == 0) 33305a244b4SYoshinobu Inoue return(-1); 33405a244b4SYoshinobu Inoue 33505a244b4SYoshinobu Inoue *tptrp = *tptrp + optlen; 33605a244b4SYoshinobu Inoue } 33705a244b4SYoshinobu Inoue for (optp = *tptrp; optp < lim; optp += optlen) { 33805a244b4SYoshinobu Inoue if (*optp == type) { 33905a244b4SYoshinobu Inoue *tptrp = optp; 34005a244b4SYoshinobu Inoue return(0); 34105a244b4SYoshinobu Inoue } 34205a244b4SYoshinobu Inoue if ((optlen = ip6optlen(optp, lim)) == 0) 34305a244b4SYoshinobu Inoue return(-1); 34405a244b4SYoshinobu Inoue } 34505a244b4SYoshinobu Inoue 34605a244b4SYoshinobu Inoue /* search failed */ 34705a244b4SYoshinobu Inoue *tptrp = NULL; 34805a244b4SYoshinobu Inoue return(-1); 34905a244b4SYoshinobu Inoue } 35005a244b4SYoshinobu Inoue 35105a244b4SYoshinobu Inoue /* 35205a244b4SYoshinobu Inoue * Calculate the length of a given IPv6 option. Also checks 35305a244b4SYoshinobu Inoue * if the option is safely stored in user's buffer according to the 35405a244b4SYoshinobu Inoue * calculated length and the limitation of the buffer. 35505a244b4SYoshinobu Inoue */ 35605a244b4SYoshinobu Inoue static int 35705a244b4SYoshinobu Inoue ip6optlen(opt, lim) 35805a244b4SYoshinobu Inoue u_int8_t *opt, *lim; 35905a244b4SYoshinobu Inoue { 36005a244b4SYoshinobu Inoue int optlen; 36105a244b4SYoshinobu Inoue 36205a244b4SYoshinobu Inoue if (*opt == IP6OPT_PAD1) 36305a244b4SYoshinobu Inoue optlen = 1; 36405a244b4SYoshinobu Inoue else { 36505a244b4SYoshinobu Inoue /* is there enough space to store type and len? */ 36605a244b4SYoshinobu Inoue if (opt + 2 > lim) 36705a244b4SYoshinobu Inoue return(0); 36805a244b4SYoshinobu Inoue optlen = *(opt + 1) + 2; 36905a244b4SYoshinobu Inoue } 37005a244b4SYoshinobu Inoue if (opt + optlen <= lim) 37105a244b4SYoshinobu Inoue return(optlen); 37205a244b4SYoshinobu Inoue 37305a244b4SYoshinobu Inoue return(0); 37405a244b4SYoshinobu Inoue } 37505a244b4SYoshinobu Inoue 37605a244b4SYoshinobu Inoue static void 37705a244b4SYoshinobu Inoue inet6_insert_padopt(u_char *p, int len) 37805a244b4SYoshinobu Inoue { 37905a244b4SYoshinobu Inoue switch(len) { 38005a244b4SYoshinobu Inoue case 0: 38105a244b4SYoshinobu Inoue return; 38205a244b4SYoshinobu Inoue case 1: 38305a244b4SYoshinobu Inoue p[0] = IP6OPT_PAD1; 38405a244b4SYoshinobu Inoue return; 38505a244b4SYoshinobu Inoue default: 38605a244b4SYoshinobu Inoue p[0] = IP6OPT_PADN; 38705a244b4SYoshinobu Inoue p[1] = len - 2; 38805a244b4SYoshinobu Inoue memset(&p[2], 0, len - 2); 38905a244b4SYoshinobu Inoue return; 39005a244b4SYoshinobu Inoue } 39105a244b4SYoshinobu Inoue } 392f95d4633SHajimu UMEMOTO 393f95d4633SHajimu UMEMOTO /* 394f95d4633SHajimu UMEMOTO * The following functions are defined in a successor of RFC2292, aka 395f95d4633SHajimu UMEMOTO * rfc2292bis. 396f95d4633SHajimu UMEMOTO */ 397f95d4633SHajimu UMEMOTO 398f95d4633SHajimu UMEMOTO int 399f95d4633SHajimu UMEMOTO inet6_opt_init(void *extbuf, socklen_t extlen) 400f95d4633SHajimu UMEMOTO { 401f95d4633SHajimu UMEMOTO struct ip6_ext *ext = (struct ip6_ext *)extbuf; 402f95d4633SHajimu UMEMOTO 403f95d4633SHajimu UMEMOTO if (extlen < 0 || (extlen % 8)) 404f95d4633SHajimu UMEMOTO return(-1); 405f95d4633SHajimu UMEMOTO 406f95d4633SHajimu UMEMOTO if (ext) { 407f95d4633SHajimu UMEMOTO if (extlen == 0) 408f95d4633SHajimu UMEMOTO return(-1); 409f95d4633SHajimu UMEMOTO ext->ip6e_len = (extlen >> 3) - 1; 410f95d4633SHajimu UMEMOTO } 411f95d4633SHajimu UMEMOTO 412f95d4633SHajimu UMEMOTO return(2); /* sizeof the next and the length fields */ 413f95d4633SHajimu UMEMOTO } 414f95d4633SHajimu UMEMOTO 415f95d4633SHajimu UMEMOTO int 416f95d4633SHajimu UMEMOTO inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 417f95d4633SHajimu UMEMOTO socklen_t len, u_int8_t align, void **databufp) 418f95d4633SHajimu UMEMOTO { 419f95d4633SHajimu UMEMOTO int currentlen = offset, padlen = 0; 420f95d4633SHajimu UMEMOTO 421f95d4633SHajimu UMEMOTO /* 422f95d4633SHajimu UMEMOTO * The option type must have a value from 2 to 255, inclusive. 423f95d4633SHajimu UMEMOTO * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) 424f95d4633SHajimu UMEMOTO */ 425f95d4633SHajimu UMEMOTO if (type < 2 || type > 255) 426f95d4633SHajimu UMEMOTO return(-1); 427f95d4633SHajimu UMEMOTO 428f95d4633SHajimu UMEMOTO /* 429f95d4633SHajimu UMEMOTO * The option data length must have a value between 0 and 255, 430f95d4633SHajimu UMEMOTO * inclusive, and is the length of the option data that follows. 431f95d4633SHajimu UMEMOTO */ 432f95d4633SHajimu UMEMOTO if (len < 0 || len > 255) 433f95d4633SHajimu UMEMOTO return(-1); 434f95d4633SHajimu UMEMOTO 435f95d4633SHajimu UMEMOTO /* 436f95d4633SHajimu UMEMOTO * The align parameter must have a value of 1, 2, 4, or 8. 437f95d4633SHajimu UMEMOTO * The align value can not exceed the value of len. 438f95d4633SHajimu UMEMOTO */ 439f95d4633SHajimu UMEMOTO if (align != 1 && align != 2 && align != 4 && align != 8) 440f95d4633SHajimu UMEMOTO return(-1); 441f95d4633SHajimu UMEMOTO if (align > len) 442f95d4633SHajimu UMEMOTO return(-1); 443f95d4633SHajimu UMEMOTO 444f95d4633SHajimu UMEMOTO /* Calculate the padding length. */ 445f95d4633SHajimu UMEMOTO currentlen += 2 + len; /* 2 means "type + len" */ 446f95d4633SHajimu UMEMOTO if (currentlen % align) 447f95d4633SHajimu UMEMOTO padlen = align - (currentlen % align); 448f95d4633SHajimu UMEMOTO 449f95d4633SHajimu UMEMOTO /* The option must fit in the extension header buffer. */ 450f95d4633SHajimu UMEMOTO currentlen += padlen; 451f95d4633SHajimu UMEMOTO if (extlen && /* XXX: right? */ 452f95d4633SHajimu UMEMOTO currentlen > extlen) 453f95d4633SHajimu UMEMOTO return(-1); 454f95d4633SHajimu UMEMOTO 455f95d4633SHajimu UMEMOTO if (extbuf) { 456f95d4633SHajimu UMEMOTO u_int8_t *optp = (u_int8_t *)extbuf + offset; 457f95d4633SHajimu UMEMOTO 458f95d4633SHajimu UMEMOTO if (padlen == 1) { 459f95d4633SHajimu UMEMOTO /* insert a Pad1 option */ 460f95d4633SHajimu UMEMOTO *optp = IP6OPT_PAD1; 461f95d4633SHajimu UMEMOTO optp++; 462f95d4633SHajimu UMEMOTO } 463f95d4633SHajimu UMEMOTO else if (padlen > 0) { 464f95d4633SHajimu UMEMOTO /* insert a PadN option for alignment */ 465f95d4633SHajimu UMEMOTO *optp++ = IP6OPT_PADN; 466f95d4633SHajimu UMEMOTO *optp++ = padlen - 2; 467f95d4633SHajimu UMEMOTO memset(optp, 0, padlen - 2); 468f95d4633SHajimu UMEMOTO optp += (padlen - 2); 469f95d4633SHajimu UMEMOTO } 470f95d4633SHajimu UMEMOTO 471f95d4633SHajimu UMEMOTO *optp++ = type; 472f95d4633SHajimu UMEMOTO *optp++ = len; 473f95d4633SHajimu UMEMOTO 474f95d4633SHajimu UMEMOTO *databufp = optp; 475f95d4633SHajimu UMEMOTO } 476f95d4633SHajimu UMEMOTO 477f95d4633SHajimu UMEMOTO return(currentlen); 478f95d4633SHajimu UMEMOTO } 479f95d4633SHajimu UMEMOTO 480f95d4633SHajimu UMEMOTO int 481f95d4633SHajimu UMEMOTO inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) 482f95d4633SHajimu UMEMOTO { 483f95d4633SHajimu UMEMOTO int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;; 484f95d4633SHajimu UMEMOTO 485f95d4633SHajimu UMEMOTO if (extbuf) { 486f95d4633SHajimu UMEMOTO u_int8_t *padp; 487f95d4633SHajimu UMEMOTO int padlen = updatelen - offset; 488f95d4633SHajimu UMEMOTO 489f95d4633SHajimu UMEMOTO if (updatelen > extlen) 490f95d4633SHajimu UMEMOTO return(-1); 491f95d4633SHajimu UMEMOTO 492f95d4633SHajimu UMEMOTO padp = (u_int8_t *)extbuf + offset; 493f95d4633SHajimu UMEMOTO if (padlen == 1) 494f95d4633SHajimu UMEMOTO *padp = IP6OPT_PAD1; 495f95d4633SHajimu UMEMOTO else if (padlen > 0) { 496f95d4633SHajimu UMEMOTO *padp++ = IP6OPT_PADN; 497f95d4633SHajimu UMEMOTO *padp++ = (padlen - 2); 498f95d4633SHajimu UMEMOTO memset(padp, 0, padlen - 2); 499f95d4633SHajimu UMEMOTO } 500f95d4633SHajimu UMEMOTO } 501f95d4633SHajimu UMEMOTO 502f95d4633SHajimu UMEMOTO return(updatelen); 503f95d4633SHajimu UMEMOTO } 504f95d4633SHajimu UMEMOTO 505f95d4633SHajimu UMEMOTO int 506f95d4633SHajimu UMEMOTO inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) 507f95d4633SHajimu UMEMOTO { 508f95d4633SHajimu UMEMOTO 509f95d4633SHajimu UMEMOTO memcpy((u_int8_t *)databuf + offset, val, vallen); 510f95d4633SHajimu UMEMOTO return(offset + vallen); 511f95d4633SHajimu UMEMOTO } 512f95d4633SHajimu UMEMOTO 513f95d4633SHajimu UMEMOTO int 514f95d4633SHajimu UMEMOTO inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, 515f95d4633SHajimu UMEMOTO size_t *lenp, void **databufp) 516f95d4633SHajimu UMEMOTO { 517f95d4633SHajimu UMEMOTO u_int8_t *optp, *lim; 518f95d4633SHajimu UMEMOTO int optlen; 519f95d4633SHajimu UMEMOTO 520f95d4633SHajimu UMEMOTO /* Validate extlen. XXX: is the variable really necessary?? */ 521f95d4633SHajimu UMEMOTO if (extlen == 0 || (extlen % 8)) 522f95d4633SHajimu UMEMOTO return(-1); 523f95d4633SHajimu UMEMOTO lim = (u_int8_t *)extbuf + extlen; 524f95d4633SHajimu UMEMOTO 525f95d4633SHajimu UMEMOTO /* 526f95d4633SHajimu UMEMOTO * If this is the first time this function called for this options 527f95d4633SHajimu UMEMOTO * header, simply return the 1st option. 528f95d4633SHajimu UMEMOTO * Otherwise, search the option list for the next option. 529f95d4633SHajimu UMEMOTO */ 530f95d4633SHajimu UMEMOTO if (offset == 0) { 531f95d4633SHajimu UMEMOTO optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 532f95d4633SHajimu UMEMOTO } 533f95d4633SHajimu UMEMOTO else 534f95d4633SHajimu UMEMOTO optp = (u_int8_t *)extbuf + offset; 535f95d4633SHajimu UMEMOTO 536f95d4633SHajimu UMEMOTO /* Find the next option skipping any padding options. */ 537f95d4633SHajimu UMEMOTO while(optp < lim) { 538f95d4633SHajimu UMEMOTO switch(*optp) { 539f95d4633SHajimu UMEMOTO case IP6OPT_PAD1: 540f95d4633SHajimu UMEMOTO optp++; 541f95d4633SHajimu UMEMOTO break; 542f95d4633SHajimu UMEMOTO case IP6OPT_PADN: 543f95d4633SHajimu UMEMOTO if ((optlen = ip6optlen(optp, lim)) == 0) 544f95d4633SHajimu UMEMOTO goto optend; 545f95d4633SHajimu UMEMOTO optp += optlen; 546f95d4633SHajimu UMEMOTO break; 547f95d4633SHajimu UMEMOTO default: /* found */ 548f95d4633SHajimu UMEMOTO if ((optlen = ip6optlen(optp, lim)) == 0) 549f95d4633SHajimu UMEMOTO goto optend; 550f95d4633SHajimu UMEMOTO *typep = *optp; 551f95d4633SHajimu UMEMOTO *lenp = optlen - 2; 552f95d4633SHajimu UMEMOTO *databufp = optp + 2; 553f95d4633SHajimu UMEMOTO return(optp + optlen - (u_int8_t *)extbuf); 554f95d4633SHajimu UMEMOTO } 555f95d4633SHajimu UMEMOTO } 556f95d4633SHajimu UMEMOTO 557f95d4633SHajimu UMEMOTO optend: 558f95d4633SHajimu UMEMOTO *databufp = NULL; /* for safety */ 559f95d4633SHajimu UMEMOTO return(-1); 560f95d4633SHajimu UMEMOTO } 561f95d4633SHajimu UMEMOTO 562f95d4633SHajimu UMEMOTO int 563f95d4633SHajimu UMEMOTO inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 564f95d4633SHajimu UMEMOTO socklen_t *lenp, void **databufp) 565f95d4633SHajimu UMEMOTO { 566f95d4633SHajimu UMEMOTO u_int8_t *optp, *lim; 567f95d4633SHajimu UMEMOTO int optlen; 568f95d4633SHajimu UMEMOTO 569f95d4633SHajimu UMEMOTO /* Validate extlen. XXX: is the variable really necessary?? */ 570f95d4633SHajimu UMEMOTO if (extlen == 0 || (extlen % 8)) 571f95d4633SHajimu UMEMOTO return(-1); 572f95d4633SHajimu UMEMOTO lim = (u_int8_t *)extbuf + extlen; 573f95d4633SHajimu UMEMOTO 574f95d4633SHajimu UMEMOTO /* 575f95d4633SHajimu UMEMOTO * If this is the first time this function called for this options 576f95d4633SHajimu UMEMOTO * header, simply return the 1st option. 577f95d4633SHajimu UMEMOTO * Otherwise, search the option list for the next option. 578f95d4633SHajimu UMEMOTO */ 579f95d4633SHajimu UMEMOTO if (offset == 0) { 580f95d4633SHajimu UMEMOTO optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 581f95d4633SHajimu UMEMOTO } 582f95d4633SHajimu UMEMOTO else 583f95d4633SHajimu UMEMOTO optp = (u_int8_t *)extbuf + offset; 584f95d4633SHajimu UMEMOTO 585f95d4633SHajimu UMEMOTO /* Find the specified option */ 586f95d4633SHajimu UMEMOTO while(optp < lim) { 587f95d4633SHajimu UMEMOTO if ((optlen = ip6optlen(optp, lim)) == 0) 588f95d4633SHajimu UMEMOTO goto optend; 589f95d4633SHajimu UMEMOTO 590f95d4633SHajimu UMEMOTO if (*optp == type) { /* found */ 591f95d4633SHajimu UMEMOTO *lenp = optlen - 2; 592f95d4633SHajimu UMEMOTO *databufp = optp + 2; 593f95d4633SHajimu UMEMOTO return(optp + optlen - (u_int8_t *)extbuf); 594f95d4633SHajimu UMEMOTO } 595f95d4633SHajimu UMEMOTO 596f95d4633SHajimu UMEMOTO optp += optlen; 597f95d4633SHajimu UMEMOTO } 598f95d4633SHajimu UMEMOTO 599f95d4633SHajimu UMEMOTO optend: 600f95d4633SHajimu UMEMOTO *databufp = NULL; /* for safety */ 601f95d4633SHajimu UMEMOTO return(-1); 602f95d4633SHajimu UMEMOTO } 603f95d4633SHajimu UMEMOTO 604f95d4633SHajimu UMEMOTO int 605f95d4633SHajimu UMEMOTO inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) 606f95d4633SHajimu UMEMOTO { 607f95d4633SHajimu UMEMOTO 608f95d4633SHajimu UMEMOTO /* we can't assume alignment here */ 609f95d4633SHajimu UMEMOTO memcpy(val, (u_int8_t *)databuf + offset, vallen); 610f95d4633SHajimu UMEMOTO 611f95d4633SHajimu UMEMOTO return(offset + vallen); 612f95d4633SHajimu UMEMOTO } 613