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