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