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