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