1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
  25  *
  26  * REQUESTING state of the client state machine.
  27  */
  28 
  29 #include <stdlib.h>
  30 #include <string.h>
  31 #include <search.h>
  32 #include <sys/types.h>
  33 #include <netinet/in.h>
  34 #include <netinet/dhcp.h>
  35 #include <netinet/udp.h>
  36 #include <netinet/ip_var.h>
  37 #include <netinet/udp_var.h>
  38 #include <arpa/inet.h>
  39 #include <dhcp_hostconf.h>
  40 #include <dhcpagent_util.h>
  41 #include <dhcpmsg.h>
  42 
  43 #include "states.h"
  44 #include "util.h"
  45 #include "packet.h"
  46 #include "interface.h"
  47 #include "agent.h"
  48 
  49 static PKT_LIST         *select_best(dhcp_smach_t *);
  50 static void             request_failed(dhcp_smach_t *);
  51 static stop_func_t      stop_requesting;
  52 
  53 /*
  54  * send_v6_request(): sends a DHCPv6 Request message and switches to REQUESTING
  55  *                    state.  This is a separate function because a NoBinding
  56  *                    response can also cause us to do this.
  57  *
  58  *   input: dhcp_smach_t *: the state machine
  59  *  output: none
  60  */
  61 
  62 void
  63 send_v6_request(dhcp_smach_t *dsmp)
  64 {
  65         dhcp_pkt_t *dpkt;
  66         dhcpv6_ia_na_t d6in;
  67 
  68         dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST);
  69         (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid,
  70             dsmp->dsm_serveridlen);
  71 
  72         /* Add an IA_NA option for our controlling LIF */
  73         d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
  74         d6in.d6in_t1 = htonl(0);
  75         d6in.d6in_t2 = htonl(0);
  76         (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
  77             (dhcpv6_option_t *)&d6in + 1,
  78             sizeof (d6in) - sizeof (dhcpv6_option_t));
  79 
  80         /* Add required Option Request option */
  81         (void) add_pkt_prl(dpkt, dsmp);
  82 
  83         /* Add FQDN if configured */
  84         (void) dhcp_add_fqdn_opt(dpkt, dsmp);
  85 
  86         (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
  87             DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);
  88 
  89         /* For DHCPv6, state switch cannot fail */
  90         (void) set_smach_state(dsmp, REQUESTING);
  91 }
  92 
  93 /*
  94  * server_unicast_option(): determines the server address to use based on the
  95  *                          DHCPv6 Server Unicast option present in the given
  96  *                          packet.
  97  *
  98  *   input: dhcp_smach_t *: the state machine
  99  *          PKT_LIST *: received packet (Advertisement or Reply)
 100  *  output: none
 101  */
 102 
 103 void
 104 server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp)
 105 {
 106         const dhcpv6_option_t *d6o;
 107         uint_t olen;
 108 
 109         d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen);
 110         olen -= sizeof (*d6o);
 111         /* LINTED: no consequent */
 112         if (d6o == NULL) {
 113                 /* No Server Unicast option specified */
 114         } else if (olen != sizeof (dsmp->dsm_server)) {
 115                 dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server "
 116                     "Unicast option with bad length",
 117                     pkt_type_to_string(pkt_recv_type(plp), B_TRUE));
 118         } else {
 119                 in6_addr_t addr;
 120 
 121                 (void) memcpy(&addr, d6o + 1, olen);
 122                 if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
 123                         dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
 124                             "to unspecified address ignored");
 125                 } else if (IN6_IS_ADDR_MULTICAST(&addr)) {
 126                         dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
 127                             "to multicast address ignored");
 128                 } else if (IN6_IS_ADDR_V4COMPAT(&addr) ||
 129                     IN6_IS_ADDR_V4MAPPED(&addr)) {
 130                         dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
 131                             "to invalid address ignored");
 132                 } else {
 133                         dsmp->dsm_server = addr;
 134                 }
 135         }
 136 }
 137 
 138 /*
 139  * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers.
 140  *                    if so, chooses the best one, sends a REQUEST to the
 141  *                    server and registers an event handler to receive
 142  *                    the ACK/NAK.  This may be called by the offer timer or
 143  *                    by any function that wants to check for offers after
 144  *                    canceling that timer.
 145  *
 146  *   input: iu_tq_t *: timer queue; non-NULL if this is a timer callback
 147  *          void *: the state machine receiving OFFER packets
 148  *  output: void
 149  */
 150 
 151 void
 152 dhcp_requesting(iu_tq_t *tqp, void *arg)
 153 {
 154         dhcp_smach_t            *dsmp = arg;
 155         dhcp_pkt_t              *dpkt;
 156         PKT_LIST                *offer;
 157         lease_t                 lease;
 158         boolean_t               isv6 = dsmp->dsm_isv6;
 159 
 160         /*
 161          * We assume here that if tqp is set, then this means we're being
 162          * called back by the offer wait timer.  If so, then drop our hold
 163          * on the state machine.  Otherwise, cancel the timer if it's running.
 164          */
 165         if (tqp != NULL) {
 166                 dhcpmsg(MSG_VERBOSE,
 167                     "dhcp_requesting: offer wait timer on v%d %s",
 168                     isv6 ? 6 : 4, dsmp->dsm_name);
 169                 dsmp->dsm_offer_timer = -1;
 170                 if (!verify_smach(dsmp))
 171                         return;
 172         } else {
 173                 cancel_offer_timer(dsmp);
 174         }
 175 
 176         /*
 177          * select the best OFFER; all others pitched.
 178          */
 179 
 180         offer = select_best(dsmp);
 181         if (offer == NULL) {
 182 
 183                 dhcpmsg(MSG_VERBOSE,
 184                     "no OFFERs/Advertisements on %s, waiting...",
 185                     dsmp->dsm_name);
 186 
 187                 /*
 188                  * no acceptable OFFERs have come in.  reschedule
 189                  * ourself for callback.
 190                  */
 191 
 192                 if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
 193                     dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) {
 194 
 195                         /*
 196                          * ugh.  the best we can do at this point is
 197                          * revert back to INIT and wait for a user to
 198                          * restart us.
 199                          */
 200 
 201                         dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
 202                             "reschedule callback, reverting to INIT state on "
 203                             "%s", dsmp->dsm_name);
 204 
 205                         stop_pkt_retransmission(dsmp);
 206                         (void) set_smach_state(dsmp, INIT);
 207                         dsmp->dsm_dflags |= DHCP_IF_FAILED;
 208                         ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
 209                 } else {
 210                         hold_smach(dsmp);
 211                 }
 212 
 213                 return;
 214         }
 215 
 216         /*
 217          * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly
 218          * declines all other offers we've received.  We can no longer use any
 219          * cached offers, so we must discard them now.  With DHCPv6, though,
 220          * we're permitted to hang onto the advertisements (offers) and try
 221          * them if the preferred one doesn't pan out.
 222          */
 223         if (!isv6)
 224                 free_pkt_list(&dsmp->dsm_recv_pkt_list);
 225 
 226         /* stop collecting packets. */
 227 
 228         stop_pkt_retransmission(dsmp);
 229 
 230         /*
 231          * For IPv4, check to see whether we got an OFFER or a BOOTP packet.
 232          * If we got a BOOTP packet, go to the BOUND state now.
 233          */
 234         if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
 235                 free_pkt_list(&dsmp->dsm_recv_pkt_list);
 236 
 237                 if (!set_smach_state(dsmp, REQUESTING)) {
 238                         dhcp_restart(dsmp);
 239                         return;
 240                 }
 241 
 242                 if (!dhcp_bound(dsmp, offer)) {
 243                         dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
 244                             "failed for %s", dsmp->dsm_name);
 245                         dhcp_restart(dsmp);
 246                         return;
 247                 }
 248 
 249                 return;
 250         }
 251 
 252         if (isv6) {
 253                 const char *estr, *msg;
 254                 const dhcpv6_option_t *d6o;
 255                 uint_t olen, msglen;
 256 
 257                 /* If there's a Status Code option, print the message */
 258                 d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
 259                     &olen);
 260                 (void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
 261                 print_server_msg(dsmp, msg, msglen);
 262 
 263                 /* Copy in the Server ID (guaranteed to be present now) */
 264                 if (!save_server_id(dsmp, offer))
 265                         goto failure;
 266 
 267                 /*
 268                  * Determine how to send this message.  If the Advertisement
 269                  * (offer) has the unicast option, then use the address
 270                  * specified in the option.  Otherwise, send via multicast.
 271                  */
 272                 server_unicast_option(dsmp, offer);
 273 
 274                 send_v6_request(dsmp);
 275         } else {
 276                 /* if we got a message from the server, display it. */
 277                 if (offer->opts[CD_MESSAGE] != NULL) {
 278                         print_server_msg(dsmp,
 279                             (char *)offer->opts[CD_MESSAGE]->value,
 280                             offer->opts[CD_MESSAGE]->len);
 281                 }
 282 
 283                 /*
 284                  * assemble a DHCPREQUEST, with the ciaddr field set to 0,
 285                  * since we got here from the INIT state.
 286                  */
 287 
 288                 dpkt = init_pkt(dsmp, REQUEST);
 289 
 290                 /*
 291                  * Grab the lease out of the OFFER; we know it's valid because
 292                  * select_best() already checked.  The max dhcp message size
 293                  * option is set to the interface max, minus the size of the
 294                  * udp and ip headers.
 295                  */
 296 
 297                 (void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
 298                     sizeof (lease_t));
 299 
 300                 (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
 301                 (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
 302                     htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
 303                 (void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
 304                     offer->pkt->yiaddr.s_addr);
 305                 (void) add_pkt_opt(dpkt, CD_SERVER_ID,
 306                     offer->opts[CD_SERVER_ID]->value,
 307                     offer->opts[CD_SERVER_ID]->len);
 308 
 309                 if (class_id_len != 0) {
 310                         (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
 311                             class_id_len);
 312                 }
 313                 (void) add_pkt_prl(dpkt, dsmp);
 314 
 315                 /*
 316                  * dsm_reqhost was set for this state machine in
 317                  * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
 318                  * host name was found
 319                  */
 320                 if (dhcp_add_fqdn_opt(dpkt, dsmp) != 0 &&
 321                     dsmp->dsm_reqhost != NULL) {
 322                         (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
 323                             strlen(dsmp->dsm_reqhost));
 324                 }
 325                 (void) add_pkt_opt(dpkt, CD_END, NULL, 0);
 326 
 327                 /*
 328                  * send out the REQUEST, trying retransmissions.  either a NAK
 329                  * or too many REQUEST attempts will revert us to SELECTING.
 330                  */
 331 
 332                 if (!set_smach_state(dsmp, REQUESTING)) {
 333                         dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
 334                             "REQUESTING state; reverting to INIT on %s",
 335                             dsmp->dsm_name);
 336                         goto failure;
 337                 }
 338 
 339                 (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
 340                     stop_requesting);
 341         }
 342 
 343         /* all done with the offer */
 344         free_pkt_entry(offer);
 345 
 346         return;
 347 
 348 failure:
 349         dsmp->dsm_dflags |= DHCP_IF_FAILED;
 350         (void) set_smach_state(dsmp, INIT);
 351         ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
 352         free_pkt_list(&dsmp->dsm_recv_pkt_list);
 353 }
 354 
 355 /*
 356  * compute_points_v6(): compute the number of "points" for a given v6
 357  *                      advertisement.
 358  *
 359  *   input: const PKT_LIST *: packet to inspect
 360  *          const dhcp_smach_t *: state machine that received the packet
 361  *  output: int: -1 to discard, -2 to accept immediately, >=0 for preference.
 362  */
 363 
 364 static int
 365 compute_points_v6(const PKT_LIST *pkt, const dhcp_smach_t *dsmp)
 366 {
 367         char abuf[INET6_ADDRSTRLEN];
 368         int points = 0;
 369         const dhcpv6_option_t *d6o, *d6so;
 370         uint_t olen, solen;
 371         int i;
 372         const char *estr, *msg;
 373         uint_t msglen;
 374 
 375         /*
 376          * Look through the packet contents.  Valid packets must have our
 377          * client ID and a server ID, which has already been checked by
 378          * dhcp_packet_lif.  Bonus points for each option.
 379          */
 380 
 381         /* One point for having a valid message. */
 382         points++;
 383 
 384         /*
 385          * Per RFC 3315, if the Advertise message says, "yes, we have no
 386          * bananas today," then ignore the entire message.  (Why it's just
 387          * _this_ error and no other is a bit of a mystery, but a standard is a
 388          * standard.)
 389          */
 390         d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_STATUS_CODE, &olen);
 391         if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) ==
 392             DHCPV6_STAT_NOADDRS) {
 393                 dhcpmsg(MSG_INFO,
 394                     "discard advertisement from %s on %s: no address status",
 395                     inet_ntop(AF_INET6,
 396                     &((struct sockaddr_in6 *)&pkt->pktfrom)->sin6_addr,
 397                     abuf, sizeof (abuf)), dsmp->dsm_name);
 398                 return (-1);
 399         }
 400 
 401         /* Two points for each batch of offered IP addresses */
 402         d6o = NULL;
 403         while ((d6o = dhcpv6_pkt_option(pkt, d6o, DHCPV6_OPT_IA_NA,
 404             &olen)) != NULL) {
 405 
 406                 /*
 407                  * Note that it's possible to have "no bananas" on an
 408                  * individual IA.  We must look for that here.
 409                  *
 410                  * RFC 3315 section 17.1.3 does not refer to the status code
 411                  * embedded in the IA itself.  However, the TAHI test suite
 412                  * checks for this specific case.  Because it's extremely
 413                  * unlikely that any usable server is going to report that it
 414                  * has no addresses on a network using DHCP for address
 415                  * assignment, we allow such messages to be dropped.
 416                  */
 417                 d6so = dhcpv6_find_option(
 418                     (const char *)d6o + sizeof (dhcpv6_ia_na_t),
 419                     olen - sizeof (dhcpv6_ia_na_t), NULL,
 420                     DHCPV6_OPT_STATUS_CODE, &solen);
 421                 if (dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen) ==
 422                     DHCPV6_STAT_NOADDRS)
 423                         return (-1);
 424                 points += 2;
 425         }
 426 
 427         /*
 428          * Note that we drive on in the case where there are no addresses.  The
 429          * hope here is that we'll at least get some useful configuration
 430          * information.
 431          */
 432 
 433         /* One point for each requested option */
 434         for (i = 0; i < dsmp->dsm_prllen; i++) {
 435                 if (dhcpv6_pkt_option(pkt, NULL, dsmp->dsm_prl[i], NULL) !=
 436                     NULL)
 437                         points++;
 438         }
 439 
 440         /*
 441          * Ten points for each point of "preference."  Note: the value 255 is
 442          * special.  It means "stop right now and select this server."
 443          */
 444         d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_PREFERENCE, &olen);
 445         if (d6o != NULL && olen == sizeof (*d6o) + 1) {
 446                 int pref = *(const uchar_t *)(d6o + 1);
 447 
 448                 if (pref == 255)
 449                         return (-2);
 450                 points += 10 * pref;
 451         }
 452 
 453         return (points);
 454 }
 455 
 456 /*
 457  * compute_points_v4(): compute the number of "points" for a given v4 offer.
 458  *
 459  *   input: const PKT_LIST *: packet to inspect
 460  *          const dhcp_smach_t *: state machine that received the packet
 461  *  output: int: -1 to discard, >=0 for preference.
 462  */
 463 
 464 static int
 465 compute_points_v4(const PKT_LIST *pkt)
 466 {
 467         int points = 0;
 468 
 469         if (pkt->opts[CD_DHCP_TYPE] == NULL) {
 470                 dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid BOOTP reply");
 471                 goto valid_offer;
 472         }
 473 
 474         if (pkt->opts[CD_LEASE_TIME] == NULL) {
 475                 dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without lease "
 476                     "time");
 477                 return (-1);
 478         }
 479 
 480         if (pkt->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
 481                 dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
 482                     "lease time");
 483                 return (-1);
 484         }
 485 
 486         if (pkt->opts[CD_SERVER_ID] == NULL) {
 487                 dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without server "
 488                     "id");
 489                 return (-1);
 490         }
 491 
 492         if (pkt->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
 493                 dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
 494                     "server id");
 495                 return (-1);
 496         }
 497 
 498         /* valid DHCP OFFER.  see if we got our parameters. */
 499         dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid OFFER packet");
 500         points += 30;
 501 
 502 valid_offer:
 503         if (pkt->rfc1048)
 504                 points += 5;
 505 
 506         /*
 507          * also could be faked, though more difficult because the encapsulation
 508          * is hard to encode on a BOOTP server; plus there's not as much real
 509          * estate in the packet for options, so it's likely this option would
 510          * get dropped.
 511          */
 512 
 513         if (pkt->opts[CD_VENDOR_SPEC] != NULL)
 514                 points += 80;
 515 
 516         if (pkt->opts[CD_SUBNETMASK] != NULL)
 517                 points++;
 518 
 519         if (pkt->opts[CD_ROUTER] != NULL)
 520                 points++;
 521 
 522         if (pkt->opts[CD_HOSTNAME] != NULL)
 523                 points += 5;
 524 
 525         return (points);
 526 }
 527 
 528 /*
 529  * select_best(): selects the best offer from a list of IPv4 OFFER packets or
 530  *                DHCPv6 Advertise packets.
 531  *
 532  *   input: dhcp_smach_t *: state machine with enqueued offers
 533  *  output: PKT_LIST *: the best packet, or NULL if none are acceptable
 534  */
 535 
 536 static PKT_LIST *
 537 select_best(dhcp_smach_t *dsmp)
 538 {
 539         PKT_LIST        *current = dsmp->dsm_recv_pkt_list;
 540         PKT_LIST        *next, *best = NULL;
 541         int             points, best_points = -1;
 542 
 543         /*
 544          * pick out the best offer.  point system.
 545          * what's important for IPv4?
 546          *
 547          *      0) DHCP (30 points)
 548          *      1) no option overload
 549          *      2) encapsulated vendor option (80 points)
 550          *      3) non-null sname and siaddr fields
 551          *      4) non-null file field
 552          *      5) hostname (5 points)
 553          *      6) subnetmask (1 point)
 554          *      7) router (1 point)
 555          */
 556 
 557         for (; current != NULL; current = next) {
 558                 next = current->next;
 559 
 560                 points = current->isv6 ?
 561                     compute_points_v6(current, dsmp) :
 562                     compute_points_v4(current);
 563 
 564                 /*
 565                  * Just discard any unacceptable entries we encounter.
 566                  */
 567                 if (points == -1) {
 568                         remque(current);
 569                         free_pkt_entry(current);
 570                         continue;
 571                 }
 572 
 573                 dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
 574 
 575                 /* Special case: stop now and select */
 576                 if (points == -2) {
 577                         best = current;
 578                         break;
 579                 }
 580 
 581                 if (points >= best_points) {
 582                         best_points = points;
 583                         best = current;
 584                 }
 585         }
 586 
 587         if (best != NULL) {
 588                 dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
 589                 remque(best);
 590         } else {
 591                 dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
 592         }
 593 
 594         return (best);
 595 }
 596 
 597 /*
 598  * accept_v4_acknak(): determine what to do with a DHCPv4 ACK/NAK based on the
 599  *                     current state.  If we're renewing or rebinding, the ACK
 600  *                     must be for the same address and must have a new lease
 601  *                     time.  If it's a NAK, then our cache is garbage, and we
 602  *                     must restart.  Finally, call dhcp_bound on accepted
 603  *                     ACKs.
 604  *
 605  *   input: dhcp_smach_t *: the state machine to handle the ACK/NAK
 606  *          PKT_LIST *: the ACK/NAK message
 607  *  output: void
 608  */
 609 
 610 static void
 611 accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp)
 612 {
 613         /* Account for received and processed messages */
 614         dsmp->dsm_received++;
 615 
 616         if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
 617                 if (dsmp->dsm_state != INFORM_SENT &&
 618                     dsmp->dsm_state != INFORMATION &&
 619                     (plp->opts[CD_LEASE_TIME] == NULL ||
 620                     plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
 621                         dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on "
 622                             "%s missing mandatory lease option, ignored",
 623                             dsmp->dsm_name);
 624                         dsmp->dsm_bad_offers++;
 625                         free_pkt_entry(plp);
 626                         return;
 627                 }
 628                 if ((dsmp->dsm_state == RENEWING ||
 629                     dsmp->dsm_state == REBINDING) &&
 630                     dsmp->dsm_leases->dl_lifs->lif_addr !=
 631                     plp->pkt->yiaddr.s_addr) {
 632                         dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK "
 633                             "packet has a different IP address (%s), ignored",
 634                             inet_ntoa(plp->pkt->yiaddr));
 635                         dsmp->dsm_bad_offers++;
 636                         free_pkt_entry(plp);
 637                         return;
 638                 }
 639         }
 640 
 641         /*
 642          * looks good; cancel the retransmission timer and unregister
 643          * the acknak handler. ACK to BOUND, NAK back to SELECTING.
 644          */
 645 
 646         stop_pkt_retransmission(dsmp);
 647 
 648         if (*plp->opts[CD_DHCP_TYPE]->value == NAK) {
 649                 dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s",
 650                     dsmp->dsm_name);
 651                 dsmp->dsm_bad_offers++;
 652                 free_pkt_entry(plp);
 653                 dhcp_restart(dsmp);
 654 
 655                 /*
 656                  * remove any bogus cached configuration we might have
 657                  * around (right now would only happen if we got here
 658                  * from INIT_REBOOT).
 659                  */
 660 
 661                 (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
 662                 return;
 663         }
 664 
 665         if (plp->opts[CD_SERVER_ID] == NULL ||
 666             plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
 667                 dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid "
 668                     "server id on %s", dsmp->dsm_name);
 669                 dsmp->dsm_bad_offers++;
 670                 free_pkt_entry(plp);
 671                 dhcp_restart(dsmp);
 672                 return;
 673         }
 674 
 675         if (plp->opts[CD_MESSAGE] != NULL) {
 676                 print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value,
 677                     plp->opts[CD_MESSAGE]->len);
 678         }
 679 
 680         dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name);
 681         if (!dhcp_bound(dsmp, plp)) {
 682                 dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed "
 683                     "for %s", dsmp->dsm_name);
 684                 dhcp_restart(dsmp);
 685         }
 686 }
 687 
 688 /*
 689  * accept_v6_message(): determine what to do with a DHCPv6 message based on the
 690  *                      current state.
 691  *
 692  *   input: dhcp_smach_t *: the state machine to handle the message
 693  *          PKT_LIST *: the DHCPv6 message
 694  *          const char *: type of message (for logging)
 695  *          uchar_t: type of message (extracted from packet)
 696  *  output: void
 697  */
 698 
 699 static void
 700 accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname,
 701     uchar_t recv_type)
 702 {
 703         const dhcpv6_option_t *d6o;
 704         uint_t olen;
 705         const char *estr, *msg;
 706         uint_t msglen;
 707         int status;
 708 
 709         /* Account for received and processed messages */
 710         dsmp->dsm_received++;
 711 
 712         /* We don't yet support Reconfigure at all. */
 713         if (recv_type == DHCPV6_MSG_RECONFIGURE) {
 714                 dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure "
 715                     "on %s", dsmp->dsm_name);
 716                 free_pkt_entry(plp);
 717                 return;
 718         }
 719 
 720         /*
 721          * All valid DHCPv6 messages must have our Client ID specified.
 722          */
 723         d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen);
 724         olen -= sizeof (*d6o);
 725         if (d6o == NULL || olen != dsmp->dsm_cidlen ||
 726             memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) {
 727                 dhcpmsg(MSG_VERBOSE,
 728                     "accept_v6_message: discarded %s on %s: %s Client ID",
 729                     pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong");
 730                 free_pkt_entry(plp);
 731                 return;
 732         }
 733 
 734         /*
 735          * All valid DHCPv6 messages must have a Server ID specified.
 736          *
 737          * If this is a Reply and it's not in response to Solicit, Confirm,
 738          * Rebind, or Information-Request, then it must also match the Server
 739          * ID we're expecting.
 740          *
 741          * For Reply in the Solicit, Confirm, Rebind, and Information-Request
 742          * cases, the Server ID needs to be saved.  This is done inside of
 743          * dhcp_bound().
 744          */
 745         d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen);
 746         if (d6o == NULL) {
 747                 dhcpmsg(MSG_DEBUG,
 748                     "accept_v6_message: discarded %s on %s: no Server ID",
 749                     pname, dsmp->dsm_name);
 750                 free_pkt_entry(plp);
 751                 return;
 752         }
 753         if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING &&
 754             dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING &&
 755             dsmp->dsm_state != INFORM_SENT) {
 756                 olen -= sizeof (*d6o);
 757                 if (olen != dsmp->dsm_serveridlen ||
 758                     memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) {
 759                         dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on "
 760                             "%s: wrong Server ID", pname, dsmp->dsm_name);
 761                         free_pkt_entry(plp);
 762                         return;
 763                 }
 764         }
 765 
 766         /*
 767          * Break out of the switch if the input message needs to be discarded.
 768          * Return from the function if the message has been enqueued or
 769          * consumed.
 770          */
 771         switch (dsmp->dsm_state) {
 772         case SELECTING:
 773                 /* A Reply message signifies a Rapid-Commit. */
 774                 if (recv_type == DHCPV6_MSG_REPLY) {
 775                         if (dhcpv6_pkt_option(plp, NULL,
 776                             DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) {
 777                                 dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply "
 778                                     "on %s lacks Rapid-Commit; ignoring",
 779                                     dsmp->dsm_name);
 780                                 break;
 781                         }
 782                         dhcpmsg(MSG_VERBOSE,
 783                             "accept_v6_message: rapid-commit Reply on %s",
 784                             dsmp->dsm_name);
 785                         cancel_offer_timer(dsmp);
 786                         goto rapid_commit;
 787                 }
 788 
 789                 /* Otherwise, we're looking for Advertisements. */
 790                 if (recv_type != DHCPV6_MSG_ADVERTISE)
 791                         break;
 792 
 793                 /*
 794                  * Special case: if this advertisement has preference 255, then
 795                  * we must stop right now and select this server.
 796                  */
 797                 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE,
 798                     &olen);
 799                 if (d6o != NULL && olen == sizeof (*d6o) + 1 &&
 800                     *(const uchar_t *)(d6o + 1) == 255) {
 801                         pkt_smach_enqueue(dsmp, plp);
 802                         dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;"
 803                             " immediate Request on %s", dsmp->dsm_name);
 804                         dhcp_requesting(NULL, dsmp);
 805                 } else {
 806                         pkt_smach_enqueue(dsmp, plp);
 807                 }
 808                 return;
 809 
 810         case PRE_BOUND:
 811         case BOUND:
 812                 /*
 813                  * Not looking for anything in these states.  (If we
 814                  * implemented reconfigure, that might go here.)
 815                  */
 816                 break;
 817 
 818         case REQUESTING:
 819         case INIT_REBOOT:
 820         case RENEWING:
 821         case REBINDING:
 822         case INFORM_SENT:
 823                 /*
 824                  * We're looking for Reply messages.
 825                  */
 826                 if (recv_type != DHCPV6_MSG_REPLY)
 827                         break;
 828                 dhcpmsg(MSG_VERBOSE,
 829                     "accept_v6_message: received Reply message on %s",
 830                     dsmp->dsm_name);
 831         rapid_commit:
 832                 /*
 833                  * Extract the status code option.  If one is present and the
 834                  * request failed, then try to go to another advertisement in
 835                  * the list or restart the selection machinery.
 836                  */
 837                 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
 838                     &olen);
 839                 status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
 840                 /*
 841                  * Check for the UseMulticast status code.  If this is present,
 842                  * and if we were actually using unicast, then drop back and
 843                  * try again.  If we weren't using unicast, then just pretend
 844                  * we never saw this message -- the peer is confused.  (TAHI
 845                  * does this.)
 846                  */
 847                 if (status == DHCPV6_STAT_USEMCAST) {
 848                         if (IN6_IS_ADDR_MULTICAST(
 849                             &dsmp->dsm_send_dest.v6.sin6_addr)) {
 850                                 break;
 851                         } else {
 852                                 free_pkt_entry(plp);
 853                                 dsmp->dsm_send_dest.v6.sin6_addr =
 854                                     ipv6_all_dhcp_relay_and_servers;
 855                                 retransmit_now(dsmp);
 856                                 return;
 857                         }
 858                 }
 859                 print_server_msg(dsmp, msg, msglen);
 860                 /*
 861                  * We treat NoBinding at the top level as "success."  Granted,
 862                  * this doesn't make much sense, but the TAHI test suite does
 863                  * this.  NoBinding really only makes sense in the context of a
 864                  * specific IA, as it refers to the GUID:IAID binding, so
 865                  * ignoring it at the top level is safe.
 866                  */
 867                 if (status == DHCPV6_STAT_SUCCESS ||
 868                     status == DHCPV6_STAT_NOBINDING) {
 869                         if (dhcp_bound(dsmp, plp)) {
 870                                 /*
 871                                  * dhcp_bound will stop retransmission on
 872                                  * success, if that's called for.
 873                                  */
 874                                 server_unicast_option(dsmp, plp);
 875                         } else {
 876                                 stop_pkt_retransmission(dsmp);
 877                                 dhcpmsg(MSG_WARNING, "accept_v6_message: "
 878                                     "dhcp_bound failed for %s", dsmp->dsm_name);
 879                                 (void) remove_hostconf(dsmp->dsm_name,
 880                                     dsmp->dsm_isv6);
 881                                 dhcp_restart(dsmp);
 882                         }
 883                 } else {
 884                         dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
 885                             estr);
 886                         stop_pkt_retransmission(dsmp);
 887                         free_pkt_entry(plp);
 888                         if (dsmp->dsm_state == INFORM_SENT) {
 889                                 (void) set_smach_state(dsmp, INIT);
 890                                 ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED);
 891                         } else {
 892                                 (void) remove_hostconf(dsmp->dsm_name,
 893                                     dsmp->dsm_isv6);
 894                                 request_failed(dsmp);
 895                         }
 896                 }
 897                 return;
 898 
 899         case DECLINING:
 900                 /*
 901                  * We're looking for Reply messages.
 902                  */
 903                 if (recv_type != DHCPV6_MSG_REPLY)
 904                         break;
 905                 stop_pkt_retransmission(dsmp);
 906                 /*
 907                  * Extract the status code option.  Note that it's not a
 908                  * failure if the server reports an error.
 909                  */
 910                 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
 911                     &olen);
 912                 if (dhcpv6_status_code(d6o, olen, &estr, &msg,
 913                     &msglen) == DHCPV6_STAT_SUCCESS) {
 914                         print_server_msg(dsmp, msg, msglen);
 915                 } else {
 916                         dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
 917                             estr);
 918                 }
 919                 free_pkt_entry(plp);
 920                 if (dsmp->dsm_leases == NULL) {
 921                         dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no "
 922                             "leases left", dsmp->dsm_name);
 923                         dhcp_restart(dsmp);
 924                 } else if (dsmp->dsm_lif_wait == 0) {
 925                         (void) set_smach_state(dsmp, BOUND);
 926                 } else {
 927                         (void) set_smach_state(dsmp, PRE_BOUND);
 928                 }
 929                 return;
 930 
 931         case RELEASING:
 932                 /*
 933                  * We're looking for Reply messages.
 934                  */
 935                 if (recv_type != DHCPV6_MSG_REPLY)
 936                         break;
 937                 stop_pkt_retransmission(dsmp);
 938                 /*
 939                  * Extract the status code option.
 940                  */
 941                 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
 942                     &olen);
 943                 if (dhcpv6_status_code(d6o, olen, &estr, &msg,
 944                     &msglen) == DHCPV6_STAT_SUCCESS) {
 945                         print_server_msg(dsmp, msg, msglen);
 946                 } else {
 947                         dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
 948                             estr);
 949                 }
 950                 free_pkt_entry(plp);
 951                 finished_smach(dsmp, DHCP_IPC_SUCCESS);
 952                 return;
 953         }
 954 
 955         /*
 956          * Break from above switch means that the message must be discarded.
 957          */
 958         dhcpmsg(MSG_VERBOSE,
 959             "accept_v6_message: discarded v6 %s on %s; state %s",
 960             pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
 961         free_pkt_entry(plp);
 962 }
 963 
 964 /*
 965  * dhcp_acknak_global(): Processes reception of an ACK or NAK packet on the
 966  *                       global socket -- broadcast packets for IPv4, all
 967  *                       packets for DHCPv6.
 968  *
 969  *   input: iu_eh_t *: unused
 970  *          int: the global file descriptor the ACK/NAK arrived on
 971  *          short: unused
 972  *          iu_event_id_t: unused
 973  *          void *: unused
 974  *  output: void
 975  */
 976 
 977 /* ARGSUSED */
 978 void
 979 dhcp_acknak_global(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
 980     void *arg)
 981 {
 982         PKT_LIST        *plp;
 983         dhcp_pif_t      *pif;
 984         uchar_t         recv_type;
 985         const char      *pname;
 986         uint_t          xid;
 987         dhcp_smach_t    *dsmp;
 988         boolean_t       isv6 = (fd == v6_sock_fd);
 989         struct sockaddr_in sin;
 990         const char      *reason;
 991         size_t          sinlen = sizeof (sin);
 992         int             sock;
 993 
 994         plp = recv_pkt(fd, get_max_mtu(isv6), isv6);
 995         if (plp == NULL)
 996                 return;
 997 
 998         recv_type = pkt_recv_type(plp);
 999         pname = pkt_type_to_string(recv_type, isv6);
1000 
1001         /*
1002          * Find the corresponding state machine and pif.
1003          *
1004          * Note that DHCPv6 Reconfigure would be special: it's not the reply to
1005          * any transaction, and thus we would need to search on transaction ID
1006          * zero (all state machines) to find the match.  However, Reconfigure
1007          * is not yet supported.
1008          */
1009         xid = pkt_get_xid(plp->pkt, isv6);
1010 
1011         for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL;
1012             dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) {
1013                 pif = dsmp->dsm_lif->lif_pif;
1014                 if (pif->pif_index == plp->ifindex ||
1015                     pif->pif_under_ipmp && pif->pif_grindex == plp->ifindex)
1016                         break;
1017         }
1018 
1019         if (dsmp == NULL) {
1020                 dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet"
1021                     " on ifindex %d: unknown state machine", isv6 ? 6 : 4,
1022                     pname, plp->ifindex);
1023                 free_pkt_entry(plp);
1024                 return;
1025         }
1026 
1027         if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
1028                 reason = "not ACK or NAK";
1029                 goto drop;
1030         }
1031 
1032         /*
1033          * For IPv4, most packets will be handled by dhcp_packet_lif().  The
1034          * only exceptions are broadcast packets sent when lif_sock_ip_fd has
1035          * bound to something other than INADDR_ANY.
1036          */
1037         if (!isv6) {
1038                 sock = dsmp->dsm_lif->lif_sock_ip_fd;
1039 
1040                 if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != -1 &&
1041                     sin.sin_addr.s_addr == INADDR_ANY) {
1042                         reason = "handled by lif_sock_ip_fd";
1043                         goto drop;
1044                 }
1045         }
1046 
1047         /*
1048          * We've got a packet; make sure it's acceptable and cancel the REQUEST
1049          * retransmissions.
1050          */
1051         if (isv6)
1052                 accept_v6_message(dsmp, plp, pname, recv_type);
1053         else
1054                 accept_v4_acknak(dsmp, plp);
1055         return;
1056 drop:
1057         dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet for %s "
1058             "received on global socket: %s", isv6 ? 6 : 4, pname, pif->pif_name,
1059             reason);
1060         free_pkt_entry(plp);
1061 }
1062 
1063 /*
1064  * request_failed(): Attempt to request an address has failed.  Take an
1065  *                   appropriate action.
1066  *
1067  *   input: dhcp_smach_t *: state machine that has failed
1068  *  output: void
1069  */
1070 
1071 static void
1072 request_failed(dhcp_smach_t *dsmp)
1073 {
1074         PKT_LIST *offer;
1075 
1076         dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
1077         if ((offer = select_best(dsmp)) != NULL) {
1078                 insque(offer, &dsmp->dsm_recv_pkt_list);
1079                 dhcp_requesting(NULL, dsmp);
1080         } else {
1081                 dhcpmsg(MSG_INFO, "no offers left on %s; restarting",
1082                     dsmp->dsm_name);
1083                 dhcp_selecting(dsmp);
1084         }
1085 }
1086 
1087 /*
1088  * dhcp_packet_lif(): Processes reception of an ACK, NAK, or OFFER packet on
1089  *                    a given logical interface for IPv4 (only).
1090  *
1091  *   input: iu_eh_t *: unused
1092  *          int: the file descriptor the packet arrived on
1093  *          short: unused
1094  *          iu_event_id_t: the id of this event callback with the handler
1095  *          void *: pointer to logical interface receiving message
1096  *  output: void
1097  */
1098 
1099 /* ARGSUSED */
1100 void
1101 dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
1102     void *arg)
1103 {
1104         dhcp_lif_t      *lif = arg;
1105         PKT_LIST        *plp;
1106         uchar_t         recv_type;
1107         const char      *pname;
1108         uint_t          xid;
1109         dhcp_smach_t    *dsmp;
1110 
1111         if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL)
1112                 return;
1113 
1114         recv_type = pkt_recv_type(plp);
1115         pname = pkt_type_to_string(recv_type, B_FALSE);
1116 
1117         if (!pkt_v4_match(recv_type,
1118             DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) {
1119                 dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet "
1120                     "received via LIF %s", pname, lif->lif_name);
1121                 free_pkt_entry(plp);
1122                 return;
1123         }
1124 
1125         /*
1126          * Find the corresponding state machine.
1127          */
1128         xid = pkt_get_xid(plp->pkt, B_FALSE);
1129         for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
1130             dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
1131                 if (dsmp->dsm_lif == lif)
1132                         break;
1133         }
1134 
1135         if (dsmp == NULL)
1136                 goto drop;
1137 
1138         if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
1139                 /*
1140                  * We've got an ACK/NAK; make sure it's acceptable and cancel
1141                  * the REQUEST retransmissions.
1142                  */
1143                 accept_v4_acknak(dsmp, plp);
1144         } else {
1145                 if (is_bound_state(dsmp->dsm_state))
1146                         goto drop;
1147                 /*
1148                  * Must be an OFFER or a BOOTP message: enqueue it for later
1149                  * processing by select_best().
1150                  */
1151                 pkt_smach_enqueue(dsmp, plp);
1152         }
1153         return;
1154 drop:
1155         dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid "
1156             "%x received via LIF %s; %s", pname, xid, lif->lif_name,
1157             dsmp == NULL ? "unknown state machine" : "bound");
1158         free_pkt_entry(plp);
1159 }
1160 
1161 /*
1162  * dhcp_restart(): restarts DHCP (from INIT) on a given state machine, but only
1163  *                 if we're leasing addresses.  Doesn't restart for information-
1164  *                 only interfaces.
1165  *
1166  *   input: dhcp_smach_t *: the state machine to restart DHCP on
1167  *  output: void
1168  */
1169 
1170 void
1171 dhcp_restart(dhcp_smach_t *dsmp)
1172 {
1173         if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION)
1174                 return;
1175 
1176         /*
1177          * As we're returning to INIT state, we need to discard any leases we
1178          * may have, and (for v4) canonize the LIF.  There's a bit of tension
1179          * between keeping around a possibly still working address, and obeying
1180          * the RFCs.  A more elaborate design would be to mark the addresses as
1181          * DEPRECATED, and then start a removal timer.  Such a design would
1182          * probably compromise testing.
1183          */
1184         deprecate_leases(dsmp);
1185 
1186         if (!set_start_timer(dsmp)) {
1187                 dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
1188                     "reverting to INIT state on %s", dsmp->dsm_name);
1189 
1190                 (void) set_smach_state(dsmp, INIT);
1191                 dsmp->dsm_dflags |= DHCP_IF_FAILED;
1192                 ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
1193         } else {
1194                 dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s",
1195                     dsmp->dsm_name);
1196         }
1197 }
1198 
1199 /*
1200  * stop_requesting(): decides when to stop retransmitting REQUESTs
1201  *
1202  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from
1203  *          unsigned int: the number of REQUESTs sent so far
1204  *  output: boolean_t: B_TRUE if retransmissions should stop
1205  */
1206 
1207 static boolean_t
1208 stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests)
1209 {
1210         uint_t maxreq;
1211 
1212         maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS;
1213         if (n_requests >= maxreq) {
1214 
1215                 dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s",
1216                     dsmp->dsm_name);
1217 
1218                 request_failed(dsmp);
1219                 return (B_TRUE);
1220         } else {
1221                 return (B_FALSE);
1222         }
1223 }