1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright (c) 2016, Chris Fraire <cfraire@me.com>. 25 */ 26 27 #include <string.h> 28 #include <sys/types.h> 29 #include <stdlib.h> 30 #include <dhcpmsg.h> 31 #include <stddef.h> 32 #include <assert.h> 33 #include <search.h> 34 #include <alloca.h> 35 #include <limits.h> 36 #include <stropts.h> 37 #include <netinet/dhcp6.h> 38 #include <arpa/inet.h> 39 #include <sys/sysmacros.h> 40 #include <sys/sockio.h> 41 #include <inet/ip6_asp.h> 42 43 #include "states.h" 44 #include "interface.h" 45 #include "agent.h" 46 #include "packet.h" 47 #include "util.h" 48 49 int v6_sock_fd = -1; 50 int v4_sock_fd = -1; 51 52 const in6_addr_t ipv6_all_dhcp_relay_and_servers = { 53 0xff, 0x02, 0x00, 0x00, 54 0x00, 0x00, 0x00, 0x00, 55 0x00, 0x00, 0x00, 0x00, 56 0x00, 0x01, 0x00, 0x02 57 }; 58 59 /* 60 * We have our own version of this constant because dhcpagent is compiled with 61 * -lxnet. 62 */ 63 const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT; 64 65 static void retransmit(iu_tq_t *, void *); 66 static void next_retransmission(dhcp_smach_t *, boolean_t, boolean_t); 67 static boolean_t send_pkt_internal(dhcp_smach_t *); 68 69 /* 70 * pkt_send_type(): returns an integer representing the packet's type; only 71 * for use with outbound packets. 72 * 73 * input: dhcp_pkt_t *: the packet to examine 74 * output: uchar_t: the packet type (0 if unknown) 75 */ 76 77 static uchar_t 78 pkt_send_type(const dhcp_pkt_t *dpkt) 79 { 80 const uchar_t *option; 81 82 if (dpkt->pkt_isv6) 83 return (((const dhcpv6_message_t *)dpkt->pkt)->d6m_msg_type); 84 85 /* 86 * this is a little dirty but it should get the job done. 87 * assumes that the type is in the statically allocated part 88 * of the options field. 89 */ 90 91 option = dpkt->pkt->options; 92 for (;;) { 93 if (*option == CD_PAD) { 94 option++; 95 continue; 96 } 97 if (*option == CD_END || 98 option + 2 - dpkt->pkt->options >= 99 sizeof (dpkt->pkt->options)) 100 return (0); 101 if (*option == CD_DHCP_TYPE) 102 break; 103 option++; 104 option += *option + 1; 105 } 106 107 return (option[2]); 108 } 109 110 /* 111 * pkt_recv_type(): returns an integer representing the packet's type; only 112 * for use with inbound packets. 113 * 114 * input: dhcp_pkt_t *: the packet to examine 115 * output: uchar_t: the packet type (0 if unknown) 116 */ 117 118 uchar_t 119 pkt_recv_type(const PKT_LIST *plp) 120 { 121 if (plp->isv6) 122 return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type); 123 else if (plp->opts[CD_DHCP_TYPE] != NULL) 124 return (plp->opts[CD_DHCP_TYPE]->value[0]); 125 else 126 return (0); 127 } 128 129 /* 130 * pkt_get_xid(): returns transaction ID from a DHCP packet. 131 * 132 * input: const PKT *: the packet to examine 133 * output: uint_t: the transaction ID (0 if unknown) 134 */ 135 136 uint_t 137 pkt_get_xid(const PKT *pkt, boolean_t isv6) 138 { 139 if (pkt == NULL) 140 return (0); 141 if (isv6) 142 return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt)); 143 else 144 return (pkt->xid); 145 } 146 147 /* 148 * init_pkt(): initializes and returns a packet of a given type 149 * 150 * input: dhcp_smach_t *: the state machine that will send the packet 151 * uchar_t: the packet type (DHCP message type) 152 * output: dhcp_pkt_t *: a pointer to the initialized packet; may be NULL 153 */ 154 155 dhcp_pkt_t * 156 init_pkt(dhcp_smach_t *dsmp, uchar_t type) 157 { 158 dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt; 159 dhcp_lif_t *lif = dsmp->dsm_lif; 160 dhcp_pif_t *pif = lif->lif_pif; 161 uint_t mtu = lif->lif_max; 162 uint32_t xid; 163 boolean_t isv6; 164 165 dpkt->pkt_isv6 = isv6 = pif->pif_isv6; 166 167 /* 168 * Since multiple dhcp leases may be maintained over the same pif 169 * (e.g. "hme0" and "hme0:1"), make sure the xid is unique. 170 * 171 * Note that transaction ID zero is intentionally never assigned. 172 * That's used to represent "no ID." Also note that transaction IDs 173 * are only 24 bits long in DHCPv6. 174 */ 175 176 do { 177 xid = mrand48(); 178 if (isv6) 179 xid &= 0xFFFFFF; 180 } while (xid == 0 || 181 lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL); 182 183 if (isv6) { 184 dhcpv6_message_t *v6; 185 186 if (mtu != dpkt->pkt_max_len && 187 (v6 = realloc(dpkt->pkt, mtu)) != NULL) { 188 /* LINTED: alignment known to be correct */ 189 dpkt->pkt = (PKT *)v6; 190 dpkt->pkt_max_len = mtu; 191 } 192 193 if (sizeof (*v6) > dpkt->pkt_max_len) { 194 dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u", 195 mtu); 196 return (NULL); 197 } 198 199 v6 = (dhcpv6_message_t *)dpkt->pkt; 200 dpkt->pkt_cur_len = sizeof (*v6); 201 202 (void) memset(v6, 0, dpkt->pkt_max_len); 203 204 v6->d6m_msg_type = type; 205 DHCPV6_SET_TRANSID(v6, xid); 206 207 if (dsmp->dsm_cidlen > 0 && 208 add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid, 209 dsmp->dsm_cidlen) == NULL) { 210 dhcpmsg(MSG_WARNING, 211 "init_pkt: cannot insert client ID"); 212 return (NULL); 213 } 214 215 /* For v6, time starts with the creation of a transaction */ 216 dsmp->dsm_neg_hrtime = gethrtime(); 217 dsmp->dsm_newstart_monosec = monosec(); 218 } else { 219 static uint8_t bootmagic[] = BOOTMAGIC; 220 PKT *v4; 221 222 if (mtu != dpkt->pkt_max_len && 223 (v4 = realloc(dpkt->pkt, mtu)) != NULL) { 224 dpkt->pkt = v4; 225 dpkt->pkt_max_len = mtu; 226 } 227 228 if (offsetof(PKT, options) > dpkt->pkt_max_len) { 229 dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u", 230 mtu); 231 return (NULL); 232 } 233 234 v4 = dpkt->pkt; 235 dpkt->pkt_cur_len = offsetof(PKT, options); 236 237 (void) memset(v4, 0, dpkt->pkt_max_len); 238 (void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic)); 239 if (pif->pif_hwlen <= sizeof (v4->chaddr)) { 240 v4->hlen = pif->pif_hwlen; 241 (void) memcpy(v4->chaddr, pif->pif_hwaddr, 242 pif->pif_hwlen); 243 } else { 244 /* 245 * The mac address does not fit in the chaddr 246 * field, thus it can not be sent to the server, 247 * thus server can not unicast the reply. Per 248 * RFC 2131 4.4.1, client can set this bit in 249 * DISCOVER/REQUEST. If the client is already 250 * in a bound state, do not set this bit, as it 251 * can respond to unicast responses from server 252 * using the 'ciaddr' address. 253 */ 254 if (type == DISCOVER || (type == REQUEST && 255 !is_bound_state(dsmp->dsm_state))) 256 v4->flags = htons(BCAST_MASK); 257 } 258 259 v4->xid = xid; 260 v4->op = BOOTREQUEST; 261 v4->htype = pif->pif_hwtype; 262 263 if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) { 264 dhcpmsg(MSG_WARNING, 265 "init_pkt: cannot set DHCP packet type"); 266 return (NULL); 267 } 268 269 if (dsmp->dsm_cidlen > 0 && 270 add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid, 271 dsmp->dsm_cidlen) == NULL) { 272 dhcpmsg(MSG_WARNING, 273 "init_pkt: cannot insert client ID"); 274 return (NULL); 275 } 276 } 277 278 return (dpkt); 279 } 280 281 /* 282 * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t 283 * 284 * input: dhcp_pkt_t *: the packet to remove the option from 285 * uint_t: the type of option being added 286 * output: boolean_t: B_TRUE on success, B_FALSE on failure 287 * note: currently does not work with DHCPv6 suboptions, or to remove 288 * arbitrary option instances. 289 */ 290 291 boolean_t 292 remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type) 293 { 294 uchar_t *raw_pkt, *raw_end, *next; 295 uint_t len; 296 297 raw_pkt = (uchar_t *)dpkt->pkt; 298 raw_end = raw_pkt + dpkt->pkt_cur_len; 299 if (dpkt->pkt_isv6) { 300 dhcpv6_option_t d6o; 301 302 raw_pkt += sizeof (dhcpv6_message_t); 303 304 opt_type = htons(opt_type); 305 while (raw_pkt + sizeof (d6o) <= raw_end) { 306 (void) memcpy(&d6o, raw_pkt, sizeof (d6o)); 307 len = ntohs(d6o.d6o_len) + sizeof (d6o); 308 if (len > raw_end - raw_pkt) 309 break; 310 next = raw_pkt + len; 311 if (d6o.d6o_code == opt_type) { 312 if (next < raw_end) { 313 (void) memmove(raw_pkt, next, 314 raw_end - next); 315 } 316 dpkt->pkt_cur_len -= len; 317 return (B_TRUE); 318 } 319 raw_pkt = next; 320 } 321 } else { 322 uchar_t *pstart, *padrun; 323 324 raw_pkt += offsetof(PKT, options); 325 pstart = raw_pkt; 326 327 if (opt_type == CD_END || opt_type == CD_PAD) 328 return (B_FALSE); 329 330 padrun = NULL; 331 while (raw_pkt + 1 <= raw_end) { 332 if (*raw_pkt == CD_END) 333 break; 334 if (*raw_pkt == CD_PAD) { 335 if (padrun == NULL) 336 padrun = raw_pkt; 337 raw_pkt++; 338 continue; 339 } 340 if (raw_pkt + 2 > raw_end) 341 break; 342 len = raw_pkt[1]; 343 if (len > raw_end - raw_pkt || len < 2) 344 break; 345 next = raw_pkt + len; 346 if (*raw_pkt == opt_type) { 347 if (next < raw_end) { 348 int toadd = (4 + ((next-pstart)&3) - 349 ((raw_pkt-pstart)&3)) & 3; 350 int torem = 4 - toadd; 351 352 if (torem != 4 && padrun != NULL && 353 (raw_pkt - padrun) >= torem) { 354 raw_pkt -= torem; 355 dpkt->pkt_cur_len -= torem; 356 } else if (toadd > 0) { 357 (void) memset(raw_pkt, CD_PAD, 358 toadd); 359 raw_pkt += toadd; 360 /* max is not an issue here */ 361 dpkt->pkt_cur_len += toadd; 362 } 363 if (raw_pkt != next) { 364 (void) memmove(raw_pkt, next, 365 raw_end - next); 366 } 367 } 368 dpkt->pkt_cur_len -= len; 369 return (B_TRUE); 370 } 371 padrun = NULL; 372 raw_pkt = next; 373 } 374 } 375 return (B_FALSE); 376 } 377 378 /* 379 * update_v6opt_len(): updates the length field of a DHCPv6 option. 380 * 381 * input: dhcpv6_option_t *: option to be updated 382 * int: number of octets to add or subtract 383 * output: boolean_t: B_TRUE on success, B_FALSE on failure 384 */ 385 386 boolean_t 387 update_v6opt_len(dhcpv6_option_t *opt, int adjust) 388 { 389 dhcpv6_option_t optval; 390 391 (void) memcpy(&optval, opt, sizeof (optval)); 392 adjust += ntohs(optval.d6o_len); 393 if (adjust < 0 || adjust > UINT16_MAX) { 394 return (B_FALSE); 395 } else { 396 optval.d6o_len = htons(adjust); 397 (void) memcpy(opt, &optval, sizeof (optval)); 398 return (B_TRUE); 399 } 400 } 401 402 /* 403 * add_pkt_opt(): adds an option to a dhcp_pkt_t 404 * 405 * input: dhcp_pkt_t *: the packet to add the option to 406 * uint_t: the type of option being added 407 * const void *: the value of that option 408 * uint_t: the length of the value of the option 409 * output: void *: pointer to the option that was added, or NULL on failure. 410 */ 411 412 void * 413 add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val, 414 uint_t opt_len) 415 { 416 uchar_t *raw_pkt; 417 size_t req_len; 418 void *optr; 419 420 raw_pkt = (uchar_t *)dpkt->pkt; 421 optr = raw_pkt + dpkt->pkt_cur_len; 422 if (dpkt->pkt_isv6) { 423 req_len = opt_len + sizeof (dhcpv6_option_t); 424 425 if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) { 426 dhcpmsg(MSG_WARNING, 427 "add_pkt_opt: not enough room for v6 option %u in " 428 "packet (%u + %u > %u)", opt_type, 429 dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len); 430 return (NULL); 431 } 432 } else { 433 req_len = opt_len + DHCP_OPT_META_LEN; 434 435 /* CD_END and CD_PAD options don't have a length field */ 436 if (opt_type == CD_END || opt_type == CD_PAD) { 437 req_len = 1; 438 } else if (opt_val == NULL) { 439 dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is " 440 "missing required value", opt_type); 441 return (NULL); 442 } 443 444 if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) { 445 dhcpmsg(MSG_WARNING, 446 "add_pkt_opt: not enough room for v4 option %u in " 447 "packet", opt_type); 448 return (NULL); 449 } 450 } 451 452 req_len = encode_dhcp_opt(&raw_pkt[dpkt->pkt_cur_len], dpkt->pkt_isv6, 453 opt_type, opt_val, opt_len); 454 dpkt->pkt_cur_len += req_len; 455 456 return (optr); 457 } 458 459 /* 460 * encode_dhcp_opt(): sets the fields of an allocated DHCP option buffer 461 * 462 * input: void *: the buffer allocated for enough space for 463 * (DHCPv6) dhcpv6_option_t and value, or for 464 * (DHCPv4) opt_type + length + value (length/value are 465 * skipped for CD_END or CD_PAD); 466 * boolean_t: a value indicating whether DHCPv6 or not; 467 * uint_t: the type of option being added; 468 * const void *: the value of that option; 469 * uint_t: the length of the value of the option 470 * output: size_t: the number of bytes written starting at opt. 471 */ 472 473 size_t 474 encode_dhcp_opt(void *dopt, boolean_t isv6, uint_t opt_type, 475 const void *opt_val, uint_t opt_len) 476 { 477 boolean_t do_copy_value = B_FALSE; 478 size_t res_len = 0; 479 uint8_t *pval; 480 481 if (isv6) { 482 dhcpv6_option_t d6o; 483 d6o.d6o_code = htons(opt_type); 484 d6o.d6o_len = htons(opt_len); 485 (void) memcpy(dopt, &d6o, sizeof (d6o)); 486 res_len += sizeof (d6o); 487 488 do_copy_value = B_TRUE; 489 } else { 490 pval = (uint8_t *)dopt; 491 pval[res_len++] = opt_type; 492 493 if (opt_type != CD_END && opt_type != CD_PAD) { 494 pval[res_len++] = opt_len; 495 do_copy_value = B_TRUE; 496 } 497 } 498 499 pval = (uint8_t *)dopt + res_len; 500 if (do_copy_value && opt_len > 0) { 501 (void) memcpy(pval, opt_val, opt_len); 502 res_len += opt_len; 503 } 504 505 return (res_len); 506 } 507 508 /* 509 * add_pkt_subopt(): adds an option to a dhcp_pkt_t option. DHCPv6-specific, 510 * but could be extended to IPv4 DHCP if necessary. Assumes 511 * that if the parent isn't a top-level option, the caller 512 * will adjust any upper-level options recursively using 513 * update_v6opt_len. 514 * 515 * input: dhcp_pkt_t *: the packet to add the suboption to 516 * dhcpv6_option_t *: the start of the option to that should contain 517 * it (parent) 518 * uint_t: the type of suboption being added 519 * const void *: the value of that option 520 * uint_t: the length of the value of the option 521 * output: void *: pointer to the suboption that was added, or NULL on 522 * failure. 523 */ 524 525 void * 526 add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type, 527 const void *opt_val, uint_t opt_len) 528 { 529 uchar_t *raw_pkt; 530 int req_len; 531 void *optr; 532 dhcpv6_option_t d6o; 533 uchar_t *optend; 534 int olen; 535 536 if (!dpkt->pkt_isv6) 537 return (NULL); 538 539 raw_pkt = (uchar_t *)dpkt->pkt; 540 req_len = opt_len + sizeof (d6o); 541 542 if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) { 543 dhcpmsg(MSG_WARNING, 544 "add_pkt_subopt: not enough room for v6 suboption %u in " 545 "packet (%u + %u > %u)", opt_type, 546 dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len); 547 return (NULL); 548 } 549 550 /* 551 * Update the parent option to include room for this option, 552 * and compute the insertion point. 553 */ 554 (void) memcpy(&d6o, parentopt, sizeof (d6o)); 555 olen = ntohs(d6o.d6o_len); 556 optend = (uchar_t *)(parentopt + 1) + olen; 557 olen += req_len; 558 d6o.d6o_len = htons(olen); 559 (void) memcpy(parentopt, &d6o, sizeof (d6o)); 560 561 /* 562 * If there's anything at the end to move, then move it. Also bump up 563 * the packet size. 564 */ 565 if (optend < raw_pkt + dpkt->pkt_cur_len) { 566 (void) memmove(optend + req_len, optend, 567 (raw_pkt + dpkt->pkt_cur_len) - optend); 568 } 569 dpkt->pkt_cur_len += req_len; 570 571 /* 572 * Now format the suboption and add it in. 573 */ 574 optr = optend; 575 d6o.d6o_code = htons(opt_type); 576 d6o.d6o_len = htons(opt_len); 577 (void) memcpy(optend, &d6o, sizeof (d6o)); 578 if (opt_len > 0) 579 (void) memcpy(optend + sizeof (d6o), opt_val, opt_len); 580 return (optr); 581 } 582 583 /* 584 * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t 585 * 586 * input: dhcp_pkt_t *: the packet to add the option to 587 * uint_t: the type of option being added 588 * uint16_t: the value of that option 589 * output: void *: pointer to the option that was added, or NULL on failure. 590 */ 591 592 void * 593 add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value) 594 { 595 return (add_pkt_opt(dpkt, opt_type, &opt_value, 2)); 596 } 597 598 /* 599 * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t 600 * 601 * input: dhcp_pkt_t *: the packet to add the option to 602 * uint_t: the type of option being added 603 * uint32_t: the value of that option 604 * output: void *: pointer to the option that was added, or NULL on failure. 605 */ 606 607 void * 608 add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value) 609 { 610 return (add_pkt_opt(dpkt, opt_type, &opt_value, 4)); 611 } 612 613 /* 614 * add_pkt_prl(): adds the parameter request option to the packet 615 * 616 * input: dhcp_pkt_t *: the packet to add the option to 617 * dhcp_smach_t *: state machine with request option 618 * output: void *: pointer to the option that was added, or NULL on failure. 619 */ 620 621 void * 622 add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp) 623 { 624 uint_t len; 625 626 if (dsmp->dsm_prllen == 0) 627 return (0); 628 629 if (dpkt->pkt_isv6) { 630 uint16_t *prl; 631 632 /* 633 * RFC 3315 requires that we include the option, even if we 634 * have nothing to request. 635 */ 636 if (dsmp->dsm_prllen == 0) 637 prl = NULL; 638 else 639 prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t)); 640 641 for (len = 0; len < dsmp->dsm_prllen; len++) 642 prl[len] = htons(dsmp->dsm_prl[len]); 643 return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl, 644 len * sizeof (uint16_t))); 645 } else { 646 uint8_t *prl = alloca(dsmp->dsm_prllen); 647 648 for (len = 0; len < dsmp->dsm_prllen; len++) 649 prl[len] = dsmp->dsm_prl[len]; 650 return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len)); 651 } 652 } 653 654 /* 655 * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR 656 * (DHCPv6) options to the packet to represent the given LIF. 657 * 658 * input: dhcp_pkt_t *: the packet to add the options to 659 * dhcp_lif_t *: the logical interface to represent 660 * int: status code (unused for IPv4 DHCP) 661 * const char *: message to include with status option, or NULL 662 * output: boolean_t: B_TRUE on success, B_FALSE on failure 663 */ 664 665 boolean_t 666 add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg) 667 { 668 if (lif->lif_pif->pif_isv6) { 669 dhcp_smach_t *dsmp; 670 dhcpv6_message_t *d6m; 671 dhcpv6_ia_na_t d6in; 672 dhcpv6_iaaddr_t d6ia; 673 uint32_t iaid; 674 uint16_t *statusopt; 675 dhcpv6_option_t *d6o, *d6so; 676 uint_t olen; 677 678 /* 679 * Currently, we support just one IAID related to the primary 680 * LIF on the state machine. 681 */ 682 dsmp = lif->lif_lease->dl_smach; 683 iaid = dsmp->dsm_lif->lif_iaid; 684 iaid = htonl(iaid); 685 686 d6m = (dhcpv6_message_t *)dpkt->pkt; 687 688 /* 689 * Find or create the IA_NA needed for this LIF. If we 690 * supported IA_TA, we'd check the IFF_TEMPORARY bit here. 691 */ 692 d6o = NULL; 693 while ((d6o = dhcpv6_find_option(d6m + 1, 694 dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA, 695 &olen)) != NULL) { 696 if (olen < sizeof (d6in)) 697 continue; 698 (void) memcpy(&d6in, d6o, sizeof (d6in)); 699 if (d6in.d6in_iaid == iaid) 700 break; 701 } 702 if (d6o == NULL) { 703 d6in.d6in_iaid = iaid; 704 d6in.d6in_t1 = 0; 705 d6in.d6in_t2 = 0; 706 d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, 707 (dhcpv6_option_t *)&d6in + 1, 708 sizeof (d6in) - sizeof (*d6o)); 709 if (d6o == NULL) 710 return (B_FALSE); 711 } 712 713 /* 714 * Now add the IAADDR suboption for this LIF. No need to 715 * search here, as we know that this is unique. 716 */ 717 d6ia.d6ia_addr = lif->lif_v6addr; 718 719 /* 720 * For Release and Decline, we zero out the lifetime. For 721 * Renew and Rebind, we report the original time as the 722 * preferred and valid lifetimes. 723 */ 724 if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE || 725 d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) { 726 d6ia.d6ia_preflife = 0; 727 d6ia.d6ia_vallife = 0; 728 } else { 729 d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start); 730 d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start); 731 } 732 d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR, 733 (dhcpv6_option_t *)&d6ia + 1, 734 sizeof (d6ia) - sizeof (*d6o)); 735 if (d6so == NULL) 736 return (B_FALSE); 737 738 /* 739 * Add a status code suboption to the IAADDR to tell the server 740 * why we're declining the address. Note that we must manually 741 * update the enclosing IA_NA, as add_pkt_subopt doesn't know 742 * how to do that. 743 */ 744 if (status != DHCPV6_STAT_SUCCESS || msg != NULL) { 745 olen = sizeof (*statusopt) + 746 (msg == NULL ? 0 : strlen(msg)); 747 statusopt = alloca(olen); 748 *statusopt = htons(status); 749 if (msg != NULL) { 750 (void) memcpy((char *)(statusopt + 1), msg, 751 olen - sizeof (*statusopt)); 752 } 753 d6so = add_pkt_subopt(dpkt, d6so, 754 DHCPV6_OPT_STATUS_CODE, statusopt, olen); 755 if (d6so != NULL) { 756 /* 757 * Update for length of suboption header and 758 * suboption contents. 759 */ 760 (void) update_v6opt_len(d6o, sizeof (*d6so) + 761 olen); 762 } 763 } 764 } else { 765 /* 766 * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option. 767 * In all other cases (RELEASE and REQUEST), we need to set 768 * ciadr. 769 */ 770 if (pkt_send_type(dpkt) == DECLINE) { 771 if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, 772 lif->lif_addr)) 773 return (B_FALSE); 774 } else { 775 dpkt->pkt->ciaddr.s_addr = lif->lif_addr; 776 } 777 778 /* 779 * It's not too worrisome if the message fails to fit in the 780 * packet. The result will still be valid. 781 */ 782 if (msg != NULL) 783 (void) add_pkt_opt(dpkt, CD_MESSAGE, msg, 784 strlen(msg) + 1); 785 } 786 return (B_TRUE); 787 } 788 789 /* 790 * free_pkt_entry(): frees a packet list list entry 791 * 792 * input: PKT_LIST *: the packet list entry to free 793 * output: void 794 */ 795 void 796 free_pkt_entry(PKT_LIST *plp) 797 { 798 if (plp != NULL) { 799 free(plp->pkt); 800 free(plp); 801 } 802 } 803 804 /* 805 * free_pkt_list(): frees an entire packet list 806 * 807 * input: PKT_LIST **: the packet list to free 808 * output: void 809 */ 810 811 void 812 free_pkt_list(PKT_LIST **head) 813 { 814 PKT_LIST *plp; 815 816 while ((plp = *head) != NULL) { 817 remque(plp); 818 free_pkt_entry(plp); 819 } 820 } 821 822 /* 823 * send_pkt_internal(): sends a packet out on an interface 824 * 825 * input: dhcp_smach_t *: the state machine with a packet to send 826 * output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise 827 */ 828 829 static boolean_t 830 send_pkt_internal(dhcp_smach_t *dsmp) 831 { 832 ssize_t n_bytes; 833 dhcp_lif_t *lif = dsmp->dsm_lif; 834 dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt; 835 uchar_t ptype = pkt_send_type(dpkt); 836 const char *pkt_name; 837 struct iovec iov; 838 struct msghdr msg; 839 struct cmsghdr *cmsg; 840 struct in6_pktinfo *ipi6; 841 boolean_t ismcast; 842 int msgtype; 843 844 /* 845 * Timer should not be running at the point we go to send a packet. 846 */ 847 if (dsmp->dsm_retrans_timer != -1) { 848 dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit " 849 "timer on %s", dsmp->dsm_name); 850 stop_pkt_retransmission(dsmp); 851 } 852 853 pkt_name = pkt_type_to_string(ptype, dpkt->pkt_isv6); 854 855 /* 856 * if needed, schedule a retransmission timer, then attempt to 857 * send the packet. if we fail, then log the error. our 858 * return value should indicate whether or not we were 859 * successful in sending the request, independent of whether 860 * we could schedule a timer. 861 */ 862 863 if (dsmp->dsm_send_timeout != 0) { 864 if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq, 865 dsmp->dsm_send_timeout, retransmit, dsmp)) == -1) 866 dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot " 867 "schedule retransmit timer for %s packet", 868 pkt_name); 869 else 870 hold_smach(dsmp); 871 } 872 873 if (dpkt->pkt_isv6) { 874 hrtime_t delta; 875 876 /* 877 * Convert current time into centiseconds since transaction 878 * started. This is what DHCPv6 expects to see in the Elapsed 879 * Time option. 880 */ 881 delta = (gethrtime() - dsmp->dsm_neg_hrtime) / 882 (NANOSEC / 100); 883 if (delta > DHCPV6_FOREVER) 884 delta = DHCPV6_FOREVER; 885 (void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME); 886 (void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME, 887 htons(delta)); 888 } else { 889 /* 890 * set the `pkt->secs' field depending on the type of packet. 891 * it should be zero, except in the following cases: 892 * 893 * DISCOVER: set to the number of seconds since we started 894 * trying to obtain a lease. 895 * 896 * INFORM: set to the number of seconds since we started 897 * trying to get configuration parameters. 898 * 899 * REQUEST: if in the REQUESTING state, then same value as 900 * DISCOVER, otherwise the number of seconds 901 * since we started trying to obtain a lease. 902 * 903 * we also set `dsm_newstart_monosec', to the time we sent a 904 * REQUEST or DISCOVER packet, so we know the lease start 905 * time (the DISCOVER case is for handling BOOTP servers). 906 */ 907 908 switch (ptype) { 909 910 case DISCOVER: 911 dsmp->dsm_newstart_monosec = monosec(); 912 dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec - 913 hrtime_to_monosec(dsmp->dsm_neg_hrtime); 914 dpkt->pkt->secs = htons(dsmp->dsm_disc_secs); 915 break; 916 917 case INFORM: 918 dpkt->pkt->secs = htons(monosec() - 919 hrtime_to_monosec(dsmp->dsm_neg_hrtime)); 920 break; 921 922 case REQUEST: 923 dsmp->dsm_newstart_monosec = monosec(); 924 925 if (dsmp->dsm_state == REQUESTING) { 926 dpkt->pkt->secs = htons(dsmp->dsm_disc_secs); 927 break; 928 } 929 930 dpkt->pkt->secs = htons(monosec() - 931 hrtime_to_monosec(dsmp->dsm_neg_hrtime)); 932 break; 933 934 default: 935 dpkt->pkt->secs = htons(0); 936 break; 937 } 938 } 939 940 if (dpkt->pkt_isv6) { 941 struct sockaddr_in6 sin6; 942 943 (void) memset(&iov, 0, sizeof (iov)); 944 iov.iov_base = dpkt->pkt; 945 iov.iov_len = dpkt->pkt_cur_len; 946 947 (void) memset(&msg, 0, sizeof (msg)); 948 msg.msg_name = &dsmp->dsm_send_dest.v6; 949 msg.msg_namelen = sizeof (struct sockaddr_in6); 950 msg.msg_iov = &iov; 951 msg.msg_iovlen = 1; 952 953 /* 954 * If the address that's requested cannot be reached, then fall 955 * back to the multcast address. 956 */ 957 if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) { 958 ismcast = B_TRUE; 959 } else { 960 struct dstinforeq dinfo; 961 struct strioctl str; 962 963 ismcast = B_FALSE; 964 (void) memset(&dinfo, 0, sizeof (dinfo)); 965 dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr; 966 str.ic_cmd = SIOCGDSTINFO; 967 str.ic_timout = 0; 968 str.ic_len = sizeof (dinfo); 969 str.ic_dp = (char *)&dinfo; 970 if (ioctl(v6_sock_fd, I_STR, &str) == -1) { 971 dhcpmsg(MSG_ERR, 972 "send_pkt_internal: ioctl SIOCGDSTINFO"); 973 } else if (!dinfo.dir_dreachable) { 974 char abuf[INET6_ADDRSTRLEN]; 975 976 dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is " 977 "not reachable; using multicast instead", 978 inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf, 979 sizeof (abuf))); 980 sin6 = dsmp->dsm_send_dest.v6; 981 sin6.sin6_addr = 982 ipv6_all_dhcp_relay_and_servers; 983 msg.msg_name = &sin6; 984 ismcast = B_TRUE; 985 } 986 } 987 988 /* 989 * Make room for our ancillary data option as well as a dummy 990 * option used by CMSG_NXTHDR. 991 */ 992 msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT + 993 sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg); 994 msg.msg_control = alloca(msg.msg_controllen); 995 cmsg = CMSG_FIRSTHDR(&msg); 996 cmsg->cmsg_level = IPPROTO_IPV6; 997 cmsg->cmsg_type = IPV6_PKTINFO; 998 /* LINTED: alignment */ 999 ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); 1000 if (ismcast) 1001 ipi6->ipi6_addr = lif->lif_v6addr; 1002 else 1003 ipi6->ipi6_addr = my_in6addr_any; 1004 if (lif->lif_pif->pif_under_ipmp) 1005 ipi6->ipi6_ifindex = lif->lif_pif->pif_grindex; 1006 else 1007 ipi6->ipi6_ifindex = lif->lif_pif->pif_index; 1008 cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg; 1009 1010 /* 1011 * Now correct the control message length. 1012 */ 1013 cmsg = CMSG_NXTHDR(&msg, cmsg); 1014 msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control; 1015 1016 n_bytes = sendmsg(v6_sock_fd, &msg, 0); 1017 } else { 1018 n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt, 1019 dpkt->pkt_cur_len, 0, 1020 (struct sockaddr *)&dsmp->dsm_send_dest.v4, 1021 sizeof (struct sockaddr_in)); 1022 } 1023 1024 if (n_bytes != dpkt->pkt_cur_len) { 1025 msgtype = (n_bytes == -1) ? MSG_ERR : MSG_WARNING; 1026 if (dsmp->dsm_retrans_timer == -1) 1027 dhcpmsg(msgtype, "send_pkt_internal: cannot send " 1028 "%s packet to server", pkt_name); 1029 else 1030 dhcpmsg(msgtype, "send_pkt_internal: cannot send " 1031 "%s packet to server (will retry in %u seconds)", 1032 pkt_name, dsmp->dsm_send_timeout / MILLISEC); 1033 return (B_FALSE); 1034 } 1035 1036 dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name, 1037 pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name); 1038 1039 dsmp->dsm_packet_sent++; 1040 dsmp->dsm_sent++; 1041 return (B_TRUE); 1042 } 1043 1044 /* 1045 * send_pkt(): sends a packet out 1046 * 1047 * input: dhcp_smach_t *: the state machine sending the packet 1048 * dhcp_pkt_t *: the packet to send out 1049 * in_addr_t: the destination IP address for the packet 1050 * stop_func_t *: a pointer to function to indicate when to stop 1051 * retransmitting the packet (if NULL, packet is 1052 * not retransmitted) 1053 * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise 1054 */ 1055 1056 boolean_t 1057 send_pkt(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in_addr_t dest, 1058 stop_func_t *stop) 1059 { 1060 /* 1061 * packets must be at least sizeof (PKT) or they may be dropped 1062 * by routers. pad out the packet in this case. 1063 */ 1064 1065 dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT)); 1066 1067 dsmp->dsm_packet_sent = 0; 1068 1069 (void) memset(&dsmp->dsm_send_dest.v4, 0, 1070 sizeof (dsmp->dsm_send_dest.v4)); 1071 dsmp->dsm_send_dest.v4.sin_addr.s_addr = dest; 1072 dsmp->dsm_send_dest.v4.sin_family = AF_INET; 1073 dsmp->dsm_send_dest.v4.sin_port = htons(IPPORT_BOOTPS); 1074 dsmp->dsm_send_stop_func = stop; 1075 1076 /* 1077 * TODO: dispose of this gruesome assumption (there's no real 1078 * technical gain from doing so, but it would be cleaner) 1079 */ 1080 1081 assert(dpkt == &dsmp->dsm_send_pkt); 1082 1083 /* 1084 * clear out any packets which had been previously received 1085 * but not pulled off of the recv_packet queue. 1086 */ 1087 1088 free_pkt_list(&dsmp->dsm_recv_pkt_list); 1089 1090 if (stop == NULL) 1091 dsmp->dsm_send_timeout = 0; /* prevents retransmissions */ 1092 else 1093 next_retransmission(dsmp, B_TRUE, B_FALSE); 1094 1095 return (send_pkt_internal(dsmp)); 1096 } 1097 1098 /* 1099 * send_pkt_v6(): sends a DHCPv6 packet out 1100 * 1101 * input: dhcp_smach_t *: the state machine sending the packet 1102 * dhcp_pkt_t *: the packet to send out 1103 * in6_addr_t: the destination IPv6 address for the packet 1104 * stop_func_t *: a pointer to function to indicate when to stop 1105 * retransmitting the packet (if NULL, packet is 1106 * not retransmitted) 1107 * uint_t: Initial Retransmit Timer value 1108 * uint_t: Maximum Retransmit Timer value, zero if none 1109 * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise 1110 */ 1111 1112 boolean_t 1113 send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest, 1114 stop_func_t *stop, uint_t irt, uint_t mrt) 1115 { 1116 dsmp->dsm_packet_sent = 0; 1117 1118 (void) memset(&dsmp->dsm_send_dest.v6, 0, 1119 sizeof (dsmp->dsm_send_dest.v6)); 1120 dsmp->dsm_send_dest.v6.sin6_addr = dest; 1121 dsmp->dsm_send_dest.v6.sin6_family = AF_INET6; 1122 dsmp->dsm_send_dest.v6.sin6_port = htons(IPPORT_DHCPV6S); 1123 dsmp->dsm_send_stop_func = stop; 1124 1125 /* 1126 * TODO: dispose of this gruesome assumption (there's no real 1127 * technical gain from doing so, but it would be cleaner) 1128 */ 1129 1130 assert(dpkt == &dsmp->dsm_send_pkt); 1131 1132 /* 1133 * clear out any packets which had been previously received 1134 * but not pulled off of the recv_packet queue. 1135 */ 1136 1137 free_pkt_list(&dsmp->dsm_recv_pkt_list); 1138 1139 if (stop == NULL) { 1140 dsmp->dsm_send_timeout = 0; /* prevents retransmissions */ 1141 } else { 1142 dsmp->dsm_send_timeout = irt; 1143 dsmp->dsm_send_tcenter = mrt; 1144 /* 1145 * This is quite ugly, but RFC 3315 section 17.1.2 requires 1146 * that the RAND value for the very first retransmission of a 1147 * Solicit message is strictly greater than zero. 1148 */ 1149 next_retransmission(dsmp, B_TRUE, 1150 pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT); 1151 } 1152 1153 return (send_pkt_internal(dsmp)); 1154 } 1155 1156 /* 1157 * retransmit(): retransmits the current packet on an interface 1158 * 1159 * input: iu_tq_t *: unused 1160 * void *: the dhcp_smach_t * (state machine) sending a packet 1161 * output: void 1162 */ 1163 1164 /* ARGSUSED */ 1165 static void 1166 retransmit(iu_tq_t *tqp, void *arg) 1167 { 1168 dhcp_smach_t *dsmp = arg; 1169 1170 dsmp->dsm_retrans_timer = -1; 1171 1172 if (!verify_smach(dsmp)) 1173 return; 1174 1175 /* 1176 * Check the callback to see if we should keep sending retransmissions. 1177 * Compute the next retransmission time first, so that the callback can 1178 * cap the value if need be. (Required for DHCPv6 Confirm messages.) 1179 * 1180 * Hold the state machine across the callback so that the called 1181 * function can remove the state machine from the system without 1182 * disturbing the string used subsequently for verbose logging. The 1183 * Release function destroys the state machine when the retry count 1184 * expires. 1185 */ 1186 1187 next_retransmission(dsmp, B_FALSE, B_FALSE); 1188 hold_smach(dsmp); 1189 if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) { 1190 dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s", 1191 dsmp->dsm_name); 1192 } else { 1193 dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s", 1194 dsmp->dsm_name); 1195 (void) send_pkt_internal(dsmp); 1196 } 1197 release_smach(dsmp); 1198 } 1199 1200 /* 1201 * stop_pkt_retransmission(): stops retransmission of last sent packet 1202 * 1203 * input: dhcp_smach_t *: the state machine to stop retransmission on 1204 * output: void 1205 */ 1206 1207 void 1208 stop_pkt_retransmission(dhcp_smach_t *dsmp) 1209 { 1210 if (dsmp->dsm_retrans_timer != -1 && 1211 iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) { 1212 dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s", 1213 dsmp->dsm_name); 1214 dsmp->dsm_retrans_timer = -1; 1215 release_smach(dsmp); 1216 } 1217 } 1218 1219 /* 1220 * retransmit_now(): force a packet retransmission right now. Used only with 1221 * the DHCPv6 UseMulticast status code. Use with caution; 1222 * triggered retransmissions can cause packet storms. 1223 * 1224 * input: dhcp_smach_t *: the state machine to force retransmission on 1225 * output: void 1226 */ 1227 1228 void 1229 retransmit_now(dhcp_smach_t *dsmp) 1230 { 1231 stop_pkt_retransmission(dsmp); 1232 (void) send_pkt_internal(dsmp); 1233 } 1234 1235 /* 1236 * alloc_pkt_entry(): Allocates a packet list entry with a given data area 1237 * size. 1238 * 1239 * input: size_t: size of data area for packet 1240 * boolean_t: B_TRUE for IPv6 1241 * output: PKT_LIST *: allocated packet list entry 1242 */ 1243 1244 PKT_LIST * 1245 alloc_pkt_entry(size_t psize, boolean_t isv6) 1246 { 1247 PKT_LIST *plp; 1248 1249 if ((plp = calloc(1, sizeof (*plp))) == NULL || 1250 (plp->pkt = malloc(psize)) == NULL) { 1251 free(plp); 1252 plp = NULL; 1253 } else { 1254 plp->len = psize; 1255 plp->isv6 = isv6; 1256 } 1257 1258 return (plp); 1259 } 1260 1261 /* 1262 * sock_recvpkt(): read from the given socket into an allocated buffer and 1263 * handles any ancillary data options. 1264 * 1265 * input: int: file descriptor to read 1266 * PKT_LIST *: allocated buffer 1267 * output: ssize_t: number of bytes read, or -1 on error 1268 */ 1269 1270 static ssize_t 1271 sock_recvpkt(int fd, PKT_LIST *plp) 1272 { 1273 struct iovec iov; 1274 struct msghdr msg; 1275 int64_t ctrl[8192 / sizeof (int64_t)]; 1276 ssize_t msglen; 1277 1278 (void) memset(&iov, 0, sizeof (iov)); 1279 iov.iov_base = (caddr_t)plp->pkt; 1280 iov.iov_len = plp->len; 1281 1282 (void) memset(&msg, 0, sizeof (msg)); 1283 msg.msg_name = &plp->pktfrom; 1284 msg.msg_namelen = sizeof (plp->pktfrom); 1285 msg.msg_iov = &iov; 1286 msg.msg_iovlen = 1; 1287 msg.msg_control = ctrl; 1288 msg.msg_controllen = sizeof (ctrl); 1289 1290 if ((msglen = recvmsg(fd, &msg, 0)) != -1) { 1291 struct cmsghdr *cmsg; 1292 1293 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 1294 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 1295 struct sockaddr_in *sinp; 1296 struct sockaddr_in6 *sin6; 1297 struct in6_pktinfo *ipi6; 1298 1299 switch (cmsg->cmsg_level) { 1300 case IPPROTO_IP: 1301 switch (cmsg->cmsg_type) { 1302 case IP_RECVDSTADDR: 1303 sinp = (struct sockaddr_in *) 1304 &plp->pktto; 1305 sinp->sin_family = AF_INET; 1306 (void) memcpy(&sinp->sin_addr.s_addr, 1307 CMSG_DATA(cmsg), 1308 sizeof (ipaddr_t)); 1309 break; 1310 1311 case IP_RECVIF: 1312 (void) memcpy(&plp->ifindex, 1313 CMSG_DATA(cmsg), sizeof (uint_t)); 1314 break; 1315 } 1316 break; 1317 1318 case IPPROTO_IPV6: 1319 switch (cmsg->cmsg_type) { 1320 case IPV6_PKTINFO: 1321 /* LINTED: alignment */ 1322 ipi6 = (struct in6_pktinfo *) 1323 CMSG_DATA(cmsg); 1324 sin6 = (struct sockaddr_in6 *) 1325 &plp->pktto; 1326 sin6->sin6_family = AF_INET6; 1327 (void) memcpy(&sin6->sin6_addr, 1328 &ipi6->ipi6_addr, 1329 sizeof (ipi6->ipi6_addr)); 1330 (void) memcpy(&plp->ifindex, 1331 &ipi6->ipi6_ifindex, 1332 sizeof (uint_t)); 1333 break; 1334 } 1335 } 1336 } 1337 } 1338 return (msglen); 1339 } 1340 1341 /* 1342 * recv_pkt(): receives a single DHCP packet on a given file descriptor. 1343 * 1344 * input: int: the file descriptor to receive the packet from 1345 * int: the maximum packet size to allow 1346 * boolean_t: B_TRUE for IPv6 1347 * output: PKT_LIST *: the received packet 1348 */ 1349 1350 PKT_LIST * 1351 recv_pkt(int fd, int mtu, boolean_t isv6) 1352 { 1353 PKT_LIST *plp; 1354 ssize_t retval; 1355 1356 if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) { 1357 dhcpmsg(MSG_ERROR, 1358 "recv_pkt: allocation failure; dropped packet"); 1359 return (NULL); 1360 } 1361 1362 retval = sock_recvpkt(fd, plp); 1363 if (retval == -1) { 1364 dhcpmsg(MSG_ERR, "recv_pkt: recvfrom v%d failed, dropped", 1365 isv6 ? 6 : 4); 1366 goto failure; 1367 } 1368 1369 plp->len = retval; 1370 1371 if (isv6) { 1372 if (retval < sizeof (dhcpv6_message_t)) { 1373 dhcpmsg(MSG_WARNING, "recv_pkt: runt message"); 1374 goto failure; 1375 } 1376 } else { 1377 switch (dhcp_options_scan(plp, B_TRUE)) { 1378 1379 case DHCP_WRONG_MSG_TYPE: 1380 dhcpmsg(MSG_WARNING, 1381 "recv_pkt: unexpected DHCP message"); 1382 goto failure; 1383 1384 case DHCP_GARBLED_MSG_TYPE: 1385 dhcpmsg(MSG_WARNING, 1386 "recv_pkt: garbled DHCP message type"); 1387 goto failure; 1388 1389 case DHCP_BAD_OPT_OVLD: 1390 dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload"); 1391 goto failure; 1392 1393 case 0: 1394 break; 1395 1396 default: 1397 dhcpmsg(MSG_WARNING, 1398 "recv_pkt: packet corrupted, dropped"); 1399 goto failure; 1400 } 1401 } 1402 return (plp); 1403 1404 failure: 1405 free_pkt_entry(plp); 1406 return (NULL); 1407 } 1408 1409 /* 1410 * pkt_v4_match(): check if a given DHCPv4 message type is in a given set 1411 * 1412 * input: uchar_t: packet type 1413 * dhcp_message_type_t: bit-wise OR of DHCP_P* values. 1414 * output: boolean_t: B_TRUE if packet type is in the set 1415 */ 1416 1417 boolean_t 1418 pkt_v4_match(uchar_t type, dhcp_message_type_t match_type) 1419 { 1420 /* 1421 * note: the ordering here allows direct indexing of the table 1422 * based on the RFC2131 packet type value passed in. 1423 */ 1424 1425 static dhcp_message_type_t type_map[] = { 1426 DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST, 1427 DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE, 1428 DHCP_PINFORM 1429 }; 1430 1431 if (type < (sizeof (type_map) / sizeof (*type_map))) 1432 return ((type_map[type] & match_type) ? B_TRUE : B_FALSE); 1433 else 1434 return (B_FALSE); 1435 } 1436 1437 /* 1438 * pkt_smach_enqueue(): enqueue a packet on a given state machine 1439 * 1440 * input: dhcp_smach_t: state machine 1441 * PKT_LIST *: packet to enqueue 1442 * output: none 1443 */ 1444 1445 void 1446 pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp) 1447 { 1448 dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s", 1449 pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6), 1450 dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name); 1451 1452 /* add to front of list */ 1453 insque(plp, &dsmp->dsm_recv_pkt_list); 1454 } 1455 1456 /* 1457 * next_retransmission(): computes the number of seconds until the next 1458 * retransmission, based on the algorithms in RFCs 2131 1459 * 3315. 1460 * 1461 * input: dhcp_smach_t *: state machine that needs a new timer 1462 * boolean_t: B_TRUE if this is the first time sending the message 1463 * boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2) 1464 * output: none 1465 */ 1466 1467 static void 1468 next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send, 1469 boolean_t positive_only) 1470 { 1471 uint32_t timeout_ms; 1472 1473 if (dsmp->dsm_isv6) { 1474 double randval; 1475 1476 /* 1477 * The RFC specifies 0 to 10% jitter for the initial 1478 * solicitation, and plus or minus 10% jitter for all others. 1479 * This works out to 100 milliseconds on the shortest timer we 1480 * use. 1481 */ 1482 if (positive_only) 1483 randval = drand48() / 10.0; 1484 else 1485 randval = (drand48() - 0.5) / 5.0; 1486 1487 /* The RFC specifies doubling *after* the first transmission */ 1488 timeout_ms = dsmp->dsm_send_timeout; 1489 if (!first_send) 1490 timeout_ms *= 2; 1491 timeout_ms += (int)(randval * dsmp->dsm_send_timeout); 1492 1493 /* This checks the MRT (maximum retransmission time) */ 1494 if (dsmp->dsm_send_tcenter != 0 && 1495 timeout_ms > dsmp->dsm_send_tcenter) { 1496 timeout_ms = dsmp->dsm_send_tcenter + 1497 (uint_t)(randval * dsmp->dsm_send_tcenter); 1498 } 1499 1500 dsmp->dsm_send_timeout = timeout_ms; 1501 } else { 1502 if (dsmp->dsm_state == RENEWING || 1503 dsmp->dsm_state == REBINDING) { 1504 monosec_t mono; 1505 1506 timeout_ms = dsmp->dsm_state == RENEWING ? 1507 dsmp->dsm_leases->dl_t2.dt_start : 1508 dsmp->dsm_leases->dl_lifs->lif_expire.dt_start; 1509 timeout_ms += dsmp->dsm_curstart_monosec; 1510 mono = monosec(); 1511 if (mono > timeout_ms) 1512 timeout_ms = 0; 1513 else 1514 timeout_ms -= mono; 1515 timeout_ms *= MILLISEC / 2; 1516 } else { 1517 /* 1518 * Start at 4, and increase by a factor of 2 up to 64. 1519 */ 1520 if (first_send) { 1521 timeout_ms = 4 * MILLISEC; 1522 } else { 1523 timeout_ms = MIN(dsmp->dsm_send_tcenter << 1, 1524 64 * MILLISEC); 1525 } 1526 } 1527 1528 dsmp->dsm_send_tcenter = timeout_ms; 1529 1530 /* 1531 * At each iteration, jitter the timeout by some fraction of a 1532 * second. 1533 */ 1534 dsmp->dsm_send_timeout = timeout_ms + 1535 ((lrand48() % (2 * MILLISEC)) - MILLISEC); 1536 } 1537 } 1538 1539 /* 1540 * dhcp_ip_default(): open and bind the default IP sockets used for I/O and 1541 * interface control. 1542 * 1543 * input: none 1544 * output: B_TRUE on success 1545 */ 1546 1547 boolean_t 1548 dhcp_ip_default(void) 1549 { 1550 int on = 1; 1551 1552 if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 1553 dhcpmsg(MSG_ERR, 1554 "dhcp_ip_default: unable to create IPv4 socket"); 1555 return (B_FALSE); 1556 } 1557 1558 if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on, 1559 sizeof (on)) == -1) { 1560 dhcpmsg(MSG_ERR, 1561 "dhcp_ip_default: unable to enable IP_RECVDSTADDR"); 1562 return (B_FALSE); 1563 } 1564 1565 if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) == 1566 -1) { 1567 dhcpmsg(MSG_ERR, 1568 "dhcp_ip_default: unable to enable IP_RECVIF"); 1569 return (B_FALSE); 1570 } 1571 1572 if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) { 1573 dhcpmsg(MSG_ERROR, 1574 "dhcp_ip_default: unable to bind IPv4 socket to port %d", 1575 IPPORT_BOOTPC); 1576 return (B_FALSE); 1577 } 1578 1579 if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_global, 1580 NULL) == -1) { 1581 dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to " 1582 "receive IPv4 broadcasts"); 1583 return (B_FALSE); 1584 } 1585 1586 if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { 1587 dhcpmsg(MSG_ERR, 1588 "dhcp_ip_default: unable to create IPv6 socket"); 1589 return (B_FALSE); 1590 } 1591 1592 if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 1593 sizeof (on)) == -1) { 1594 dhcpmsg(MSG_ERR, 1595 "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO"); 1596 return (B_FALSE); 1597 } 1598 1599 if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) { 1600 dhcpmsg(MSG_ERROR, 1601 "dhcp_ip_default: unable to bind IPv6 socket to port %d", 1602 IPPORT_DHCPV6C); 1603 return (B_FALSE); 1604 } 1605 1606 if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_global, 1607 NULL) == -1) { 1608 dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to " 1609 "receive IPv6 packets"); 1610 return (B_FALSE); 1611 } 1612 1613 return (B_TRUE); 1614 } 1615