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