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