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