xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c (revision 5123:b07e8361d166)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53431Scarlsonj  * Common Development and Distribution License (the "License").
63431Scarlsonj  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
223431Scarlsonj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <string.h>
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <stdlib.h>
310Sstevel@tonic-gate #include <dhcpmsg.h>
320Sstevel@tonic-gate #include <stddef.h>
330Sstevel@tonic-gate #include <assert.h>
343431Scarlsonj #include <search.h>
353431Scarlsonj #include <alloca.h>
363431Scarlsonj #include <limits.h>
373431Scarlsonj #include <stropts.h>
383431Scarlsonj #include <netinet/dhcp6.h>
393431Scarlsonj #include <arpa/inet.h>
403431Scarlsonj #include <sys/sysmacros.h>
413431Scarlsonj #include <sys/sockio.h>
423431Scarlsonj #include <inet/ip6_asp.h>
430Sstevel@tonic-gate 
440Sstevel@tonic-gate #include "states.h"
450Sstevel@tonic-gate #include "interface.h"
460Sstevel@tonic-gate #include "agent.h"
470Sstevel@tonic-gate #include "packet.h"
480Sstevel@tonic-gate #include "util.h"
493431Scarlsonj #include "dlpi_io.h"
500Sstevel@tonic-gate 
513431Scarlsonj int v6_sock_fd = -1;
523431Scarlsonj int v4_sock_fd = -1;
533431Scarlsonj 
543431Scarlsonj const in6_addr_t ipv6_all_dhcp_relay_and_servers = {
553431Scarlsonj 	0xff, 0x02, 0x00, 0x00,
563431Scarlsonj 	0x00, 0x00, 0x00, 0x00,
573431Scarlsonj 	0x00, 0x00, 0x00, 0x00,
583431Scarlsonj 	0x00, 0x01, 0x00, 0x02
593431Scarlsonj };
600Sstevel@tonic-gate 
610Sstevel@tonic-gate /*
623431Scarlsonj  * We have our own version of this constant because dhcpagent is compiled with
633431Scarlsonj  * -lxnet.
640Sstevel@tonic-gate  */
653431Scarlsonj const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT;
660Sstevel@tonic-gate 
673431Scarlsonj static void 	retransmit(iu_tq_t *, void *);
683431Scarlsonj static void	next_retransmission(dhcp_smach_t *, boolean_t, boolean_t);
693431Scarlsonj static boolean_t send_pkt_internal(dhcp_smach_t *);
700Sstevel@tonic-gate 
710Sstevel@tonic-gate /*
723431Scarlsonj  * pkt_send_type(): returns an integer representing the packet's type; only
733431Scarlsonj  *		    for use with outbound packets.
740Sstevel@tonic-gate  *
753431Scarlsonj  *   input: dhcp_pkt_t *: the packet to examine
760Sstevel@tonic-gate  *  output: uchar_t: the packet type (0 if unknown)
770Sstevel@tonic-gate  */
780Sstevel@tonic-gate 
790Sstevel@tonic-gate static uchar_t
803431Scarlsonj pkt_send_type(const dhcp_pkt_t *dpkt)
810Sstevel@tonic-gate {
823431Scarlsonj 	const uchar_t *option;
833431Scarlsonj 
843431Scarlsonj 	if (dpkt->pkt_isv6)
853431Scarlsonj 		return (((const dhcpv6_message_t *)dpkt->pkt)->d6m_msg_type);
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 	/*
880Sstevel@tonic-gate 	 * this is a little dirty but it should get the job done.
890Sstevel@tonic-gate 	 * assumes that the type is in the statically allocated part
900Sstevel@tonic-gate 	 * of the options field.
910Sstevel@tonic-gate 	 */
920Sstevel@tonic-gate 
933431Scarlsonj 	option = dpkt->pkt->options;
943431Scarlsonj 	for (;;) {
953431Scarlsonj 		if (*option == CD_PAD) {
963431Scarlsonj 			option++;
973431Scarlsonj 			continue;
983431Scarlsonj 		}
993431Scarlsonj 		if (*option == CD_END ||
1003431Scarlsonj 		    option + 2 - dpkt->pkt->options >=
1013431Scarlsonj 		    sizeof (dpkt->pkt->options))
1020Sstevel@tonic-gate 			return (0);
1033431Scarlsonj 		if (*option == CD_DHCP_TYPE)
1043431Scarlsonj 			break;
1050Sstevel@tonic-gate 		option++;
1063431Scarlsonj 		option += *option + 1;
1070Sstevel@tonic-gate 	}
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	return (option[2]);
1100Sstevel@tonic-gate }
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate /*
1133431Scarlsonj  * pkt_recv_type(): returns an integer representing the packet's type; only
1143431Scarlsonj  *		    for use with inbound packets.
1153431Scarlsonj  *
1163431Scarlsonj  *   input: dhcp_pkt_t *: the packet to examine
1173431Scarlsonj  *  output: uchar_t: the packet type (0 if unknown)
1183431Scarlsonj  */
1193431Scarlsonj 
1203431Scarlsonj uchar_t
1213431Scarlsonj pkt_recv_type(const PKT_LIST *plp)
1223431Scarlsonj {
1233431Scarlsonj 	if (plp->isv6)
1243431Scarlsonj 		return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type);
1253431Scarlsonj 	else if (plp->opts[CD_DHCP_TYPE] != NULL)
1263431Scarlsonj 		return (plp->opts[CD_DHCP_TYPE]->value[0]);
1273431Scarlsonj 	else
1283431Scarlsonj 		return (0);
1293431Scarlsonj }
1303431Scarlsonj 
1313431Scarlsonj /*
1323431Scarlsonj  * pkt_get_xid(): returns transaction ID from a DHCP packet.
1333431Scarlsonj  *
1343431Scarlsonj  *   input: const PKT *: the packet to examine
1353431Scarlsonj  *  output: uint_t: the transaction ID (0 if unknown)
1363431Scarlsonj  */
1373431Scarlsonj 
1383431Scarlsonj uint_t
1393431Scarlsonj pkt_get_xid(const PKT *pkt, boolean_t isv6)
1403431Scarlsonj {
1413431Scarlsonj 	if (pkt == NULL)
1423431Scarlsonj 		return (0);
1433431Scarlsonj 	if (isv6)
1443431Scarlsonj 		return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt));
1453431Scarlsonj 	else
1463431Scarlsonj 		return (pkt->xid);
1473431Scarlsonj }
1483431Scarlsonj 
1493431Scarlsonj /*
1500Sstevel@tonic-gate  * init_pkt(): initializes and returns a packet of a given type
1510Sstevel@tonic-gate  *
1523431Scarlsonj  *   input: dhcp_smach_t *: the state machine that will send the packet
1530Sstevel@tonic-gate  *	    uchar_t: the packet type (DHCP message type)
1543431Scarlsonj  *  output: dhcp_pkt_t *: a pointer to the initialized packet; may be NULL
1550Sstevel@tonic-gate  */
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate dhcp_pkt_t *
1583431Scarlsonj init_pkt(dhcp_smach_t *dsmp, uchar_t type)
1590Sstevel@tonic-gate {
1603431Scarlsonj 	uint_t		mtu;
1613431Scarlsonj 	dhcp_pkt_t	*dpkt = &dsmp->dsm_send_pkt;
1623431Scarlsonj 	dhcp_lif_t	*lif = dsmp->dsm_lif;
1633431Scarlsonj 	dhcp_pif_t	*pif = lif->lif_pif;
1640Sstevel@tonic-gate 	uint32_t	xid;
1653431Scarlsonj 	boolean_t	isv6;
1660Sstevel@tonic-gate 
1673431Scarlsonj 	mtu = dsmp->dsm_using_dlpi ? pif->pif_max : lif->lif_max;
1683431Scarlsonj 	dpkt->pkt_isv6 = isv6 = pif->pif_isv6;
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	/*
1710Sstevel@tonic-gate 	 * since multiple dhcp leases may be maintained over the same dlpi
1720Sstevel@tonic-gate 	 * device (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
1733431Scarlsonj 	 *
1743431Scarlsonj 	 * Note that transaction ID zero is intentionally never assigned.
1753431Scarlsonj 	 * That's used to represent "no ID."  Also note that transaction IDs
1763431Scarlsonj 	 * are only 24 bits long in DHCPv6.
1770Sstevel@tonic-gate 	 */
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	do {
1800Sstevel@tonic-gate 		xid = mrand48();
1813431Scarlsonj 		if (isv6)
1823431Scarlsonj 			xid &= 0xFFFFFF;
1833431Scarlsonj 	} while (xid == 0 ||
1843431Scarlsonj 	    lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL);
1853431Scarlsonj 
1863431Scarlsonj 	if (isv6) {
1873431Scarlsonj 		dhcpv6_message_t *v6;
1883431Scarlsonj 
1893431Scarlsonj 		if (mtu != dpkt->pkt_max_len &&
1903431Scarlsonj 		    (v6 = realloc(dpkt->pkt, mtu)) != NULL) {
1913431Scarlsonj 			/* LINTED: alignment known to be correct */
1923431Scarlsonj 			dpkt->pkt = (PKT *)v6;
1933431Scarlsonj 			dpkt->pkt_max_len = mtu;
1943431Scarlsonj 		}
1953431Scarlsonj 
1963431Scarlsonj 		if (sizeof (*v6) > dpkt->pkt_max_len) {
1973431Scarlsonj 			dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u",
1983431Scarlsonj 			    mtu);
1993431Scarlsonj 			return (NULL);
2003431Scarlsonj 		}
2013431Scarlsonj 
2023431Scarlsonj 		v6 = (dhcpv6_message_t *)dpkt->pkt;
2033431Scarlsonj 		dpkt->pkt_cur_len = sizeof (*v6);
2043431Scarlsonj 
2053431Scarlsonj 		(void) memset(v6, 0, dpkt->pkt_max_len);
2063431Scarlsonj 
2073431Scarlsonj 		v6->d6m_msg_type = type;
2083431Scarlsonj 		DHCPV6_SET_TRANSID(v6, xid);
2093431Scarlsonj 
2103431Scarlsonj 		if (dsmp->dsm_cidlen > 0 &&
2113431Scarlsonj 		    add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid,
2123431Scarlsonj 		    dsmp->dsm_cidlen) == NULL) {
2133431Scarlsonj 			dhcpmsg(MSG_WARNING,
2143431Scarlsonj 			    "init_pkt: cannot insert client ID");
2153431Scarlsonj 			return (NULL);
2163431Scarlsonj 		}
2173431Scarlsonj 
2183431Scarlsonj 		/* For v6, time starts with the creation of a transaction */
2193431Scarlsonj 		dsmp->dsm_neg_hrtime = gethrtime();
2203431Scarlsonj 		dsmp->dsm_newstart_monosec = monosec();
2213431Scarlsonj 	} else {
2223431Scarlsonj 		static uint8_t bootmagic[] = BOOTMAGIC;
2233431Scarlsonj 		PKT *v4;
2243431Scarlsonj 
2253431Scarlsonj 		if (mtu != dpkt->pkt_max_len &&
2263431Scarlsonj 		    (v4 = realloc(dpkt->pkt, mtu)) != NULL) {
2273431Scarlsonj 			dpkt->pkt = v4;
2283431Scarlsonj 			dpkt->pkt_max_len = mtu;
2293431Scarlsonj 		}
2300Sstevel@tonic-gate 
2313431Scarlsonj 		if (offsetof(PKT, options) > dpkt->pkt_max_len) {
2323431Scarlsonj 			dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u",
2333431Scarlsonj 			    mtu);
2343431Scarlsonj 			return (NULL);
2353431Scarlsonj 		}
2363431Scarlsonj 
2373431Scarlsonj 		v4 = dpkt->pkt;
2383431Scarlsonj 		dpkt->pkt_cur_len = offsetof(PKT, options);
2390Sstevel@tonic-gate 
2403431Scarlsonj 		(void) memset(v4, 0, dpkt->pkt_max_len);
2413431Scarlsonj 		(void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic));
2423431Scarlsonj 		if (pif->pif_hwlen <= sizeof (v4->chaddr)) {
2433431Scarlsonj 			v4->hlen  = pif->pif_hwlen;
2443431Scarlsonj 			(void) memcpy(v4->chaddr, pif->pif_hwaddr,
2453431Scarlsonj 			    pif->pif_hwlen);
2463431Scarlsonj 		} else {
2473431Scarlsonj 			/*
2483431Scarlsonj 			 * The mac address does not fit in the chaddr
2493431Scarlsonj 			 * field, thus it can not be sent to the server,
2503431Scarlsonj 			 * thus server can not unicast the reply. Per
2513431Scarlsonj 			 * RFC 2131 4.4.1, client can set this bit in
2523431Scarlsonj 			 * DISCOVER/REQUEST. If the client is already
2533431Scarlsonj 			 * in BOUND/REBINDING/RENEWING state, do not set
2543431Scarlsonj 			 * this bit, as it can respond to unicast responses
2553431Scarlsonj 			 * from server using the 'ciaddr' address.
2563431Scarlsonj 			 */
2573431Scarlsonj 			if (type == DISCOVER ||
2583431Scarlsonj 			    (type == REQUEST && dsmp->dsm_state != RENEWING &&
2593431Scarlsonj 			    dsmp->dsm_state != REBINDING &&
2603431Scarlsonj 			    dsmp->dsm_state != BOUND))
2613431Scarlsonj 				v4->flags = htons(BCAST_MASK);
2623431Scarlsonj 		}
2633431Scarlsonj 
2643431Scarlsonj 		v4->xid   = xid;
2653431Scarlsonj 		v4->op    = BOOTREQUEST;
2663431Scarlsonj 		v4->htype = pif->pif_hwtype;
2673431Scarlsonj 
2683431Scarlsonj 		if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) {
2693431Scarlsonj 			dhcpmsg(MSG_WARNING,
2703431Scarlsonj 			    "init_pkt: cannot set DHCP packet type");
2713431Scarlsonj 			return (NULL);
2723431Scarlsonj 		}
2733431Scarlsonj 
2743431Scarlsonj 		if (dsmp->dsm_cidlen > 0 &&
2753431Scarlsonj 		    add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid,
2763431Scarlsonj 		    dsmp->dsm_cidlen) == NULL) {
2773431Scarlsonj 			dhcpmsg(MSG_WARNING,
2783431Scarlsonj 			    "init_pkt: cannot insert client ID");
2793431Scarlsonj 			return (NULL);
2803431Scarlsonj 		}
2813431Scarlsonj 	}
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	return (dpkt);
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate /*
2873431Scarlsonj  * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t
2883431Scarlsonj  *
2893431Scarlsonj  *   input: dhcp_pkt_t *: the packet to remove the option from
2903431Scarlsonj  *	    uint_t: the type of option being added
2913431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
2923431Scarlsonj  *    note: currently does not work with DHCPv6 suboptions, or to remove
2933431Scarlsonj  *	    arbitrary option instances.
2943431Scarlsonj  */
2953431Scarlsonj 
2963431Scarlsonj boolean_t
2973431Scarlsonj remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type)
2983431Scarlsonj {
2993431Scarlsonj 	uchar_t		*raw_pkt, *raw_end, *next;
3003431Scarlsonj 	uint_t		len;
3013431Scarlsonj 
3023431Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
3033431Scarlsonj 	raw_end = raw_pkt + dpkt->pkt_cur_len;
3043431Scarlsonj 	if (dpkt->pkt_isv6) {
3053431Scarlsonj 		dhcpv6_option_t d6o;
3063431Scarlsonj 
3073431Scarlsonj 		raw_pkt += sizeof (dhcpv6_message_t);
3083431Scarlsonj 
3093431Scarlsonj 		opt_type = htons(opt_type);
3103431Scarlsonj 		while (raw_pkt + sizeof (d6o) <= raw_end) {
3113431Scarlsonj 			(void) memcpy(&d6o, raw_pkt, sizeof (d6o));
3123431Scarlsonj 			len = ntohs(d6o.d6o_len) + sizeof (d6o);
3133431Scarlsonj 			if (len > raw_end - raw_pkt)
3143431Scarlsonj 				break;
3153431Scarlsonj 			next = raw_pkt + len;
3163431Scarlsonj 			if (d6o.d6o_code == opt_type) {
3173431Scarlsonj 				if (next < raw_end) {
3183431Scarlsonj 					(void) memmove(raw_pkt, next,
3193431Scarlsonj 					    raw_end - next);
3203431Scarlsonj 				}
3213431Scarlsonj 				dpkt->pkt_cur_len -= len;
3223431Scarlsonj 				return (B_TRUE);
3233431Scarlsonj 			}
3243431Scarlsonj 			raw_pkt = next;
3253431Scarlsonj 		}
3263431Scarlsonj 	} else {
3273431Scarlsonj 		uchar_t *pstart, *padrun;
3283431Scarlsonj 
3293431Scarlsonj 		raw_pkt += offsetof(PKT, options);
3303431Scarlsonj 		pstart = raw_pkt;
3313431Scarlsonj 
3323431Scarlsonj 		if (opt_type == CD_END || opt_type == CD_PAD)
3333431Scarlsonj 			return (B_FALSE);
3343431Scarlsonj 
3353431Scarlsonj 		padrun = NULL;
3363431Scarlsonj 		while (raw_pkt + 1 <= raw_end) {
3373431Scarlsonj 			if (*raw_pkt == CD_END)
3383431Scarlsonj 				break;
3393431Scarlsonj 			if (*raw_pkt == CD_PAD) {
3403431Scarlsonj 				if (padrun == NULL)
3413431Scarlsonj 					padrun = raw_pkt;
3423431Scarlsonj 				raw_pkt++;
3433431Scarlsonj 				continue;
3443431Scarlsonj 			}
3453431Scarlsonj 			if (raw_pkt + 2 > raw_end)
3463431Scarlsonj 				break;
3473431Scarlsonj 			len = raw_pkt[1];
3483431Scarlsonj 			if (len > raw_end - raw_pkt || len < 2)
3493431Scarlsonj 				break;
3503431Scarlsonj 			next = raw_pkt + len;
3513431Scarlsonj 			if (*raw_pkt == opt_type) {
3523431Scarlsonj 				if (next < raw_end) {
3533431Scarlsonj 					int toadd = (4 + ((next-pstart)&3) -
3543431Scarlsonj 					    ((raw_pkt-pstart)&3)) & 3;
3553431Scarlsonj 					int torem = 4 - toadd;
3563431Scarlsonj 
3573431Scarlsonj 					if (torem != 4 && padrun != NULL &&
3583431Scarlsonj 					    (raw_pkt - padrun) >= torem) {
3593431Scarlsonj 						raw_pkt -= torem;
3603431Scarlsonj 						dpkt->pkt_cur_len -= torem;
3613431Scarlsonj 					} else if (toadd > 0) {
3623431Scarlsonj 						(void) memset(raw_pkt, CD_PAD,
3633431Scarlsonj 						    toadd);
3643431Scarlsonj 						raw_pkt += toadd;
3653431Scarlsonj 						/* max is not an issue here */
3663431Scarlsonj 						dpkt->pkt_cur_len += toadd;
3673431Scarlsonj 					}
3683431Scarlsonj 					if (raw_pkt != next) {
3693431Scarlsonj 						(void) memmove(raw_pkt, next,
3703431Scarlsonj 						    raw_end - next);
3713431Scarlsonj 					}
3723431Scarlsonj 				}
3733431Scarlsonj 				dpkt->pkt_cur_len -= len;
3743431Scarlsonj 				return (B_TRUE);
3753431Scarlsonj 			}
3763431Scarlsonj 			padrun = NULL;
3773431Scarlsonj 			raw_pkt = next;
3783431Scarlsonj 		}
3793431Scarlsonj 	}
3803431Scarlsonj 	return (B_FALSE);
3813431Scarlsonj }
3823431Scarlsonj 
3833431Scarlsonj /*
3843431Scarlsonj  * update_v6opt_len(): updates the length field of a DHCPv6 option.
3853431Scarlsonj  *
3863431Scarlsonj  *   input: dhcpv6_option_t *: option to be updated
3873431Scarlsonj  *	    int: number of octets to add or subtract
3883431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
3893431Scarlsonj  */
3903431Scarlsonj 
3913431Scarlsonj boolean_t
3923431Scarlsonj update_v6opt_len(dhcpv6_option_t *opt, int adjust)
3933431Scarlsonj {
3943431Scarlsonj 	dhcpv6_option_t optval;
3953431Scarlsonj 
3963431Scarlsonj 	(void) memcpy(&optval, opt, sizeof (optval));
3973431Scarlsonj 	adjust += ntohs(optval.d6o_len);
3983431Scarlsonj 	if (adjust < 0 || adjust > UINT16_MAX) {
3993431Scarlsonj 		return (B_FALSE);
4003431Scarlsonj 	} else {
4013431Scarlsonj 		optval.d6o_len = htons(adjust);
4023431Scarlsonj 		(void) memcpy(opt, &optval, sizeof (optval));
4033431Scarlsonj 		return (B_TRUE);
4043431Scarlsonj 	}
4053431Scarlsonj }
4063431Scarlsonj 
4073431Scarlsonj /*
4080Sstevel@tonic-gate  * add_pkt_opt(): adds an option to a dhcp_pkt_t
4090Sstevel@tonic-gate  *
4100Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
4113431Scarlsonj  *	    uint_t: the type of option being added
4120Sstevel@tonic-gate  *	    const void *: the value of that option
4133431Scarlsonj  *	    uint_t: the length of the value of the option
4143431Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
4150Sstevel@tonic-gate  */
4160Sstevel@tonic-gate 
4173431Scarlsonj void *
4183431Scarlsonj add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val,
4193431Scarlsonj     uint_t opt_len)
4200Sstevel@tonic-gate {
4213431Scarlsonj 	uchar_t		*raw_pkt;
4223431Scarlsonj 	int		req_len;
4233431Scarlsonj 	void		*optr;
4243431Scarlsonj 
4253431Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
4263431Scarlsonj 	optr = raw_pkt + dpkt->pkt_cur_len;
4273431Scarlsonj 	if (dpkt->pkt_isv6) {
4283431Scarlsonj 		dhcpv6_option_t d6o;
4293431Scarlsonj 
4303431Scarlsonj 		req_len = opt_len + sizeof (d6o);
4313431Scarlsonj 
4323431Scarlsonj 		if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
4333431Scarlsonj 			dhcpmsg(MSG_WARNING,
4343431Scarlsonj 			    "add_pkt_opt: not enough room for v6 option %u in "
4353431Scarlsonj 			    "packet (%u + %u > %u)", opt_type,
4363431Scarlsonj 			    dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
4373431Scarlsonj 			return (NULL);
4383431Scarlsonj 		}
4393431Scarlsonj 		d6o.d6o_code = htons(opt_type);
4403431Scarlsonj 		d6o.d6o_len = htons(opt_len);
4413431Scarlsonj 		(void) memcpy(&raw_pkt[dpkt->pkt_cur_len], &d6o, sizeof (d6o));
4423431Scarlsonj 		dpkt->pkt_cur_len += sizeof (d6o);
4433431Scarlsonj 		if (opt_len > 0) {
4443431Scarlsonj 			(void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val,
4453431Scarlsonj 			    opt_len);
4463431Scarlsonj 			dpkt->pkt_cur_len += opt_len;
4473431Scarlsonj 		}
4483431Scarlsonj 	} else {
4493431Scarlsonj 		req_len = opt_len + 2; /* + 2 for code & length bytes */
4503431Scarlsonj 
4513431Scarlsonj 		/* CD_END and CD_PAD options don't have a length field */
4523431Scarlsonj 		if (opt_type == CD_END || opt_type == CD_PAD) {
4533431Scarlsonj 			req_len = 1;
4543431Scarlsonj 		} else if (opt_val == NULL) {
4553431Scarlsonj 			dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is "
4563431Scarlsonj 			    "missing required value", opt_type);
4573431Scarlsonj 			return (NULL);
4583431Scarlsonj 		}
4593431Scarlsonj 
4603431Scarlsonj 		if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) {
4613431Scarlsonj 			dhcpmsg(MSG_WARNING,
4623431Scarlsonj 			    "add_pkt_opt: not enough room for v4 option %u in "
4633431Scarlsonj 			    "packet", opt_type);
4643431Scarlsonj 			return (NULL);
4653431Scarlsonj 		}
4663431Scarlsonj 
4673431Scarlsonj 		raw_pkt[dpkt->pkt_cur_len++] = opt_type;
4680Sstevel@tonic-gate 
4693431Scarlsonj 		if (req_len > 1) {
4703431Scarlsonj 			raw_pkt[dpkt->pkt_cur_len++] = opt_len;
4713431Scarlsonj 			if (opt_len > 0) {
4723431Scarlsonj 				(void) memcpy(&raw_pkt[dpkt->pkt_cur_len],
4733431Scarlsonj 				    opt_val, opt_len);
4743431Scarlsonj 				dpkt->pkt_cur_len += opt_len;
4753431Scarlsonj 			}
4763431Scarlsonj 		}
4773431Scarlsonj 	}
4783431Scarlsonj 	return (optr);
4793431Scarlsonj }
4800Sstevel@tonic-gate 
4813431Scarlsonj /*
4823431Scarlsonj  * add_pkt_subopt(): adds an option to a dhcp_pkt_t option.  DHCPv6-specific,
4833431Scarlsonj  *		     but could be extended to IPv4 DHCP if necessary.  Assumes
4843431Scarlsonj  *		     that if the parent isn't a top-level option, the caller
4853431Scarlsonj  *		     will adjust any upper-level options recursively using
4863431Scarlsonj  *		     update_v6opt_len.
4873431Scarlsonj  *
4883431Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the suboption to
4893431Scarlsonj  *	    dhcpv6_option_t *: the start of the option to that should contain
4903431Scarlsonj  *			       it (parent)
4913431Scarlsonj  *	    uint_t: the type of suboption being added
4923431Scarlsonj  *	    const void *: the value of that option
4933431Scarlsonj  *	    uint_t: the length of the value of the option
4943431Scarlsonj  *  output: void *: pointer to the suboption that was added, or NULL on
4953431Scarlsonj  *		    failure.
4963431Scarlsonj  */
4973431Scarlsonj 
4983431Scarlsonj void *
4993431Scarlsonj add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type,
5003431Scarlsonj     const void *opt_val, uint_t opt_len)
5013431Scarlsonj {
5023431Scarlsonj 	uchar_t		*raw_pkt;
5033431Scarlsonj 	int		req_len;
5043431Scarlsonj 	void		*optr;
5053431Scarlsonj 	dhcpv6_option_t d6o;
5063431Scarlsonj 	uchar_t		*optend;
5073431Scarlsonj 	int		olen;
5083431Scarlsonj 
5093431Scarlsonj 	if (!dpkt->pkt_isv6)
5103431Scarlsonj 		return (NULL);
5113431Scarlsonj 
5123431Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
5133431Scarlsonj 	req_len = opt_len + sizeof (d6o);
5143431Scarlsonj 
5153431Scarlsonj 	if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
5163431Scarlsonj 		dhcpmsg(MSG_WARNING,
5173431Scarlsonj 		    "add_pkt_subopt: not enough room for v6 suboption %u in "
5183431Scarlsonj 		    "packet (%u + %u > %u)", opt_type,
5193431Scarlsonj 		    dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
5203431Scarlsonj 		return (NULL);
5210Sstevel@tonic-gate 	}
5220Sstevel@tonic-gate 
5233431Scarlsonj 	/*
5243431Scarlsonj 	 * Update the parent option to include room for this option,
5253431Scarlsonj 	 * and compute the insertion point.
5263431Scarlsonj 	 */
5273431Scarlsonj 	(void) memcpy(&d6o, parentopt, sizeof (d6o));
5283431Scarlsonj 	olen = ntohs(d6o.d6o_len);
5293431Scarlsonj 	optend = (uchar_t *)(parentopt + 1) + olen;
5303431Scarlsonj 	olen += req_len;
5313431Scarlsonj 	d6o.d6o_len = htons(olen);
5323431Scarlsonj 	(void) memcpy(parentopt, &d6o, sizeof (d6o));
5330Sstevel@tonic-gate 
5343431Scarlsonj 	/*
5353431Scarlsonj 	 * If there's anything at the end to move, then move it.  Also bump up
5363431Scarlsonj 	 * the packet size.
5373431Scarlsonj 	 */
5383431Scarlsonj 	if (optend < raw_pkt + dpkt->pkt_cur_len) {
5393431Scarlsonj 		(void) memmove(optend + req_len, optend,
5403431Scarlsonj 		    (raw_pkt + dpkt->pkt_cur_len) - optend);
5410Sstevel@tonic-gate 	}
5423431Scarlsonj 	dpkt->pkt_cur_len += req_len;
5433431Scarlsonj 
5443431Scarlsonj 	/*
5453431Scarlsonj 	 * Now format the suboption and add it in.
5463431Scarlsonj 	 */
5473431Scarlsonj 	optr = optend;
5483431Scarlsonj 	d6o.d6o_code = htons(opt_type);
5493431Scarlsonj 	d6o.d6o_len = htons(opt_len);
5503431Scarlsonj 	(void) memcpy(optend, &d6o, sizeof (d6o));
5513431Scarlsonj 	if (opt_len > 0)
5523431Scarlsonj 		(void) memcpy(optend + sizeof (d6o), opt_val, opt_len);
5533431Scarlsonj 	return (optr);
5540Sstevel@tonic-gate }
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate /*
5570Sstevel@tonic-gate  * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t
5580Sstevel@tonic-gate  *
5590Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
5603431Scarlsonj  *	    uint_t: the type of option being added
5610Sstevel@tonic-gate  *	    uint16_t: the value of that option
5623431Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
5630Sstevel@tonic-gate  */
5640Sstevel@tonic-gate 
5653431Scarlsonj void *
5663431Scarlsonj add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value)
5670Sstevel@tonic-gate {
5683431Scarlsonj 	return (add_pkt_opt(dpkt, opt_type, &opt_value, 2));
5690Sstevel@tonic-gate }
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate /*
5720Sstevel@tonic-gate  * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t
5730Sstevel@tonic-gate  *
5740Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
5753431Scarlsonj  *	    uint_t: the type of option being added
5760Sstevel@tonic-gate  *	    uint32_t: the value of that option
5773431Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
5783431Scarlsonj  */
5793431Scarlsonj 
5803431Scarlsonj void *
5813431Scarlsonj add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value)
5823431Scarlsonj {
5833431Scarlsonj 	return (add_pkt_opt(dpkt, opt_type, &opt_value, 4));
5843431Scarlsonj }
5853431Scarlsonj 
5863431Scarlsonj /*
5873431Scarlsonj  * add_pkt_prl(): adds the parameter request option to the packet
5883431Scarlsonj  *
5893431Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the option to
5903431Scarlsonj  *	    dhcp_smach_t *: state machine with request option
5913431Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
5920Sstevel@tonic-gate  */
5930Sstevel@tonic-gate 
5943431Scarlsonj void *
5953431Scarlsonj add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
5960Sstevel@tonic-gate {
5973431Scarlsonj 	uint_t len;
5983431Scarlsonj 
5993431Scarlsonj 	if (dsmp->dsm_prllen == 0)
6003431Scarlsonj 		return (0);
6013431Scarlsonj 
6023431Scarlsonj 	if (dpkt->pkt_isv6) {
6033431Scarlsonj 		uint16_t *prl;
6043431Scarlsonj 
6053431Scarlsonj 		/*
6063431Scarlsonj 		 * RFC 3315 requires that we include the option, even if we
6073431Scarlsonj 		 * have nothing to request.
6083431Scarlsonj 		 */
6093431Scarlsonj 		if (dsmp->dsm_prllen == 0)
6103431Scarlsonj 			prl = NULL;
6113431Scarlsonj 		else
6123431Scarlsonj 			prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t));
6133431Scarlsonj 
6143431Scarlsonj 		for (len = 0; len < dsmp->dsm_prllen; len++)
6153431Scarlsonj 			prl[len] = htons(dsmp->dsm_prl[len]);
6163431Scarlsonj 		return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl,
6173431Scarlsonj 		    len * sizeof (uint16_t)));
6183431Scarlsonj 	} else {
6193431Scarlsonj 		uint8_t *prl = alloca(dsmp->dsm_prllen);
6203431Scarlsonj 
6213431Scarlsonj 		for (len = 0; len < dsmp->dsm_prllen; len++)
6223431Scarlsonj 			prl[len] = dsmp->dsm_prl[len];
6233431Scarlsonj 		return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len));
6243431Scarlsonj 	}
6250Sstevel@tonic-gate }
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate /*
6283431Scarlsonj  * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR
6293431Scarlsonj  *		  (DHCPv6) options to the packet to represent the given LIF.
6300Sstevel@tonic-gate  *
6313431Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the options to
6323431Scarlsonj  *	    dhcp_lif_t *: the logical interface to represent
6333431Scarlsonj  *	    int: status code (unused for IPv4 DHCP)
6343431Scarlsonj  *	    const char *: message to include with status option, or NULL
6353431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
6360Sstevel@tonic-gate  */
6370Sstevel@tonic-gate 
6383431Scarlsonj boolean_t
6393431Scarlsonj add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg)
6400Sstevel@tonic-gate {
6413431Scarlsonj 	if (lif->lif_pif->pif_isv6) {
6423431Scarlsonj 		dhcp_smach_t *dsmp;
6433431Scarlsonj 		dhcpv6_message_t *d6m;
6443431Scarlsonj 		dhcpv6_ia_na_t d6in;
6453431Scarlsonj 		dhcpv6_iaaddr_t d6ia;
6463431Scarlsonj 		uint32_t iaid;
6473431Scarlsonj 		uint16_t *statusopt;
6483431Scarlsonj 		dhcpv6_option_t *d6o, *d6so;
6493431Scarlsonj 		uint_t olen;
6503431Scarlsonj 
6513431Scarlsonj 		/*
6523431Scarlsonj 		 * Currently, we support just one IAID related to the primary
6533431Scarlsonj 		 * LIF on the state machine.
6543431Scarlsonj 		 */
6553431Scarlsonj 		dsmp = lif->lif_lease->dl_smach;
6563431Scarlsonj 		iaid = dsmp->dsm_lif->lif_iaid;
6573431Scarlsonj 		iaid = htonl(iaid);
6583431Scarlsonj 
6593431Scarlsonj 		d6m = (dhcpv6_message_t *)dpkt->pkt;
6600Sstevel@tonic-gate 
6613431Scarlsonj 		/*
6623431Scarlsonj 		 * Find or create the IA_NA needed for this LIF.  If we
6633431Scarlsonj 		 * supported IA_TA, we'd check the IFF_TEMPORARY bit here.
6643431Scarlsonj 		 */
6653431Scarlsonj 		d6o = NULL;
6663431Scarlsonj 		while ((d6o = dhcpv6_find_option(d6m + 1,
6673431Scarlsonj 		    dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA,
6683431Scarlsonj 		    &olen)) != NULL) {
6693431Scarlsonj 			if (olen < sizeof (d6in))
6703431Scarlsonj 				continue;
6713431Scarlsonj 			(void) memcpy(&d6in, d6o, sizeof (d6in));
6723431Scarlsonj 			if (d6in.d6in_iaid == iaid)
6733431Scarlsonj 				break;
6743431Scarlsonj 		}
6753431Scarlsonj 		if (d6o == NULL) {
6763431Scarlsonj 			d6in.d6in_iaid = iaid;
6773431Scarlsonj 			d6in.d6in_t1 = 0;
6783431Scarlsonj 			d6in.d6in_t2 = 0;
6793431Scarlsonj 			d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
6803431Scarlsonj 			    (dhcpv6_option_t *)&d6in + 1,
6813431Scarlsonj 			    sizeof (d6in) - sizeof (*d6o));
6823431Scarlsonj 			if (d6o == NULL)
6833431Scarlsonj 				return (B_FALSE);
6843431Scarlsonj 		}
6850Sstevel@tonic-gate 
6863431Scarlsonj 		/*
6873431Scarlsonj 		 * Now add the IAADDR suboption for this LIF.  No need to
6883431Scarlsonj 		 * search here, as we know that this is unique.
6893431Scarlsonj 		 */
6903431Scarlsonj 		d6ia.d6ia_addr = lif->lif_v6addr;
6910Sstevel@tonic-gate 
6923431Scarlsonj 		/*
6933431Scarlsonj 		 * For Release and Decline, we zero out the lifetime.  For
6943431Scarlsonj 		 * Renew and Rebind, we report the original time as the
6953431Scarlsonj 		 * preferred and valid lifetimes.
6963431Scarlsonj 		 */
6973431Scarlsonj 		if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE ||
6983431Scarlsonj 		    d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) {
6993431Scarlsonj 			d6ia.d6ia_preflife = 0;
7003431Scarlsonj 			d6ia.d6ia_vallife = 0;
7013431Scarlsonj 		} else {
7023431Scarlsonj 			d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start);
7033431Scarlsonj 			d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start);
7043431Scarlsonj 		}
7053431Scarlsonj 		d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR,
7063431Scarlsonj 		    (dhcpv6_option_t *)&d6ia + 1,
7073431Scarlsonj 		    sizeof (d6ia) - sizeof (*d6o));
7083431Scarlsonj 		if (d6so == NULL)
7093431Scarlsonj 			return (B_FALSE);
7100Sstevel@tonic-gate 
7113431Scarlsonj 		/*
7123431Scarlsonj 		 * Add a status code suboption to the IAADDR to tell the server
7133431Scarlsonj 		 * why we're declining the address.  Note that we must manually
7143431Scarlsonj 		 * update the enclosing IA_NA, as add_pkt_subopt doesn't know
7153431Scarlsonj 		 * how to do that.
7163431Scarlsonj 		 */
7173431Scarlsonj 		if (status != DHCPV6_STAT_SUCCESS || msg != NULL) {
7183431Scarlsonj 			olen = sizeof (*statusopt) +
7193431Scarlsonj 			    (msg == NULL ? 0 : strlen(msg));
7203431Scarlsonj 			statusopt = alloca(olen);
7213431Scarlsonj 			*statusopt = htons(status);
7223431Scarlsonj 			if (msg != NULL) {
7233431Scarlsonj 				(void) memcpy((char *)(statusopt + 1), msg,
7243431Scarlsonj 				    olen - sizeof (*statusopt));
7253431Scarlsonj 			}
7263431Scarlsonj 			d6so = add_pkt_subopt(dpkt, d6so,
7273431Scarlsonj 			    DHCPV6_OPT_STATUS_CODE, statusopt, olen);
7283431Scarlsonj 			if (d6so != NULL) {
7293431Scarlsonj 				/*
7303431Scarlsonj 				 * Update for length of suboption header and
7313431Scarlsonj 				 * suboption contents.
7323431Scarlsonj 				 */
7333431Scarlsonj 				(void) update_v6opt_len(d6o, sizeof (*d6so) +
7343431Scarlsonj 				    olen);
7353431Scarlsonj 			}
7363431Scarlsonj 		}
7373431Scarlsonj 	} else {
7383431Scarlsonj 		/*
7393431Scarlsonj 		 * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option.
7403431Scarlsonj 		 * In all other cases (RELEASE and REQUEST), we need to set
7413431Scarlsonj 		 * ciadr.
7423431Scarlsonj 		 */
7433431Scarlsonj 		if (pkt_send_type(dpkt) == DECLINE) {
7443431Scarlsonj 			if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
7453431Scarlsonj 			    lif->lif_addr))
7463431Scarlsonj 				return (B_FALSE);
7473431Scarlsonj 		} else {
7483431Scarlsonj 			dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
7493431Scarlsonj 		}
7503431Scarlsonj 
7513431Scarlsonj 		/*
7523431Scarlsonj 		 * It's not too worrisome if the message fails to fit in the
7533431Scarlsonj 		 * packet.  The result will still be valid.
7543431Scarlsonj 		 */
7553431Scarlsonj 		if (msg != NULL)
7563431Scarlsonj 			(void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
7573431Scarlsonj 			    strlen(msg) + 1);
7580Sstevel@tonic-gate 	}
7593431Scarlsonj 	return (B_TRUE);
7600Sstevel@tonic-gate }
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate /*
7633431Scarlsonj  * free_pkt_entry(): frees a packet list list entry
7640Sstevel@tonic-gate  *
7653431Scarlsonj  *   input: PKT_LIST *: the packet list entry to free
7663431Scarlsonj  *  output: void
7670Sstevel@tonic-gate  */
7683431Scarlsonj void
7693431Scarlsonj free_pkt_entry(PKT_LIST *plp)
7700Sstevel@tonic-gate {
7713431Scarlsonj 	if (plp != NULL) {
7723431Scarlsonj 		free(plp->pkt);
7733431Scarlsonj 		free(plp);
7743431Scarlsonj 	}
7750Sstevel@tonic-gate }
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate /*
7783431Scarlsonj  * free_pkt_list(): frees an entire packet list
7790Sstevel@tonic-gate  *
7800Sstevel@tonic-gate  *   input: PKT_LIST **: the packet list to free
7810Sstevel@tonic-gate  *  output: void
7820Sstevel@tonic-gate  */
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate void
7853431Scarlsonj free_pkt_list(PKT_LIST **head)
7860Sstevel@tonic-gate {
7873431Scarlsonj 	PKT_LIST *plp;
7880Sstevel@tonic-gate 
7893431Scarlsonj 	while ((plp = *head) != NULL) {
7903431Scarlsonj 		remque(plp);
7913431Scarlsonj 		free_pkt_entry(plp);
7920Sstevel@tonic-gate 	}
7930Sstevel@tonic-gate }
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate /*
7960Sstevel@tonic-gate  * send_pkt_internal(): sends a packet out on an interface
7970Sstevel@tonic-gate  *
7983431Scarlsonj  *   input: dhcp_smach_t *: the state machine with a packet to send
7993431Scarlsonj  *  output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise
8000Sstevel@tonic-gate  */
8010Sstevel@tonic-gate 
8023431Scarlsonj static boolean_t
8033431Scarlsonj send_pkt_internal(dhcp_smach_t *dsmp)
8040Sstevel@tonic-gate {
8050Sstevel@tonic-gate 	ssize_t		n_bytes;
8063431Scarlsonj 	dhcp_lif_t	*lif = dsmp->dsm_lif;
8073431Scarlsonj 	dhcp_pif_t	*pif = lif->lif_pif;
8083431Scarlsonj 	dhcp_pkt_t	*dpkt = &dsmp->dsm_send_pkt;
8093431Scarlsonj 	uchar_t		ptype = pkt_send_type(dpkt);
8103431Scarlsonj 	const char	*pkt_name;
8113431Scarlsonj 	struct iovec	iov;
8123431Scarlsonj 	struct msghdr	msg;
8133431Scarlsonj 	struct cmsghdr	*cmsg;
8143431Scarlsonj 	struct in6_pktinfo *ipi6;
8153431Scarlsonj 	boolean_t	ismcast;
8163431Scarlsonj 
8173431Scarlsonj 	/*
8183431Scarlsonj 	 * Timer should not be running at the point we go to send a packet.
8193431Scarlsonj 	 */
8203431Scarlsonj 	if (dsmp->dsm_retrans_timer != -1) {
8213431Scarlsonj 		dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit "
8223431Scarlsonj 		    "timer on %s", dsmp->dsm_name);
8233431Scarlsonj 		stop_pkt_retransmission(dsmp);
8243431Scarlsonj 	}
8253431Scarlsonj 
8263431Scarlsonj 	pkt_name = pkt_type_to_string(ptype, dpkt->pkt_isv6);
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	/*
8290Sstevel@tonic-gate 	 * if needed, schedule a retransmission timer, then attempt to
8300Sstevel@tonic-gate 	 * send the packet.  if we fail, then log the error.  our
8310Sstevel@tonic-gate 	 * return value should indicate whether or not we were
8320Sstevel@tonic-gate 	 * successful in sending the request, independent of whether
8330Sstevel@tonic-gate 	 * we could schedule a timer.
8340Sstevel@tonic-gate 	 */
8350Sstevel@tonic-gate 
8363431Scarlsonj 	if (dsmp->dsm_send_timeout != 0) {
8373431Scarlsonj 		if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq,
8383431Scarlsonj 		    dsmp->dsm_send_timeout, retransmit, dsmp)) == -1)
8390Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot "
8400Sstevel@tonic-gate 			    "schedule retransmit timer for %s packet",
8410Sstevel@tonic-gate 			    pkt_name);
8420Sstevel@tonic-gate 		else
8433431Scarlsonj 			hold_smach(dsmp);
8443431Scarlsonj 	}
8453431Scarlsonj 
8463431Scarlsonj 	if (dpkt->pkt_isv6) {
8473431Scarlsonj 		hrtime_t delta;
8483431Scarlsonj 
8493431Scarlsonj 		/*
8503431Scarlsonj 		 * Convert current time into centiseconds since transaction
8513431Scarlsonj 		 * started.  This is what DHCPv6 expects to see in the Elapsed
8523431Scarlsonj 		 * Time option.
8533431Scarlsonj 		 */
8543431Scarlsonj 		delta = (gethrtime() - dsmp->dsm_neg_hrtime) /
8553431Scarlsonj 		    (NANOSEC / 100);
8563431Scarlsonj 		if (delta > DHCPV6_FOREVER)
8573431Scarlsonj 			delta = DHCPV6_FOREVER;
8583431Scarlsonj 		(void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME);
8593431Scarlsonj 		(void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME,
8603431Scarlsonj 		    htons(delta));
8613431Scarlsonj 	} else {
8623431Scarlsonj 		/*
8633431Scarlsonj 		 * set the `pkt->secs' field depending on the type of packet.
8643431Scarlsonj 		 * it should be zero, except in the following cases:
8653431Scarlsonj 		 *
8663431Scarlsonj 		 * DISCOVER:	set to the number of seconds since we started
8673431Scarlsonj 		 *		trying to obtain a lease.
8683431Scarlsonj 		 *
8693431Scarlsonj 		 * INFORM:	set to the number of seconds since we started
8703431Scarlsonj 		 *		trying to get configuration parameters.
8713431Scarlsonj 		 *
8723431Scarlsonj 		 * REQUEST:	if in the REQUESTING state, then same value as
8733431Scarlsonj 		 *		DISCOVER, otherwise the number of seconds
8743431Scarlsonj 		 *		since we started trying to obtain a lease.
8753431Scarlsonj 		 *
8763431Scarlsonj 		 * we also set `dsm_newstart_monosec', to the time we sent a
8773431Scarlsonj 		 * REQUEST or DISCOVER packet, so we know the lease start
8783431Scarlsonj 		 * time (the DISCOVER case is for handling BOOTP servers).
8793431Scarlsonj 		 */
8803431Scarlsonj 
8813431Scarlsonj 		switch (ptype) {
8823431Scarlsonj 
8833431Scarlsonj 		case DISCOVER:
8843431Scarlsonj 			dsmp->dsm_newstart_monosec = monosec();
8853431Scarlsonj 			dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec -
8863431Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime);
8873431Scarlsonj 			dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
8883431Scarlsonj 			break;
8893431Scarlsonj 
8903431Scarlsonj 		case INFORM:
8913431Scarlsonj 			dpkt->pkt->secs = htons(monosec() -
8923431Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime));
8933431Scarlsonj 			break;
8943431Scarlsonj 
8953431Scarlsonj 		case REQUEST:
8963431Scarlsonj 			dsmp->dsm_newstart_monosec = monosec();
8973431Scarlsonj 
8983431Scarlsonj 			if (dsmp->dsm_state == REQUESTING) {
8993431Scarlsonj 				dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
9003431Scarlsonj 				break;
9013431Scarlsonj 			}
9023431Scarlsonj 
9033431Scarlsonj 			dpkt->pkt->secs = htons(monosec() -
9043431Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime));
9053431Scarlsonj 			break;
9063431Scarlsonj 
9073431Scarlsonj 		default:
9083431Scarlsonj 			dpkt->pkt->secs = htons(0);
9093431Scarlsonj 			break;
9103431Scarlsonj 		}
9110Sstevel@tonic-gate 	}
9120Sstevel@tonic-gate 
9133431Scarlsonj 	if (dpkt->pkt_isv6) {
9143431Scarlsonj 		struct sockaddr_in6 sin6;
9153431Scarlsonj 
9163431Scarlsonj 		(void) memset(&iov, 0, sizeof (iov));
9173431Scarlsonj 		iov.iov_base = dpkt->pkt;
9183431Scarlsonj 		iov.iov_len = dpkt->pkt_cur_len;
9193431Scarlsonj 
9203431Scarlsonj 		(void) memset(&msg, 0, sizeof (msg));
9213431Scarlsonj 		msg.msg_name = &dsmp->dsm_send_dest.v6;
9223431Scarlsonj 		msg.msg_namelen = sizeof (struct sockaddr_in6);
9233431Scarlsonj 		msg.msg_iov = &iov;
9243431Scarlsonj 		msg.msg_iovlen = 1;
9253431Scarlsonj 
9263431Scarlsonj 		/*
9273431Scarlsonj 		 * If the address that's requested cannot be reached, then fall
9283431Scarlsonj 		 * back to the multcast address.
9293431Scarlsonj 		 */
9303431Scarlsonj 		if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) {
9313431Scarlsonj 			ismcast = B_TRUE;
9323431Scarlsonj 		} else {
9333431Scarlsonj 			struct dstinforeq dinfo;
9343431Scarlsonj 			struct strioctl str;
9350Sstevel@tonic-gate 
9363431Scarlsonj 			ismcast = B_FALSE;
9373431Scarlsonj 			(void) memset(&dinfo, 0, sizeof (dinfo));
9383431Scarlsonj 			dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr;
9393431Scarlsonj 			str.ic_cmd = SIOCGDSTINFO;
9403431Scarlsonj 			str.ic_timout = 0;
9413431Scarlsonj 			str.ic_len = sizeof (dinfo);
9423431Scarlsonj 			str.ic_dp = (char *)&dinfo;
9433431Scarlsonj 			if (ioctl(v6_sock_fd, I_STR, &str) == -1) {
9443431Scarlsonj 				dhcpmsg(MSG_ERR,
9453431Scarlsonj 				    "send_pkt_internal: ioctl SIOCGDSTINFO");
9463431Scarlsonj 			} else if (!dinfo.dir_dreachable) {
9473431Scarlsonj 				char abuf[INET6_ADDRSTRLEN];
9480Sstevel@tonic-gate 
9493431Scarlsonj 				dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is "
9503431Scarlsonj 				    "not reachable; using multicast instead",
9513431Scarlsonj 				    inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf,
9523431Scarlsonj 				    sizeof (abuf)));
9533431Scarlsonj 				sin6 = dsmp->dsm_send_dest.v6;
9543431Scarlsonj 				sin6.sin6_addr =
9553431Scarlsonj 				    ipv6_all_dhcp_relay_and_servers;
9563431Scarlsonj 				msg.msg_name = &sin6;
9573431Scarlsonj 				ismcast = B_TRUE;
9583431Scarlsonj 			}
9590Sstevel@tonic-gate 		}
9600Sstevel@tonic-gate 
9613431Scarlsonj 		/*
9623431Scarlsonj 		 * Make room for our ancillary data option as well as a dummy
9633431Scarlsonj 		 * option used by CMSG_NXTHDR.
9643431Scarlsonj 		 */
9653431Scarlsonj 		msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT +
9663431Scarlsonj 		    sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg);
9673431Scarlsonj 		msg.msg_control = alloca(msg.msg_controllen);
9683431Scarlsonj 		cmsg = CMSG_FIRSTHDR(&msg);
9693431Scarlsonj 		cmsg->cmsg_level = IPPROTO_IPV6;
9703431Scarlsonj 		cmsg->cmsg_type = IPV6_PKTINFO;
9713431Scarlsonj 		/* LINTED: alignment */
9723431Scarlsonj 		ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
9733431Scarlsonj 		if (ismcast)
9743431Scarlsonj 			ipi6->ipi6_addr = lif->lif_v6addr;
9753431Scarlsonj 		else
9763431Scarlsonj 			ipi6->ipi6_addr = my_in6addr_any;
9773431Scarlsonj 		ipi6->ipi6_ifindex = lif->lif_pif->pif_index;
9783431Scarlsonj 		cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg;
9790Sstevel@tonic-gate 
9803431Scarlsonj 		/*
9813431Scarlsonj 		 * Now correct the control message length.
9823431Scarlsonj 		 */
9833431Scarlsonj 		cmsg = CMSG_NXTHDR(&msg, cmsg);
9843431Scarlsonj 		msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control;
9850Sstevel@tonic-gate 
9863431Scarlsonj 		n_bytes = sendmsg(v6_sock_fd, &msg, 0);
9873431Scarlsonj 	} else {
9883431Scarlsonj 		if (dsmp->dsm_using_dlpi) {
9894456Sss150715 			n_bytes = dlpi_sendto(pif->pif_dlpi_hd, dpkt->pkt,
9903431Scarlsonj 			    dpkt->pkt_cur_len, &dsmp->dsm_send_dest.v4,
9913431Scarlsonj 			    pif->pif_daddr, pif->pif_dlen);
9923431Scarlsonj 			/* dlpi_sendto calls putmsg */
9933431Scarlsonj 			if (n_bytes == 0)
9943431Scarlsonj 				n_bytes = dpkt->pkt_cur_len;
9953431Scarlsonj 		} else {
9963431Scarlsonj 			n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt,
9973431Scarlsonj 			    dpkt->pkt_cur_len, 0,
9983431Scarlsonj 			    (struct sockaddr *)&dsmp->dsm_send_dest.v4,
9993431Scarlsonj 			    sizeof (struct sockaddr_in));
10003431Scarlsonj 		}
10010Sstevel@tonic-gate 	}
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	if (n_bytes != dpkt->pkt_cur_len) {
10043431Scarlsonj 		if (dsmp->dsm_retrans_timer == -1)
10050Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send "
10060Sstevel@tonic-gate 			    "%s packet to server", pkt_name);
10070Sstevel@tonic-gate 		else
10080Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send "
10090Sstevel@tonic-gate 			    "%s packet to server (will retry in %u seconds)",
10103431Scarlsonj 			    pkt_name, dsmp->dsm_send_timeout / MILLISEC);
10113431Scarlsonj 		return (B_FALSE);
10120Sstevel@tonic-gate 	}
10130Sstevel@tonic-gate 
10143431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name,
10153431Scarlsonj 	    pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name);
10160Sstevel@tonic-gate 
10173431Scarlsonj 	dsmp->dsm_packet_sent++;
10183431Scarlsonj 	dsmp->dsm_sent++;
10193431Scarlsonj 	return (B_TRUE);
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate /*
10233431Scarlsonj  * send_pkt(): sends a packet out
10240Sstevel@tonic-gate  *
10253431Scarlsonj  *   input: dhcp_smach_t *: the state machine sending the packet
10260Sstevel@tonic-gate  *	    dhcp_pkt_t *: the packet to send out
10270Sstevel@tonic-gate  *	    in_addr_t: the destination IP address for the packet
10280Sstevel@tonic-gate  *	    stop_func_t *: a pointer to function to indicate when to stop
10290Sstevel@tonic-gate  *			   retransmitting the packet (if NULL, packet is
10300Sstevel@tonic-gate  *			   not retransmitted)
10313431Scarlsonj  *  output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
10320Sstevel@tonic-gate  */
10330Sstevel@tonic-gate 
10343431Scarlsonj boolean_t
10353431Scarlsonj send_pkt(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in_addr_t dest,
10360Sstevel@tonic-gate     stop_func_t *stop)
10370Sstevel@tonic-gate {
10380Sstevel@tonic-gate 	/*
10390Sstevel@tonic-gate 	 * packets must be at least sizeof (PKT) or they may be dropped
10400Sstevel@tonic-gate 	 * by routers.  pad out the packet in this case.
10410Sstevel@tonic-gate 	 */
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT));
10440Sstevel@tonic-gate 
10453431Scarlsonj 	dsmp->dsm_packet_sent = 0;
10460Sstevel@tonic-gate 
10473431Scarlsonj 	(void) memset(&dsmp->dsm_send_dest.v4, 0,
10483431Scarlsonj 	    sizeof (dsmp->dsm_send_dest.v4));
10493431Scarlsonj 	dsmp->dsm_send_dest.v4.sin_addr.s_addr	= dest;
10503431Scarlsonj 	dsmp->dsm_send_dest.v4.sin_family	= AF_INET;
10513431Scarlsonj 	dsmp->dsm_send_dest.v4.sin_port		= htons(IPPORT_BOOTPS);
10523431Scarlsonj 	dsmp->dsm_send_stop_func		= stop;
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate 	/*
10550Sstevel@tonic-gate 	 * TODO: dispose of this gruesome assumption (there's no real
10560Sstevel@tonic-gate 	 * technical gain from doing so, but it would be cleaner)
10570Sstevel@tonic-gate 	 */
10580Sstevel@tonic-gate 
10593431Scarlsonj 	assert(dpkt == &dsmp->dsm_send_pkt);
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 	/*
10620Sstevel@tonic-gate 	 * clear out any packets which had been previously received
10630Sstevel@tonic-gate 	 * but not pulled off of the recv_packet queue.
10640Sstevel@tonic-gate 	 */
10650Sstevel@tonic-gate 
10663431Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
10673431Scarlsonj 
10683431Scarlsonj 	if (stop == NULL)
10693431Scarlsonj 		dsmp->dsm_send_timeout = 0;	/* prevents retransmissions */
10703431Scarlsonj 	else
10713431Scarlsonj 		next_retransmission(dsmp, B_TRUE, B_FALSE);
10723431Scarlsonj 
10733431Scarlsonj 	return (send_pkt_internal(dsmp));
10743431Scarlsonj }
10753431Scarlsonj 
10763431Scarlsonj /*
10773431Scarlsonj  * send_pkt_v6(): sends a DHCPv6 packet out
10783431Scarlsonj  *
10793431Scarlsonj  *   input: dhcp_smach_t *: the state machine sending the packet
10803431Scarlsonj  *	    dhcp_pkt_t *: the packet to send out
10813431Scarlsonj  *	    in6_addr_t: the destination IPv6 address for the packet
10823431Scarlsonj  *	    stop_func_t *: a pointer to function to indicate when to stop
10833431Scarlsonj  *			   retransmitting the packet (if NULL, packet is
10843431Scarlsonj  *			   not retransmitted)
10853431Scarlsonj  *	    uint_t: Initial Retransmit Timer value
10863431Scarlsonj  *	    uint_t: Maximum Retransmit Timer value, zero if none
10873431Scarlsonj  *  output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
10883431Scarlsonj  */
10893431Scarlsonj 
10903431Scarlsonj boolean_t
10913431Scarlsonj send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest,
10923431Scarlsonj     stop_func_t *stop, uint_t irt, uint_t mrt)
10933431Scarlsonj {
10943431Scarlsonj 	dsmp->dsm_packet_sent = 0;
10953431Scarlsonj 
10963431Scarlsonj 	(void) memset(&dsmp->dsm_send_dest.v6, 0,
10973431Scarlsonj 	    sizeof (dsmp->dsm_send_dest.v6));
10983431Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_addr	= dest;
10993431Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_family	= AF_INET6;
11003431Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_port	= htons(IPPORT_DHCPV6S);
11013431Scarlsonj 	dsmp->dsm_send_stop_func		= stop;
11023431Scarlsonj 
11033431Scarlsonj 	/*
11043431Scarlsonj 	 * TODO: dispose of this gruesome assumption (there's no real
11053431Scarlsonj 	 * technical gain from doing so, but it would be cleaner)
11063431Scarlsonj 	 */
11073431Scarlsonj 
11083431Scarlsonj 	assert(dpkt == &dsmp->dsm_send_pkt);
11093431Scarlsonj 
11103431Scarlsonj 	/*
11113431Scarlsonj 	 * clear out any packets which had been previously received
11123431Scarlsonj 	 * but not pulled off of the recv_packet queue.
11133431Scarlsonj 	 */
11143431Scarlsonj 
11153431Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 	if (stop == NULL) {
11183431Scarlsonj 		dsmp->dsm_send_timeout = 0;	/* prevents retransmissions */
11193431Scarlsonj 	} else {
11203431Scarlsonj 		dsmp->dsm_send_timeout = irt;
11213431Scarlsonj 		dsmp->dsm_send_tcenter = mrt;
11223431Scarlsonj 		/*
11233431Scarlsonj 		 * This is quite ugly, but RFC 3315 section 17.1.2 requires
11243431Scarlsonj 		 * that the RAND value for the very first retransmission of a
11253431Scarlsonj 		 * Solicit message is strictly greater than zero.
11263431Scarlsonj 		 */
11273431Scarlsonj 		next_retransmission(dsmp, B_TRUE,
11283431Scarlsonj 		    pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT);
11293431Scarlsonj 	}
11300Sstevel@tonic-gate 
11313431Scarlsonj 	return (send_pkt_internal(dsmp));
11320Sstevel@tonic-gate }
11330Sstevel@tonic-gate 
11340Sstevel@tonic-gate /*
11350Sstevel@tonic-gate  * retransmit(): retransmits the current packet on an interface
11360Sstevel@tonic-gate  *
11370Sstevel@tonic-gate  *   input: iu_tq_t *: unused
11383431Scarlsonj  *	    void *: the dhcp_smach_t * (state machine) sending a packet
11390Sstevel@tonic-gate  *  output: void
11400Sstevel@tonic-gate  */
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate /* ARGSUSED */
11430Sstevel@tonic-gate static void
11440Sstevel@tonic-gate retransmit(iu_tq_t *tqp, void *arg)
11450Sstevel@tonic-gate {
11463431Scarlsonj 	dhcp_smach_t	*dsmp = arg;
11470Sstevel@tonic-gate 
11483431Scarlsonj 	dsmp->dsm_retrans_timer = -1;
11493431Scarlsonj 
11503431Scarlsonj 	if (!verify_smach(dsmp))
11510Sstevel@tonic-gate 		return;
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	/*
11543431Scarlsonj 	 * Check the callback to see if we should keep sending retransmissions.
11553431Scarlsonj 	 * Compute the next retransmission time first, so that the callback can
11563431Scarlsonj 	 * cap the value if need be.  (Required for DHCPv6 Confirm messages.)
11573431Scarlsonj 	 *
11583431Scarlsonj 	 * Hold the state machine across the callback so that the called
11593431Scarlsonj 	 * function can remove the state machine from the system without
11603431Scarlsonj 	 * disturbing the string used subsequently for verbose logging.  The
11613431Scarlsonj 	 * Release function destroys the state machine when the retry count
11623431Scarlsonj 	 * expires.
11630Sstevel@tonic-gate 	 */
11640Sstevel@tonic-gate 
11653431Scarlsonj 	next_retransmission(dsmp, B_FALSE, B_FALSE);
11663431Scarlsonj 	hold_smach(dsmp);
11673431Scarlsonj 	if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) {
11683431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s",
11693431Scarlsonj 		    dsmp->dsm_name);
11703431Scarlsonj 	} else {
11713431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s",
11723431Scarlsonj 		    dsmp->dsm_name);
11733431Scarlsonj 		(void) send_pkt_internal(dsmp);
11743431Scarlsonj 	}
11753431Scarlsonj 	release_smach(dsmp);
11760Sstevel@tonic-gate }
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate /*
11790Sstevel@tonic-gate  * stop_pkt_retransmission(): stops retransmission of last sent packet
11800Sstevel@tonic-gate  *
11813431Scarlsonj  *   input: dhcp_smach_t *: the state machine to stop retransmission on
11820Sstevel@tonic-gate  *  output: void
11830Sstevel@tonic-gate  */
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate void
11863431Scarlsonj stop_pkt_retransmission(dhcp_smach_t *dsmp)
11870Sstevel@tonic-gate {
11883431Scarlsonj 	if (dsmp->dsm_retrans_timer != -1 &&
11893431Scarlsonj 	    iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) {
11903431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s",
11913431Scarlsonj 		    dsmp->dsm_name);
11923431Scarlsonj 		dsmp->dsm_retrans_timer = -1;
11933431Scarlsonj 		release_smach(dsmp);
11940Sstevel@tonic-gate 	}
11950Sstevel@tonic-gate }
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate /*
11983431Scarlsonj  * retransmit_now(): force a packet retransmission right now.  Used only with
11993431Scarlsonj  *		     the DHCPv6 UseMulticast status code.  Use with caution;
12003431Scarlsonj  *		     triggered retransmissions can cause packet storms.
12010Sstevel@tonic-gate  *
12023431Scarlsonj  *   input: dhcp_smach_t *: the state machine to force retransmission on
12033431Scarlsonj  *  output: void
12040Sstevel@tonic-gate  */
12050Sstevel@tonic-gate 
12063431Scarlsonj void
12073431Scarlsonj retransmit_now(dhcp_smach_t *dsmp)
12083431Scarlsonj {
12093431Scarlsonj 	stop_pkt_retransmission(dsmp);
12103431Scarlsonj 	(void) send_pkt_internal(dsmp);
12113431Scarlsonj }
12123431Scarlsonj 
12133431Scarlsonj /*
12143431Scarlsonj  * alloc_pkt_entry(): Allocates a packet list entry with a given data area
12153431Scarlsonj  *		      size.
12163431Scarlsonj  *
12173431Scarlsonj  *   input: size_t: size of data area for packet
12183431Scarlsonj  *	    boolean_t: B_TRUE for IPv6
12193431Scarlsonj  *  output: PKT_LIST *: allocated packet list entry
12203431Scarlsonj  */
12213431Scarlsonj 
12223431Scarlsonj PKT_LIST *
12233431Scarlsonj alloc_pkt_entry(size_t psize, boolean_t isv6)
12240Sstevel@tonic-gate {
12250Sstevel@tonic-gate 	PKT_LIST	*plp;
12260Sstevel@tonic-gate 
12273431Scarlsonj 	if ((plp = calloc(1, sizeof (*plp))) == NULL ||
12283431Scarlsonj 	    (plp->pkt = malloc(psize)) == NULL) {
12293431Scarlsonj 		free(plp);
12303431Scarlsonj 		plp = NULL;
12313431Scarlsonj 	} else {
12323431Scarlsonj 		plp->len = psize;
12333431Scarlsonj 		plp->isv6 = isv6;
12340Sstevel@tonic-gate 	}
12350Sstevel@tonic-gate 
12363431Scarlsonj 	return (plp);
12373431Scarlsonj }
12380Sstevel@tonic-gate 
12393431Scarlsonj /*
12403431Scarlsonj  * sock_recvpkt(): read from the given socket into an allocated buffer and
12413431Scarlsonj  *		   handles any ancillary data options.
12423431Scarlsonj  *
12433431Scarlsonj  *   input: int: file descriptor to read
12443431Scarlsonj  *	    PKT_LIST *: allocated buffer
12453431Scarlsonj  *  output: ssize_t: number of bytes read, or -1 on error
12463431Scarlsonj  */
12473431Scarlsonj 
12483431Scarlsonj static ssize_t
12493431Scarlsonj sock_recvpkt(int fd, PKT_LIST *plp)
12503431Scarlsonj {
12513431Scarlsonj 	struct iovec iov;
12523431Scarlsonj 	struct msghdr msg;
12533476Scarlsonj 	int64_t ctrl[8192 / sizeof (int64_t)];
12543431Scarlsonj 	ssize_t msglen;
12550Sstevel@tonic-gate 
12563431Scarlsonj 	(void) memset(&iov, 0, sizeof (iov));
12573431Scarlsonj 	iov.iov_base = (caddr_t)plp->pkt;
12583431Scarlsonj 	iov.iov_len = plp->len;
12593431Scarlsonj 
12603431Scarlsonj 	(void) memset(&msg, 0, sizeof (msg));
12613431Scarlsonj 	msg.msg_name = &plp->pktfrom;
12623431Scarlsonj 	msg.msg_namelen = sizeof (plp->pktfrom);
12633431Scarlsonj 	msg.msg_iov = &iov;
12643431Scarlsonj 	msg.msg_iovlen = 1;
12653431Scarlsonj 	msg.msg_control = ctrl;
12663431Scarlsonj 	msg.msg_controllen = sizeof (ctrl);
12673431Scarlsonj 
12683431Scarlsonj 	if ((msglen = recvmsg(fd, &msg, 0)) != -1) {
12693431Scarlsonj 		struct cmsghdr *cmsg;
12703431Scarlsonj 
12713431Scarlsonj 		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
12723431Scarlsonj 		    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
12733431Scarlsonj 			struct sockaddr_in *sinp;
12743431Scarlsonj 			struct sockaddr_in6 *sin6;
12753431Scarlsonj 			struct in6_pktinfo *ipi6;
12760Sstevel@tonic-gate 
12773431Scarlsonj 			switch (cmsg->cmsg_level) {
12783431Scarlsonj 			case IPPROTO_IP:
12793431Scarlsonj 				switch (cmsg->cmsg_type) {
12803431Scarlsonj 				case IP_RECVDSTADDR:
12813431Scarlsonj 					sinp = (struct sockaddr_in *)
12823431Scarlsonj 					    &plp->pktto;
12833431Scarlsonj 					sinp->sin_family = AF_INET;
12843431Scarlsonj 					(void) memcpy(&sinp->sin_addr.s_addr,
12853431Scarlsonj 					    CMSG_DATA(cmsg),
12863431Scarlsonj 					    sizeof (ipaddr_t));
12873431Scarlsonj 					break;
12883431Scarlsonj 
12893431Scarlsonj 				case IP_RECVIF:
12903431Scarlsonj 					(void) memcpy(&plp->ifindex,
12913431Scarlsonj 					    CMSG_DATA(cmsg), sizeof (uint_t));
12923431Scarlsonj 					break;
12933431Scarlsonj 				}
12943431Scarlsonj 				break;
12950Sstevel@tonic-gate 
12963431Scarlsonj 			case IPPROTO_IPV6:
12973431Scarlsonj 				switch (cmsg->cmsg_type) {
12983431Scarlsonj 				case IPV6_PKTINFO:
12993431Scarlsonj 					/* LINTED: alignment */
13003431Scarlsonj 					ipi6 = (struct in6_pktinfo *)
13013431Scarlsonj 					    CMSG_DATA(cmsg);
13023431Scarlsonj 					sin6 = (struct sockaddr_in6 *)
13033431Scarlsonj 					    &plp->pktto;
13043431Scarlsonj 					sin6->sin6_family = AF_INET6;
13053431Scarlsonj 					(void) memcpy(&sin6->sin6_addr,
13063431Scarlsonj 					    &ipi6->ipi6_addr,
13073431Scarlsonj 					    sizeof (ipi6->ipi6_addr));
13083431Scarlsonj 					(void) memcpy(&plp->ifindex,
13093431Scarlsonj 					    &ipi6->ipi6_ifindex,
13103431Scarlsonj 					    sizeof (uint_t));
13113431Scarlsonj 					break;
13123431Scarlsonj 				}
13133431Scarlsonj 			}
13143431Scarlsonj 		}
13153431Scarlsonj 	}
13163431Scarlsonj 	return (msglen);
13173431Scarlsonj }
13180Sstevel@tonic-gate 
13193431Scarlsonj /*
13203431Scarlsonj  * recv_pkt(): receives a single DHCP packet on a given file descriptor.
13213431Scarlsonj  *
13224456Sss150715  *   input: int: if not using dlpi, the file descriptor to receive the packet
13233431Scarlsonj  *	    int: the maximum packet size to allow
13243431Scarlsonj  *	    boolean_t: B_TRUE for IPv6
13253431Scarlsonj  *	    boolean_t: B_TRUE if using DLPI
13264456Sss150715  *	    void *: if using DLPI, structure that has DLPI handle
13273431Scarlsonj  *  output: PKT_LIST *: the received packet
13283431Scarlsonj  */
13290Sstevel@tonic-gate 
13303431Scarlsonj PKT_LIST *
13314456Sss150715 recv_pkt(int fd, int mtu, boolean_t isv6, boolean_t isdlpi, dhcp_pif_t *arg)
13323431Scarlsonj {
13333431Scarlsonj 	PKT_LIST	*plp;
13343431Scarlsonj 	ssize_t		retval;
13353431Scarlsonj 
13363431Scarlsonj 	if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) {
13373431Scarlsonj 		dhcpmsg(MSG_ERROR,
13383431Scarlsonj 		    "recv_pkt: allocation failure; dropped packet");
13393431Scarlsonj 		return (NULL);
13400Sstevel@tonic-gate 	}
13410Sstevel@tonic-gate 
13423431Scarlsonj 	if (isv6) {
13433431Scarlsonj 		retval = sock_recvpkt(fd, plp);
13443431Scarlsonj 
13453431Scarlsonj 		if (retval == -1) {
13463431Scarlsonj 			dhcpmsg(MSG_ERR,
13473431Scarlsonj 			    "recv_pkt: recvfrom v6 failed, dropped");
13483431Scarlsonj 			goto failure;
13493431Scarlsonj 		}
13503431Scarlsonj 
13513431Scarlsonj 		plp->len = retval;
13520Sstevel@tonic-gate 
13533431Scarlsonj 		if (retval < sizeof (dhcpv6_message_t)) {
13543431Scarlsonj 			dhcpmsg(MSG_WARNING, "recv_pkt: runt message");
13553431Scarlsonj 			goto failure;
13563431Scarlsonj 		}
13573431Scarlsonj 	} else {
13583431Scarlsonj 		if (isdlpi) {
13594456Sss150715 			dhcp_pif_t	*pif = arg;
13604456Sss150715 
13614456Sss150715 			retval = dlpi_recvfrom(pif->pif_dlpi_hd, plp->pkt, mtu,
13623431Scarlsonj 			    (struct sockaddr_in *)&plp->pktfrom,
13633431Scarlsonj 			    (struct sockaddr_in *)&plp->pktto);
13643431Scarlsonj 		} else {
13653431Scarlsonj 			retval = sock_recvpkt(fd, plp);
13663431Scarlsonj 		}
13670Sstevel@tonic-gate 
13683431Scarlsonj 		if (retval == -1) {
13693431Scarlsonj 			dhcpmsg(MSG_ERR,
13703431Scarlsonj 			    "recv_pkt: %srecvfrom v4 failed, dropped",
13713431Scarlsonj 			    isdlpi ? "dlpi_" : "");
13723431Scarlsonj 			goto failure;
13733431Scarlsonj 		}
13743431Scarlsonj 
13753431Scarlsonj 		plp->len = retval;
13763431Scarlsonj 
13773431Scarlsonj 		switch (dhcp_options_scan(plp, B_TRUE)) {
13783431Scarlsonj 
13793431Scarlsonj 		case DHCP_WRONG_MSG_TYPE:
13803431Scarlsonj 			dhcpmsg(MSG_WARNING,
13813431Scarlsonj 			    "recv_pkt: unexpected DHCP message");
13823431Scarlsonj 			goto failure;
13830Sstevel@tonic-gate 
13843431Scarlsonj 		case DHCP_GARBLED_MSG_TYPE:
13853431Scarlsonj 			dhcpmsg(MSG_WARNING,
13863431Scarlsonj 			    "recv_pkt: garbled DHCP message type");
13873431Scarlsonj 			goto failure;
13880Sstevel@tonic-gate 
13893431Scarlsonj 		case DHCP_BAD_OPT_OVLD:
13903431Scarlsonj 			dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload");
13913431Scarlsonj 			goto failure;
13923431Scarlsonj 
13933431Scarlsonj 		case 0:
13943431Scarlsonj 			break;
13953431Scarlsonj 
13963431Scarlsonj 		default:
13973431Scarlsonj 			dhcpmsg(MSG_WARNING,
13983431Scarlsonj 			    "recv_pkt: packet corrupted, dropped");
13990Sstevel@tonic-gate 			goto failure;
14000Sstevel@tonic-gate 		}
14010Sstevel@tonic-gate 	}
14023431Scarlsonj 	return (plp);
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate failure:
14053431Scarlsonj 	free_pkt_entry(plp);
14063431Scarlsonj 	return (NULL);
14073431Scarlsonj }
14083431Scarlsonj 
14093431Scarlsonj /*
14103431Scarlsonj  * pkt_v4_match(): check if a given DHCPv4 message type is in a given set
14113431Scarlsonj  *
14123431Scarlsonj  *   input: uchar_t: packet type
14133431Scarlsonj  *	    dhcp_message_type_t: bit-wise OR of DHCP_P* values.
14143431Scarlsonj  *  output: boolean_t: B_TRUE if packet type is in the set
14153431Scarlsonj  */
14163431Scarlsonj 
14173431Scarlsonj boolean_t
14183431Scarlsonj pkt_v4_match(uchar_t type, dhcp_message_type_t match_type)
14193431Scarlsonj {
14203431Scarlsonj 	/*
14213431Scarlsonj 	 * note: the ordering here allows direct indexing of the table
14223431Scarlsonj 	 *	 based on the RFC2131 packet type value passed in.
14233431Scarlsonj 	 */
14243431Scarlsonj 
14253431Scarlsonj 	static dhcp_message_type_t type_map[] = {
14263431Scarlsonj 		DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST,
14273431Scarlsonj 		DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE,
14283431Scarlsonj 		DHCP_PINFORM
14293431Scarlsonj 	};
14303431Scarlsonj 
14313431Scarlsonj 	if (type < (sizeof (type_map) / sizeof (*type_map)))
14323431Scarlsonj 		return ((type_map[type] & match_type) ? B_TRUE : B_FALSE);
14333431Scarlsonj 	else
14343431Scarlsonj 		return (B_FALSE);
14353431Scarlsonj }
14363431Scarlsonj 
14373431Scarlsonj /*
14383431Scarlsonj  * pkt_smach_enqueue(): enqueue a packet on a given state machine
14393431Scarlsonj  *
14403431Scarlsonj  *   input: dhcp_smach_t: state machine
14413431Scarlsonj  *	    PKT_LIST *: packet to enqueue
14423431Scarlsonj  *  output: none
14433431Scarlsonj  */
14443431Scarlsonj 
14453431Scarlsonj void
14463431Scarlsonj pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp)
14473431Scarlsonj {
14483431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s",
14493431Scarlsonj 	    pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6),
14503431Scarlsonj 	    dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name);
14513431Scarlsonj 
14523431Scarlsonj 	/* add to front of list */
14533431Scarlsonj 	insque(plp, &dsmp->dsm_recv_pkt_list);
14540Sstevel@tonic-gate }
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate /*
14573431Scarlsonj  * next_retransmission(): computes the number of seconds until the next
14583431Scarlsonj  *			  retransmission, based on the algorithms in RFCs 2131
14593431Scarlsonj  *			  3315.
14600Sstevel@tonic-gate  *
14613431Scarlsonj  *   input: dhcp_smach_t *: state machine that needs a new timer
14623431Scarlsonj  *	    boolean_t: B_TRUE if this is the first time sending the message
14633431Scarlsonj  *	    boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2)
14643431Scarlsonj  *  output: none
14650Sstevel@tonic-gate  */
14660Sstevel@tonic-gate 
14673431Scarlsonj static void
14683431Scarlsonj next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send,
14693431Scarlsonj     boolean_t positive_only)
14700Sstevel@tonic-gate {
14713431Scarlsonj 	uint32_t timeout_ms;
14723431Scarlsonj 
14733431Scarlsonj 	if (dsmp->dsm_isv6) {
14743431Scarlsonj 		double randval;
14753431Scarlsonj 
14763431Scarlsonj 		/*
14773431Scarlsonj 		 * The RFC specifies 0 to 10% jitter for the initial
14783431Scarlsonj 		 * solicitation, and plus or minus 10% jitter for all others.
14793431Scarlsonj 		 * This works out to 100 milliseconds on the shortest timer we
14803431Scarlsonj 		 * use.
14813431Scarlsonj 		 */
14823431Scarlsonj 		if (positive_only)
14833431Scarlsonj 			randval = drand48() / 10.0;
14843431Scarlsonj 		else
14853431Scarlsonj 			randval = (drand48() - 0.5) / 5.0;
14863431Scarlsonj 
14873431Scarlsonj 		/* The RFC specifies doubling *after* the first transmission */
14883431Scarlsonj 		timeout_ms = dsmp->dsm_send_timeout;
14893431Scarlsonj 		if (!first_send)
14903431Scarlsonj 			timeout_ms *= 2;
14913431Scarlsonj 		timeout_ms += (int)(randval * dsmp->dsm_send_timeout);
14923431Scarlsonj 
14933431Scarlsonj 		/* This checks the MRT (maximum retransmission time) */
14943431Scarlsonj 		if (dsmp->dsm_send_tcenter != 0 &&
14953431Scarlsonj 		    timeout_ms > dsmp->dsm_send_tcenter) {
14963431Scarlsonj 			timeout_ms = dsmp->dsm_send_tcenter +
14973431Scarlsonj 			    (uint_t)(randval * dsmp->dsm_send_tcenter);
14983431Scarlsonj 		}
14993431Scarlsonj 
15003431Scarlsonj 		dsmp->dsm_send_timeout = timeout_ms;
15013431Scarlsonj 	} else {
15023431Scarlsonj 		if (dsmp->dsm_state == RENEWING ||
15033431Scarlsonj 		    dsmp->dsm_state == REBINDING) {
15043431Scarlsonj 			monosec_t mono;
15053431Scarlsonj 
15063431Scarlsonj 			timeout_ms = dsmp->dsm_state == RENEWING ?
15073431Scarlsonj 			    dsmp->dsm_leases->dl_t2.dt_start :
15083431Scarlsonj 			    dsmp->dsm_leases->dl_lifs->lif_expire.dt_start;
15093431Scarlsonj 			timeout_ms += dsmp->dsm_curstart_monosec;
15103431Scarlsonj 			mono = monosec();
15113431Scarlsonj 			if (mono > timeout_ms)
15123431Scarlsonj 				timeout_ms = 0;
15133431Scarlsonj 			else
15143431Scarlsonj 				timeout_ms -= mono;
15153431Scarlsonj 			timeout_ms *= MILLISEC / 2;
15163431Scarlsonj 		} else {
15173431Scarlsonj 			/*
15183431Scarlsonj 			 * Start at 4, and increase by a factor of 2 up to 64.
15193431Scarlsonj 			 */
15203431Scarlsonj 			if (first_send) {
15213431Scarlsonj 				timeout_ms = 4 * MILLISEC;
15223431Scarlsonj 			} else {
15233431Scarlsonj 				timeout_ms = MIN(dsmp->dsm_send_tcenter << 1,
15243431Scarlsonj 				    64 * MILLISEC);
15253431Scarlsonj 			}
15263431Scarlsonj 		}
15273431Scarlsonj 
15283431Scarlsonj 		dsmp->dsm_send_tcenter = timeout_ms;
15293431Scarlsonj 
15303431Scarlsonj 		/*
15313431Scarlsonj 		 * At each iteration, jitter the timeout by some fraction of a
15323431Scarlsonj 		 * second.
15333431Scarlsonj 		 */
15343431Scarlsonj 		dsmp->dsm_send_timeout = timeout_ms +
15353431Scarlsonj 		    ((lrand48() % (2 * MILLISEC)) - MILLISEC);
15363431Scarlsonj 	}
15373431Scarlsonj }
15380Sstevel@tonic-gate 
15393431Scarlsonj /*
15403431Scarlsonj  * dhcp_ip_default(): open and bind the default IP sockets used for I/O and
15413431Scarlsonj  *		      interface control.
15423431Scarlsonj  *
15433431Scarlsonj  *   input: none
15443431Scarlsonj  *  output: B_TRUE on success
15453431Scarlsonj  */
15463431Scarlsonj 
15473431Scarlsonj boolean_t
15483431Scarlsonj dhcp_ip_default(void)
15493431Scarlsonj {
1550*5123Scarlsonj 	int on = 1;
15513431Scarlsonj 
15523431Scarlsonj 	if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
15533431Scarlsonj 		dhcpmsg(MSG_ERR,
15543431Scarlsonj 		    "dhcp_ip_default: unable to create IPv4 socket");
15553431Scarlsonj 		return (B_FALSE);
15563431Scarlsonj 	}
15573431Scarlsonj 
15583431Scarlsonj 	if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
15593431Scarlsonj 	    sizeof (on)) == -1) {
15603431Scarlsonj 		dhcpmsg(MSG_ERR,
15613431Scarlsonj 		    "dhcp_ip_default: unable to enable IP_RECVDSTADDR");
15623431Scarlsonj 		return (B_FALSE);
15633431Scarlsonj 	}
15643431Scarlsonj 
15653431Scarlsonj 	if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) ==
15663431Scarlsonj 	    -1) {
15673431Scarlsonj 		dhcpmsg(MSG_ERR,
15683431Scarlsonj 		    "dhcp_ip_default: unable to enable IP_RECVIF");
15693431Scarlsonj 		return (B_FALSE);
15703431Scarlsonj 	}
15710Sstevel@tonic-gate 
15723431Scarlsonj 	if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) {
15733431Scarlsonj 		dhcpmsg(MSG_ERROR,
15743431Scarlsonj 		    "dhcp_ip_default: unable to bind IPv4 socket to port %d",
15753431Scarlsonj 		    IPPORT_BOOTPC);
15763431Scarlsonj 		return (B_FALSE);
15773431Scarlsonj 	}
15783431Scarlsonj 
15793431Scarlsonj 	if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_common,
15803431Scarlsonj 	    NULL) == -1) {
15813431Scarlsonj 		dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
15823431Scarlsonj 		    "receive IPv4 broadcasts");
15833431Scarlsonj 		return (B_FALSE);
15843431Scarlsonj 	}
15853431Scarlsonj 
15863431Scarlsonj 	if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
15873431Scarlsonj 		dhcpmsg(MSG_ERR,
15883431Scarlsonj 		    "dhcp_ip_default: unable to create IPv6 socket");
15893431Scarlsonj 		return (B_FALSE);
15903431Scarlsonj 	}
15913431Scarlsonj 
15923431Scarlsonj 	if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
15933431Scarlsonj 	    sizeof (on)) == -1) {
15943431Scarlsonj 		dhcpmsg(MSG_ERR,
15953431Scarlsonj 		    "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO");
15963431Scarlsonj 		return (B_FALSE);
15973431Scarlsonj 	}
15983431Scarlsonj 
15993431Scarlsonj 	if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) {
16003431Scarlsonj 		dhcpmsg(MSG_ERROR,
16013431Scarlsonj 		    "dhcp_ip_default: unable to bind IPv6 socket to port %d",
16023431Scarlsonj 		    IPPORT_DHCPV6C);
16033431Scarlsonj 		return (B_FALSE);
16043431Scarlsonj 	}
16053431Scarlsonj 
16063431Scarlsonj 	if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_common,
16073431Scarlsonj 	    NULL) == -1) {
16083431Scarlsonj 		dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
16093431Scarlsonj 		    "receive IPv6 packets");
16103431Scarlsonj 		return (B_FALSE);
16113431Scarlsonj 	}
16123431Scarlsonj 
16133431Scarlsonj 	return (B_TRUE);
16140Sstevel@tonic-gate }
1615