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