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