1 1.15 christos /* $NetBSD: ip6opt.c,v 1.15 2014/02/07 02:36:06 christos Exp $ */ 2 1.2 itojun 3 1.1 itojun /* 4 1.1 itojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 1.1 itojun * All rights reserved. 6 1.1 itojun * 7 1.1 itojun * Redistribution and use in source and binary forms, with or without 8 1.1 itojun * modification, are permitted provided that the following conditions 9 1.1 itojun * are met: 10 1.1 itojun * 1. Redistributions of source code must retain the above copyright 11 1.1 itojun * notice, this list of conditions and the following disclaimer. 12 1.1 itojun * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 itojun * notice, this list of conditions and the following disclaimer in the 14 1.1 itojun * documentation and/or other materials provided with the distribution. 15 1.1 itojun * 3. Neither the name of the project nor the names of its contributors 16 1.1 itojun * may be used to endorse or promote products derived from this software 17 1.1 itojun * without specific prior written permission. 18 1.1 itojun * 19 1.1 itojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 1.1 itojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 itojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 itojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 1.1 itojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 itojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 itojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 itojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 itojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 itojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 itojun * SUCH DAMAGE. 30 1.1 itojun */ 31 1.7 itojun 32 1.7 itojun #include <sys/cdefs.h> 33 1.7 itojun #if defined(LIBC_SCCS) && !defined(lint) 34 1.15 christos __RCSID("$NetBSD: ip6opt.c,v 1.15 2014/02/07 02:36:06 christos Exp $"); 35 1.7 itojun #endif /* LIBC_SCCS and not lint */ 36 1.1 itojun 37 1.6 itojun #include "namespace.h" 38 1.1 itojun #include <sys/param.h> 39 1.1 itojun #include <sys/types.h> 40 1.1 itojun #include <sys/socket.h> 41 1.1 itojun 42 1.1 itojun #include <netinet/in.h> 43 1.1 itojun #include <netinet/ip6.h> 44 1.1 itojun 45 1.3 lukem #include <assert.h> 46 1.13 christos #include <stddef.h> 47 1.1 itojun #include <string.h> 48 1.1 itojun #include <stdio.h> 49 1.6 itojun 50 1.6 itojun #ifdef __weak_alias 51 1.6 itojun __weak_alias(inet6_option_alloc,_inet6_option_alloc) 52 1.6 itojun __weak_alias(inet6_option_append,_inet6_option_append) 53 1.6 itojun __weak_alias(inet6_option_find,_inet6_option_find) 54 1.6 itojun __weak_alias(inet6_option_init,_inet6_option_init) 55 1.6 itojun __weak_alias(inet6_option_next,_inet6_option_next) 56 1.6 itojun __weak_alias(inet6_option_space,_inet6_option_space) 57 1.11 rpaulo __weak_alias(inet6_opt_init, _inet6_opt_init) 58 1.11 rpaulo __weak_alias(inet6_opt_append, _inet6_opt_append) 59 1.11 rpaulo __weak_alias(inet6_opt_finish, _inet6_opt_finish) 60 1.11 rpaulo __weak_alias(inet6_opt_set_val, _inet6_opt_set_val) 61 1.11 rpaulo __weak_alias(inet6_opt_next, _inet6_opt_next) 62 1.11 rpaulo __weak_alias(inet6_opt_find, _inet6_opt_find) 63 1.11 rpaulo __weak_alias(inet6_opt_get_val, _inet6_opt_get_val) 64 1.6 itojun #endif 65 1.1 itojun 66 1.14 matt static int ip6optlen(uint8_t *opt, uint8_t *lim); 67 1.14 matt static void inet6_insert_padopt(uint8_t *p, size_t len); 68 1.1 itojun 69 1.1 itojun /* 70 1.1 itojun * This function returns the number of bytes required to hold an option 71 1.1 itojun * when it is stored as ancillary data, including the cmsghdr structure 72 1.1 itojun * at the beginning, and any padding at the end (to make its size a 73 1.1 itojun * multiple of 8 bytes). The argument is the size of the structure 74 1.1 itojun * defining the option, which must include any pad bytes at the 75 1.1 itojun * beginning (the value y in the alignment term "xn + y"), the type 76 1.1 itojun * byte, the length byte, and the option data. 77 1.1 itojun */ 78 1.1 itojun int 79 1.14 matt inet6_option_space(int nbytes) 80 1.1 itojun { 81 1.13 christos size_t sp; 82 1.1 itojun nbytes += 2; /* we need space for nxt-hdr and length fields */ 83 1.13 christos sp = CMSG_SPACE((nbytes + 7) & ~7); 84 1.13 christos _DIAGASSERT(__type_fit(int, sp)); 85 1.13 christos return (int)sp; 86 1.1 itojun } 87 1.1 itojun 88 1.1 itojun /* 89 1.1 itojun * This function is called once per ancillary data object that will 90 1.1 itojun * contain either Hop-by-Hop or Destination options. It returns 0 on 91 1.1 itojun * success or -1 on an error. 92 1.1 itojun */ 93 1.1 itojun int 94 1.14 matt inet6_option_init(void *bp, struct cmsghdr **cmsgp, int type) 95 1.1 itojun { 96 1.3 lukem register struct cmsghdr *ch; 97 1.3 lukem 98 1.3 lukem _DIAGASSERT(bp != NULL); 99 1.3 lukem _DIAGASSERT(cmsgp != NULL); 100 1.3 lukem 101 1.3 lukem ch = (struct cmsghdr *)bp; 102 1.1 itojun 103 1.1 itojun /* argument validation */ 104 1.1 itojun if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS) 105 1.1 itojun return(-1); 106 1.1 itojun 107 1.1 itojun ch->cmsg_level = IPPROTO_IPV6; 108 1.1 itojun ch->cmsg_type = type; 109 1.1 itojun ch->cmsg_len = CMSG_LEN(0); 110 1.1 itojun 111 1.1 itojun *cmsgp = ch; 112 1.1 itojun return(0); 113 1.1 itojun } 114 1.1 itojun 115 1.1 itojun /* 116 1.1 itojun * This function appends a Hop-by-Hop option or a Destination option 117 1.1 itojun * into an ancillary data object that has been initialized by 118 1.1 itojun * inet6_option_init(). This function returns 0 if it succeeds or -1 on 119 1.1 itojun * an error. 120 1.1 itojun * multx is the value x in the alignment term "xn + y" described 121 1.1 itojun * earlier. It must have a value of 1, 2, 4, or 8. 122 1.1 itojun * plusy is the value y in the alignment term "xn + y" described 123 1.1 itojun * earlier. It must have a value between 0 and 7, inclusive. 124 1.1 itojun */ 125 1.1 itojun int 126 1.14 matt inet6_option_append(struct cmsghdr *cmsg, const uint8_t *typep, int multx, 127 1.14 matt int plusy) 128 1.1 itojun { 129 1.5 mycroft size_t padlen, optlen, off; 130 1.14 matt register uint8_t *bp; 131 1.3 lukem struct ip6_ext *eh; 132 1.3 lukem 133 1.3 lukem _DIAGASSERT(cmsg != NULL); 134 1.3 lukem _DIAGASSERT(typep != NULL); 135 1.3 lukem 136 1.14 matt bp = (uint8_t *)(void *)cmsg + cmsg->cmsg_len; 137 1.8 christos eh = (struct ip6_ext *)(void *)CMSG_DATA(cmsg); 138 1.1 itojun 139 1.1 itojun /* argument validation */ 140 1.1 itojun if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 141 1.1 itojun return(-1); 142 1.1 itojun if (plusy < 0 || plusy > 7) 143 1.1 itojun return(-1); 144 1.1 itojun 145 1.1 itojun /* 146 1.1 itojun * If this is the first option, allocate space for the 147 1.1 itojun * first 2 bytes(for next header and length fields) of 148 1.1 itojun * the option header. 149 1.1 itojun */ 150 1.14 matt if (bp == (uint8_t *)(void *)eh) { 151 1.1 itojun bp += 2; 152 1.1 itojun cmsg->cmsg_len += 2; 153 1.1 itojun } 154 1.1 itojun 155 1.1 itojun /* calculate pad length before the option. */ 156 1.14 matt off = bp - (uint8_t *)(void *)eh; 157 1.1 itojun padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 158 1.1 itojun (off % multx); 159 1.1 itojun padlen += plusy; 160 1.11 rpaulo padlen %= multx; /* keep the pad as short as possible */ 161 1.1 itojun /* insert padding */ 162 1.1 itojun inet6_insert_padopt(bp, padlen); 163 1.13 christos _DIAGASSERT(__type_fit(socklen_t, padlen + cmsg->cmsg_len)); 164 1.13 christos cmsg->cmsg_len += (socklen_t)padlen; 165 1.1 itojun bp += padlen; 166 1.1 itojun 167 1.1 itojun /* copy the option */ 168 1.1 itojun if (typep[0] == IP6OPT_PAD1) 169 1.1 itojun optlen = 1; 170 1.1 itojun else 171 1.1 itojun optlen = typep[1] + 2; 172 1.5 mycroft memcpy(bp, typep, (size_t)optlen); 173 1.1 itojun bp += optlen; 174 1.13 christos _DIAGASSERT(__type_fit(socklen_t, optlen + cmsg->cmsg_len)); 175 1.13 christos cmsg->cmsg_len += (socklen_t)optlen; 176 1.1 itojun 177 1.1 itojun /* calculate pad length after the option and insert the padding */ 178 1.14 matt off = bp - (uint8_t *)(void *)eh; 179 1.1 itojun padlen = ((off + 7) & ~7) - off; 180 1.1 itojun inet6_insert_padopt(bp, padlen); 181 1.1 itojun bp += padlen; 182 1.13 christos _DIAGASSERT(__type_fit(socklen_t, padlen + cmsg->cmsg_len)); 183 1.13 christos cmsg->cmsg_len += (socklen_t)padlen; 184 1.1 itojun 185 1.1 itojun /* update the length field of the ip6 option header */ 186 1.14 matt off = bp - (uint8_t *)(void *)eh; 187 1.13 christos _DIAGASSERT(__type_fit(uint8_t, (off >> 3) - 1)); 188 1.13 christos eh->ip6e_len = (uint8_t)((off >> 3) - 1); 189 1.1 itojun 190 1.1 itojun return(0); 191 1.1 itojun } 192 1.1 itojun 193 1.1 itojun /* 194 1.1 itojun * This function appends a Hop-by-Hop option or a Destination option 195 1.1 itojun * into an ancillary data object that has been initialized by 196 1.1 itojun * inet6_option_init(). This function returns a pointer to the 8-bit 197 1.1 itojun * option type field that starts the option on success, or NULL on an 198 1.1 itojun * error. 199 1.1 itojun * The difference between this function and inet6_option_append() is 200 1.1 itojun * that the latter copies the contents of a previously built option into 201 1.1 itojun * the ancillary data object while the current function returns a 202 1.1 itojun * pointer to the space in the data object where the option's TLV must 203 1.1 itojun * then be built by the caller. 204 1.1 itojun * 205 1.1 itojun */ 206 1.14 matt uint8_t * 207 1.14 matt inet6_option_alloc(struct cmsghdr *cmsg, int datalen, int multx, int plusy) 208 1.1 itojun { 209 1.5 mycroft size_t padlen, off; 210 1.14 matt register uint8_t *bp; 211 1.14 matt uint8_t *retval; 212 1.3 lukem struct ip6_ext *eh; 213 1.3 lukem 214 1.3 lukem _DIAGASSERT(cmsg != NULL); 215 1.3 lukem 216 1.14 matt bp = (uint8_t *)(void *)cmsg + cmsg->cmsg_len; 217 1.8 christos eh = (struct ip6_ext *)(void *)CMSG_DATA(cmsg); 218 1.1 itojun 219 1.1 itojun /* argument validation */ 220 1.1 itojun if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 221 1.1 itojun return(NULL); 222 1.1 itojun if (plusy < 0 || plusy > 7) 223 1.1 itojun return(NULL); 224 1.1 itojun 225 1.1 itojun /* 226 1.1 itojun * If this is the first option, allocate space for the 227 1.1 itojun * first 2 bytes(for next header and length fields) of 228 1.1 itojun * the option header. 229 1.1 itojun */ 230 1.14 matt if (bp == (uint8_t *)(void *)eh) { 231 1.1 itojun bp += 2; 232 1.1 itojun cmsg->cmsg_len += 2; 233 1.1 itojun } 234 1.1 itojun 235 1.1 itojun /* calculate pad length before the option. */ 236 1.14 matt off = bp - (uint8_t *)(void *)eh; 237 1.1 itojun padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 238 1.1 itojun (off % multx); 239 1.1 itojun padlen += plusy; 240 1.11 rpaulo padlen %= multx; /* keep the pad as short as possible */ 241 1.1 itojun /* insert padding */ 242 1.1 itojun inet6_insert_padopt(bp, padlen); 243 1.13 christos cmsg->cmsg_len += (socklen_t)padlen; 244 1.1 itojun bp += padlen; 245 1.1 itojun 246 1.1 itojun /* keep space to store specified length of data */ 247 1.1 itojun retval = bp; 248 1.1 itojun bp += datalen; 249 1.1 itojun cmsg->cmsg_len += datalen; 250 1.1 itojun 251 1.1 itojun /* calculate pad length after the option and insert the padding */ 252 1.14 matt off = bp - (uint8_t *)(void *)eh; 253 1.1 itojun padlen = ((off + 7) & ~7) - off; 254 1.1 itojun inet6_insert_padopt(bp, padlen); 255 1.1 itojun bp += padlen; 256 1.13 christos _DIAGASSERT(__type_fit(socklen_t, padlen + cmsg->cmsg_len)); 257 1.13 christos cmsg->cmsg_len += (socklen_t)padlen; 258 1.1 itojun 259 1.1 itojun /* update the length field of the ip6 option header */ 260 1.14 matt off = bp - (uint8_t *)(void *)eh; 261 1.13 christos _DIAGASSERT(__type_fit(uint8_t, (off >> 3) - 1)); 262 1.13 christos eh->ip6e_len = (uint8_t)((off >> 3) - 1); 263 1.1 itojun 264 1.1 itojun return(retval); 265 1.1 itojun } 266 1.1 itojun 267 1.1 itojun /* 268 1.1 itojun * This function processes the next Hop-by-Hop option or Destination 269 1.1 itojun * option in an ancillary data object. If another option remains to be 270 1.1 itojun * processed, the return value of the function is 0 and *tptrp points to 271 1.1 itojun * the 8-bit option type field (which is followed by the 8-bit option 272 1.1 itojun * data length, followed by the option data). If no more options remain 273 1.1 itojun * to be processed, the return value is -1 and *tptrp is NULL. If an 274 1.1 itojun * error occurs, the return value is -1 and *tptrp is not NULL. 275 1.1 itojun * (RFC 2292, 6.3.5) 276 1.1 itojun */ 277 1.1 itojun int 278 1.14 matt inet6_option_next(const struct cmsghdr *cmsg, uint8_t **tptrp) 279 1.1 itojun { 280 1.1 itojun struct ip6_ext *ip6e; 281 1.1 itojun int hdrlen, optlen; 282 1.14 matt uint8_t *lim; 283 1.1 itojun 284 1.3 lukem _DIAGASSERT(cmsg != NULL); 285 1.3 lukem _DIAGASSERT(tptrp != NULL); 286 1.3 lukem 287 1.1 itojun if (cmsg->cmsg_level != IPPROTO_IPV6 || 288 1.1 itojun (cmsg->cmsg_type != IPV6_HOPOPTS && 289 1.1 itojun cmsg->cmsg_type != IPV6_DSTOPTS)) 290 1.1 itojun return(-1); 291 1.1 itojun 292 1.1 itojun /* message length validation */ 293 1.1 itojun if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 294 1.1 itojun return(-1); 295 1.10 christos ip6e = __UNCONST(CCMSG_DATA(cmsg)); 296 1.1 itojun hdrlen = (ip6e->ip6e_len + 1) << 3; 297 1.1 itojun if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 298 1.1 itojun return(-1); 299 1.1 itojun 300 1.1 itojun /* 301 1.1 itojun * If the caller does not specify the starting point, 302 1.1 itojun * simply return the 1st option. 303 1.1 itojun * Otherwise, search the option list for the next option. 304 1.1 itojun */ 305 1.14 matt lim = (uint8_t *)(void *)ip6e + hdrlen; 306 1.1 itojun if (*tptrp == NULL) 307 1.14 matt *tptrp = (uint8_t *)(void *)(ip6e + 1); 308 1.1 itojun else { 309 1.1 itojun if ((optlen = ip6optlen(*tptrp, lim)) == 0) 310 1.1 itojun return(-1); 311 1.1 itojun 312 1.1 itojun *tptrp = *tptrp + optlen; 313 1.1 itojun } 314 1.1 itojun if (*tptrp >= lim) { /* there is no option */ 315 1.1 itojun *tptrp = NULL; 316 1.1 itojun return(-1); 317 1.1 itojun } 318 1.1 itojun /* 319 1.1 itojun * Finally, checks if the next option is safely stored in the 320 1.1 itojun * cmsg data. 321 1.1 itojun */ 322 1.1 itojun if (ip6optlen(*tptrp, lim) == 0) 323 1.1 itojun return(-1); 324 1.1 itojun else 325 1.1 itojun return(0); 326 1.1 itojun } 327 1.1 itojun 328 1.1 itojun /* 329 1.1 itojun * This function is similar to the inet6_option_next() function, 330 1.1 itojun * except this function lets the caller specify the option type to be 331 1.1 itojun * searched for, instead of always returning the next option in the 332 1.1 itojun * ancillary data object. 333 1.14 matt * Note: RFC 2292 says the type of tptrp is uint8_t *, but we think 334 1.14 matt * it's a typo. The variable should be type of uint8_t **. 335 1.1 itojun */ 336 1.1 itojun int 337 1.14 matt inet6_option_find(const struct cmsghdr *cmsg, uint8_t **tptrp, int type) 338 1.1 itojun { 339 1.1 itojun struct ip6_ext *ip6e; 340 1.1 itojun int hdrlen, optlen; 341 1.14 matt uint8_t *optp, *lim; 342 1.1 itojun 343 1.3 lukem _DIAGASSERT(cmsg != NULL); 344 1.3 lukem _DIAGASSERT(tptrp != NULL); 345 1.3 lukem 346 1.1 itojun if (cmsg->cmsg_level != IPPROTO_IPV6 || 347 1.1 itojun (cmsg->cmsg_type != IPV6_HOPOPTS && 348 1.1 itojun cmsg->cmsg_type != IPV6_DSTOPTS)) 349 1.1 itojun return(-1); 350 1.1 itojun 351 1.1 itojun /* message length validation */ 352 1.1 itojun if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 353 1.1 itojun return(-1); 354 1.10 christos ip6e = __UNCONST(CCMSG_DATA(cmsg)); 355 1.1 itojun hdrlen = (ip6e->ip6e_len + 1) << 3; 356 1.1 itojun if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 357 1.1 itojun return(-1); 358 1.1 itojun 359 1.1 itojun /* 360 1.1 itojun * If the caller does not specify the starting point, 361 1.1 itojun * search from the beginning of the option list. 362 1.1 itojun * Otherwise, search from *the next option* of the specified point. 363 1.1 itojun */ 364 1.14 matt lim = (uint8_t *)(void *)ip6e + hdrlen; 365 1.1 itojun if (*tptrp == NULL) 366 1.14 matt *tptrp = (uint8_t *)(void *)(ip6e + 1); 367 1.1 itojun else { 368 1.1 itojun if ((optlen = ip6optlen(*tptrp, lim)) == 0) 369 1.1 itojun return(-1); 370 1.1 itojun 371 1.1 itojun *tptrp = *tptrp + optlen; 372 1.1 itojun } 373 1.1 itojun for (optp = *tptrp; optp < lim; optp += optlen) { 374 1.1 itojun if (*optp == type) { 375 1.1 itojun *tptrp = optp; 376 1.1 itojun return(0); 377 1.1 itojun } 378 1.1 itojun if ((optlen = ip6optlen(optp, lim)) == 0) 379 1.1 itojun return(-1); 380 1.1 itojun } 381 1.1 itojun 382 1.1 itojun /* search failed */ 383 1.1 itojun *tptrp = NULL; 384 1.1 itojun return(-1); 385 1.1 itojun } 386 1.1 itojun 387 1.1 itojun /* 388 1.1 itojun * Calculate the length of a given IPv6 option. Also checks 389 1.1 itojun * if the option is safely stored in user's buffer according to the 390 1.1 itojun * calculated length and the limitation of the buffer. 391 1.1 itojun */ 392 1.1 itojun static int 393 1.14 matt ip6optlen(uint8_t *opt, uint8_t *lim) 394 1.1 itojun { 395 1.1 itojun int optlen; 396 1.1 itojun 397 1.3 lukem _DIAGASSERT(opt != NULL); 398 1.3 lukem _DIAGASSERT(lim != NULL); 399 1.3 lukem 400 1.1 itojun if (*opt == IP6OPT_PAD1) 401 1.1 itojun optlen = 1; 402 1.1 itojun else { 403 1.1 itojun /* is there enough space to store type and len? */ 404 1.1 itojun if (opt + 2 > lim) 405 1.1 itojun return(0); 406 1.1 itojun optlen = *(opt + 1) + 2; 407 1.1 itojun } 408 1.1 itojun if (opt + optlen <= lim) 409 1.1 itojun return(optlen); 410 1.1 itojun 411 1.1 itojun return(0); 412 1.1 itojun } 413 1.1 itojun 414 1.1 itojun static void 415 1.14 matt inet6_insert_padopt(uint8_t *p, size_t len) 416 1.1 itojun { 417 1.3 lukem 418 1.3 lukem _DIAGASSERT(p != NULL); 419 1.3 lukem 420 1.1 itojun switch(len) { 421 1.1 itojun case 0: 422 1.1 itojun return; 423 1.1 itojun case 1: 424 1.1 itojun p[0] = IP6OPT_PAD1; 425 1.1 itojun return; 426 1.1 itojun default: 427 1.1 itojun p[0] = IP6OPT_PADN; 428 1.14 matt _DIAGASSERT(__type_fit(uint8_t, len - 2)); 429 1.14 matt p[1] = (uint8_t)(len - 2); 430 1.1 itojun memset(&p[2], 0, len - 2); 431 1.1 itojun return; 432 1.1 itojun } 433 1.1 itojun } 434 1.11 rpaulo 435 1.11 rpaulo /* 436 1.11 rpaulo * The following functions are defined in RFC3542, which is a successor 437 1.11 rpaulo * of RFC2292. 438 1.11 rpaulo */ 439 1.11 rpaulo 440 1.11 rpaulo int 441 1.11 rpaulo inet6_opt_init(void *extbuf, socklen_t extlen) 442 1.11 rpaulo { 443 1.11 rpaulo struct ip6_ext *ext = (struct ip6_ext *)extbuf; 444 1.11 rpaulo 445 1.11 rpaulo if (ext) { 446 1.15 christos if (extlen == 0 || (extlen % 8)) 447 1.11 rpaulo return (-1); 448 1.11 rpaulo ext->ip6e_len = (extlen >> 3) - 1; 449 1.11 rpaulo } 450 1.11 rpaulo 451 1.11 rpaulo return (2); /* sizeof the next and the length fields */ 452 1.11 rpaulo } 453 1.11 rpaulo 454 1.11 rpaulo int 455 1.14 matt inet6_opt_append(void *extbuf, socklen_t extlen, int offset, uint8_t type, 456 1.14 matt socklen_t len, uint8_t align, void **databufp) 457 1.11 rpaulo { 458 1.11 rpaulo int currentlen = offset; 459 1.11 rpaulo size_t padlen = 0; 460 1.11 rpaulo 461 1.11 rpaulo /* 462 1.11 rpaulo * The option type must have a value from 2 to 255, inclusive. 463 1.11 rpaulo * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) 464 1.11 rpaulo */ 465 1.11 rpaulo if (type < 2) 466 1.11 rpaulo return (-1); 467 1.11 rpaulo 468 1.11 rpaulo /* 469 1.11 rpaulo * The option data length must have a value between 0 and 255, 470 1.11 rpaulo * inclusive, and is the length of the option data that follows. 471 1.11 rpaulo */ 472 1.11 rpaulo if (len > 255) 473 1.11 rpaulo return (-1); 474 1.11 rpaulo 475 1.11 rpaulo /* 476 1.11 rpaulo * The align parameter must have a value of 1, 2, 4, or 8. 477 1.11 rpaulo * The align value can not exceed the value of len. 478 1.11 rpaulo */ 479 1.11 rpaulo if (align != 1 && align != 2 && align != 4 && align != 8) 480 1.11 rpaulo return (-1); 481 1.11 rpaulo if (align > len) 482 1.11 rpaulo return (-1); 483 1.11 rpaulo 484 1.11 rpaulo /* Calculate the padding length. */ 485 1.11 rpaulo currentlen += 2 + len; /* 2 means "type + len" */ 486 1.11 rpaulo if (currentlen % align) 487 1.11 rpaulo padlen = align - (currentlen % align); 488 1.11 rpaulo 489 1.11 rpaulo /* The option must fit in the extension header buffer. */ 490 1.13 christos _DIAGASSERT(__type_fit(int, currentlen + padlen)); 491 1.13 christos currentlen += (int)padlen; 492 1.11 rpaulo if (extlen && /* XXX: right? */ 493 1.12 lukem (socklen_t)currentlen > extlen) 494 1.11 rpaulo return (-1); 495 1.11 rpaulo 496 1.11 rpaulo if (extbuf) { 497 1.14 matt uint8_t *optp = (uint8_t *)extbuf + offset; 498 1.11 rpaulo 499 1.11 rpaulo if (padlen == 1) { 500 1.11 rpaulo /* insert a Pad1 option */ 501 1.11 rpaulo *optp = IP6OPT_PAD1; 502 1.11 rpaulo optp++; 503 1.11 rpaulo } else if (padlen > 0) { 504 1.11 rpaulo /* insert a PadN option for alignment */ 505 1.11 rpaulo *optp++ = IP6OPT_PADN; 506 1.13 christos _DIAGASSERT(__type_fit(uint8_t, padlen - 2)); 507 1.13 christos *optp++ = (uint8_t)(padlen - 2); 508 1.11 rpaulo memset(optp, 0, padlen - 2); 509 1.11 rpaulo optp += (padlen - 2); 510 1.11 rpaulo } 511 1.11 rpaulo 512 1.11 rpaulo *optp++ = type; 513 1.11 rpaulo *optp++ = len; 514 1.11 rpaulo 515 1.11 rpaulo *databufp = optp; 516 1.11 rpaulo } 517 1.11 rpaulo 518 1.11 rpaulo return (currentlen); 519 1.11 rpaulo } 520 1.11 rpaulo 521 1.11 rpaulo int 522 1.11 rpaulo inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) 523 1.11 rpaulo { 524 1.12 lukem int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0; 525 1.11 rpaulo 526 1.11 rpaulo if (extbuf) { 527 1.14 matt uint8_t *padp; 528 1.11 rpaulo size_t padlen = updatelen - offset; 529 1.11 rpaulo 530 1.13 christos if ((socklen_t)updatelen > extlen || padlen >= 256 + 2) 531 1.11 rpaulo return (-1); 532 1.11 rpaulo 533 1.14 matt padp = (uint8_t *)extbuf + offset; 534 1.11 rpaulo if (padlen == 1) 535 1.11 rpaulo *padp = IP6OPT_PAD1; 536 1.11 rpaulo else if (padlen > 0) { 537 1.11 rpaulo *padp++ = IP6OPT_PADN; 538 1.13 christos *padp++ = (uint8_t)(padlen - 2); 539 1.11 rpaulo memset(padp, 0, padlen - 2); 540 1.11 rpaulo } 541 1.11 rpaulo } 542 1.11 rpaulo 543 1.11 rpaulo return (updatelen); 544 1.11 rpaulo } 545 1.11 rpaulo 546 1.11 rpaulo int 547 1.11 rpaulo inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) 548 1.11 rpaulo { 549 1.11 rpaulo 550 1.14 matt memcpy((uint8_t *)databuf + offset, val, vallen); 551 1.11 rpaulo return (offset + vallen); 552 1.11 rpaulo } 553 1.11 rpaulo 554 1.11 rpaulo int 555 1.14 matt inet6_opt_next(void *extbuf, socklen_t extlen, int offset, uint8_t *typep, 556 1.11 rpaulo socklen_t *lenp, void **databufp) 557 1.11 rpaulo { 558 1.14 matt uint8_t *optp, *lim; 559 1.11 rpaulo int optlen; 560 1.11 rpaulo 561 1.11 rpaulo /* Validate extlen. XXX: is the variable really necessary?? */ 562 1.11 rpaulo if (extlen == 0 || (extlen % 8)) 563 1.11 rpaulo return (-1); 564 1.14 matt lim = (uint8_t *)extbuf + extlen; 565 1.11 rpaulo 566 1.11 rpaulo /* 567 1.11 rpaulo * If this is the first time this function called for this options 568 1.11 rpaulo * header, simply return the 1st option. 569 1.11 rpaulo * Otherwise, search the option list for the next option. 570 1.11 rpaulo */ 571 1.11 rpaulo if (offset == 0) 572 1.14 matt optp = (uint8_t *)(void *)((struct ip6_hbh *)extbuf + 1); 573 1.11 rpaulo else 574 1.14 matt optp = (uint8_t *)extbuf + offset; 575 1.11 rpaulo 576 1.11 rpaulo /* Find the next option skipping any padding options. */ 577 1.11 rpaulo while (optp < lim) { 578 1.13 christos ptrdiff_t rv; 579 1.11 rpaulo switch(*optp) { 580 1.11 rpaulo case IP6OPT_PAD1: 581 1.11 rpaulo optp++; 582 1.11 rpaulo break; 583 1.11 rpaulo case IP6OPT_PADN: 584 1.11 rpaulo if ((optlen = ip6optlen(optp, lim)) == 0) 585 1.11 rpaulo goto optend; 586 1.11 rpaulo optp += optlen; 587 1.11 rpaulo break; 588 1.11 rpaulo default: /* found */ 589 1.11 rpaulo if ((optlen = ip6optlen(optp, lim)) == 0) 590 1.11 rpaulo goto optend; 591 1.11 rpaulo *typep = *optp; 592 1.11 rpaulo *lenp = optlen - 2; 593 1.11 rpaulo *databufp = optp + 2; 594 1.14 matt rv = optp + optlen - (uint8_t *)extbuf; 595 1.13 christos _DIAGASSERT(__type_fit(int, rv)); 596 1.13 christos return (int)rv; 597 1.11 rpaulo } 598 1.11 rpaulo } 599 1.11 rpaulo 600 1.11 rpaulo optend: 601 1.11 rpaulo *databufp = NULL; /* for safety */ 602 1.11 rpaulo return (-1); 603 1.11 rpaulo } 604 1.11 rpaulo 605 1.11 rpaulo int 606 1.14 matt inet6_opt_find(void *extbuf, socklen_t extlen, int offset, uint8_t type, 607 1.11 rpaulo socklen_t *lenp, void **databufp) 608 1.11 rpaulo { 609 1.14 matt uint8_t *optp, *lim; 610 1.11 rpaulo int optlen; 611 1.11 rpaulo 612 1.11 rpaulo /* Validate extlen. XXX: is the variable really necessary?? */ 613 1.11 rpaulo if (extlen == 0 || (extlen % 8)) 614 1.11 rpaulo return (-1); 615 1.14 matt lim = (uint8_t *)extbuf + extlen; 616 1.11 rpaulo 617 1.11 rpaulo /* 618 1.11 rpaulo * If this is the first time this function called for this options 619 1.11 rpaulo * header, simply return the 1st option. 620 1.11 rpaulo * Otherwise, search the option list for the next option. 621 1.11 rpaulo */ 622 1.11 rpaulo if (offset == 0) 623 1.14 matt optp = (uint8_t *)(void *)((struct ip6_hbh *)extbuf + 1); 624 1.11 rpaulo else 625 1.14 matt optp = (uint8_t *)extbuf + offset; 626 1.11 rpaulo 627 1.11 rpaulo /* Find the specified option */ 628 1.11 rpaulo while (optp < lim) { 629 1.11 rpaulo if ((optlen = ip6optlen(optp, lim)) == 0) 630 1.11 rpaulo goto optend; 631 1.11 rpaulo 632 1.11 rpaulo if (*optp == type) { /* found */ 633 1.13 christos ptrdiff_t td; 634 1.11 rpaulo *lenp = optlen - 2; 635 1.11 rpaulo *databufp = optp + 2; 636 1.14 matt td = optp + optlen - (uint8_t *)extbuf; 637 1.13 christos _DIAGASSERT(__type_fit(int, td)); 638 1.13 christos return (int)td; 639 1.11 rpaulo } 640 1.11 rpaulo 641 1.11 rpaulo optp += optlen; 642 1.11 rpaulo } 643 1.11 rpaulo 644 1.11 rpaulo optend: 645 1.11 rpaulo *databufp = NULL; /* for safety */ 646 1.11 rpaulo return (-1); 647 1.11 rpaulo } 648 1.11 rpaulo 649 1.11 rpaulo int 650 1.11 rpaulo inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) 651 1.11 rpaulo { 652 1.11 rpaulo 653 1.11 rpaulo /* we can't assume alignment here */ 654 1.14 matt memcpy(val, (uint8_t *)databuf + offset, vallen); 655 1.11 rpaulo 656 1.11 rpaulo return (offset + vallen); 657 1.11 rpaulo } 658