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