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