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