17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5d04ccbb3Scarlsonj  * Common Development and Distribution License (the "License").
6d04ccbb3Scarlsonj  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22e11c3f44Smeem  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24*b31320a7SChris Fraire  * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <string.h>
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
297c478bd9Sstevel@tonic-gate #include <stdlib.h>
307c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
317c478bd9Sstevel@tonic-gate #include <stddef.h>
327c478bd9Sstevel@tonic-gate #include <assert.h>
33d04ccbb3Scarlsonj #include <search.h>
34d04ccbb3Scarlsonj #include <alloca.h>
35d04ccbb3Scarlsonj #include <limits.h>
36d04ccbb3Scarlsonj #include <stropts.h>
37d04ccbb3Scarlsonj #include <netinet/dhcp6.h>
38d04ccbb3Scarlsonj #include <arpa/inet.h>
39d04ccbb3Scarlsonj #include <sys/sysmacros.h>
40d04ccbb3Scarlsonj #include <sys/sockio.h>
41d04ccbb3Scarlsonj #include <inet/ip6_asp.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include "states.h"
447c478bd9Sstevel@tonic-gate #include "interface.h"
457c478bd9Sstevel@tonic-gate #include "agent.h"
467c478bd9Sstevel@tonic-gate #include "packet.h"
477c478bd9Sstevel@tonic-gate #include "util.h"
487c478bd9Sstevel@tonic-gate 
49d04ccbb3Scarlsonj int v6_sock_fd = -1;
50d04ccbb3Scarlsonj int v4_sock_fd = -1;
51d04ccbb3Scarlsonj 
52d04ccbb3Scarlsonj const in6_addr_t ipv6_all_dhcp_relay_and_servers = {
53d04ccbb3Scarlsonj 	0xff, 0x02, 0x00, 0x00,
54d04ccbb3Scarlsonj 	0x00, 0x00, 0x00, 0x00,
55d04ccbb3Scarlsonj 	0x00, 0x00, 0x00, 0x00,
56d04ccbb3Scarlsonj 	0x00, 0x01, 0x00, 0x02
57d04ccbb3Scarlsonj };
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate /*
60d04ccbb3Scarlsonj  * We have our own version of this constant because dhcpagent is compiled with
61d04ccbb3Scarlsonj  * -lxnet.
627c478bd9Sstevel@tonic-gate  */
63d04ccbb3Scarlsonj const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT;
647c478bd9Sstevel@tonic-gate 
65d04ccbb3Scarlsonj static void 	retransmit(iu_tq_t *, void *);
66d04ccbb3Scarlsonj static void	next_retransmission(dhcp_smach_t *, boolean_t, boolean_t);
67d04ccbb3Scarlsonj static boolean_t send_pkt_internal(dhcp_smach_t *);
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
70d04ccbb3Scarlsonj  * pkt_send_type(): returns an integer representing the packet's type; only
71d04ccbb3Scarlsonj  *		    for use with outbound packets.
727c478bd9Sstevel@tonic-gate  *
73d04ccbb3Scarlsonj  *   input: dhcp_pkt_t *: the packet to examine
747c478bd9Sstevel@tonic-gate  *  output: uchar_t: the packet type (0 if unknown)
757c478bd9Sstevel@tonic-gate  */
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate static uchar_t
pkt_send_type(const dhcp_pkt_t * dpkt)78d04ccbb3Scarlsonj pkt_send_type(const dhcp_pkt_t *dpkt)
797c478bd9Sstevel@tonic-gate {
80d04ccbb3Scarlsonj 	const uchar_t *option;
81d04ccbb3Scarlsonj 
82d04ccbb3Scarlsonj 	if (dpkt->pkt_isv6)
83d04ccbb3Scarlsonj 		return (((const dhcpv6_message_t *)dpkt->pkt)->d6m_msg_type);
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	/*
867c478bd9Sstevel@tonic-gate 	 * this is a little dirty but it should get the job done.
877c478bd9Sstevel@tonic-gate 	 * assumes that the type is in the statically allocated part
887c478bd9Sstevel@tonic-gate 	 * of the options field.
897c478bd9Sstevel@tonic-gate 	 */
907c478bd9Sstevel@tonic-gate 
91d04ccbb3Scarlsonj 	option = dpkt->pkt->options;
92d04ccbb3Scarlsonj 	for (;;) {
93d04ccbb3Scarlsonj 		if (*option == CD_PAD) {
94d04ccbb3Scarlsonj 			option++;
95d04ccbb3Scarlsonj 			continue;
96d04ccbb3Scarlsonj 		}
97d04ccbb3Scarlsonj 		if (*option == CD_END ||
98d04ccbb3Scarlsonj 		    option + 2 - dpkt->pkt->options >=
99d04ccbb3Scarlsonj 		    sizeof (dpkt->pkt->options))
1007c478bd9Sstevel@tonic-gate 			return (0);
101d04ccbb3Scarlsonj 		if (*option == CD_DHCP_TYPE)
102d04ccbb3Scarlsonj 			break;
1037c478bd9Sstevel@tonic-gate 		option++;
104d04ccbb3Scarlsonj 		option += *option + 1;
1057c478bd9Sstevel@tonic-gate 	}
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	return (option[2]);
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate 
110d04ccbb3Scarlsonj /*
111d04ccbb3Scarlsonj  * pkt_recv_type(): returns an integer representing the packet's type; only
112d04ccbb3Scarlsonj  *		    for use with inbound packets.
113d04ccbb3Scarlsonj  *
114d04ccbb3Scarlsonj  *   input: dhcp_pkt_t *: the packet to examine
115d04ccbb3Scarlsonj  *  output: uchar_t: the packet type (0 if unknown)
116d04ccbb3Scarlsonj  */
117d04ccbb3Scarlsonj 
118d04ccbb3Scarlsonj uchar_t
pkt_recv_type(const PKT_LIST * plp)119d04ccbb3Scarlsonj pkt_recv_type(const PKT_LIST *plp)
120d04ccbb3Scarlsonj {
121d04ccbb3Scarlsonj 	if (plp->isv6)
122d04ccbb3Scarlsonj 		return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type);
123d04ccbb3Scarlsonj 	else if (plp->opts[CD_DHCP_TYPE] != NULL)
124d04ccbb3Scarlsonj 		return (plp->opts[CD_DHCP_TYPE]->value[0]);
125d04ccbb3Scarlsonj 	else
126d04ccbb3Scarlsonj 		return (0);
127d04ccbb3Scarlsonj }
128d04ccbb3Scarlsonj 
129d04ccbb3Scarlsonj /*
130d04ccbb3Scarlsonj  * pkt_get_xid(): returns transaction ID from a DHCP packet.
131d04ccbb3Scarlsonj  *
132d04ccbb3Scarlsonj  *   input: const PKT *: the packet to examine
133d04ccbb3Scarlsonj  *  output: uint_t: the transaction ID (0 if unknown)
134d04ccbb3Scarlsonj  */
135d04ccbb3Scarlsonj 
136d04ccbb3Scarlsonj uint_t
pkt_get_xid(const PKT * pkt,boolean_t isv6)137d04ccbb3Scarlsonj pkt_get_xid(const PKT *pkt, boolean_t isv6)
138d04ccbb3Scarlsonj {
139d04ccbb3Scarlsonj 	if (pkt == NULL)
140d04ccbb3Scarlsonj 		return (0);
141d04ccbb3Scarlsonj 	if (isv6)
142d04ccbb3Scarlsonj 		return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt));
143d04ccbb3Scarlsonj 	else
144d04ccbb3Scarlsonj 		return (pkt->xid);
145d04ccbb3Scarlsonj }
146d04ccbb3Scarlsonj 
1477c478bd9Sstevel@tonic-gate /*
1487c478bd9Sstevel@tonic-gate  * init_pkt(): initializes and returns a packet of a given type
1497c478bd9Sstevel@tonic-gate  *
150d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine that will send the packet
1517c478bd9Sstevel@tonic-gate  *	    uchar_t: the packet type (DHCP message type)
152d04ccbb3Scarlsonj  *  output: dhcp_pkt_t *: a pointer to the initialized packet; may be NULL
1537c478bd9Sstevel@tonic-gate  */
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate dhcp_pkt_t *
init_pkt(dhcp_smach_t * dsmp,uchar_t type)156d04ccbb3Scarlsonj init_pkt(dhcp_smach_t *dsmp, uchar_t type)
1577c478bd9Sstevel@tonic-gate {
158d04ccbb3Scarlsonj 	dhcp_pkt_t	*dpkt = &dsmp->dsm_send_pkt;
159d04ccbb3Scarlsonj 	dhcp_lif_t	*lif = dsmp->dsm_lif;
160d04ccbb3Scarlsonj 	dhcp_pif_t	*pif = lif->lif_pif;
161e704a8f2Smeem 	uint_t		mtu = lif->lif_max;
1627c478bd9Sstevel@tonic-gate 	uint32_t	xid;
163d04ccbb3Scarlsonj 	boolean_t	isv6;
1647c478bd9Sstevel@tonic-gate 
165d04ccbb3Scarlsonj 	dpkt->pkt_isv6 = isv6 = pif->pif_isv6;
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	/*
168e704a8f2Smeem 	 * Since multiple dhcp leases may be maintained over the same pif
169e704a8f2Smeem 	 * (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
170d04ccbb3Scarlsonj 	 *
171d04ccbb3Scarlsonj 	 * Note that transaction ID zero is intentionally never assigned.
172d04ccbb3Scarlsonj 	 * That's used to represent "no ID."  Also note that transaction IDs
173d04ccbb3Scarlsonj 	 * are only 24 bits long in DHCPv6.
1747c478bd9Sstevel@tonic-gate 	 */
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	do {
1777c478bd9Sstevel@tonic-gate 		xid = mrand48();
178d04ccbb3Scarlsonj 		if (isv6)
179d04ccbb3Scarlsonj 			xid &= 0xFFFFFF;
180d04ccbb3Scarlsonj 	} while (xid == 0 ||
181d04ccbb3Scarlsonj 	    lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL);
182d04ccbb3Scarlsonj 
183d04ccbb3Scarlsonj 	if (isv6) {
184d04ccbb3Scarlsonj 		dhcpv6_message_t *v6;
185d04ccbb3Scarlsonj 
186d04ccbb3Scarlsonj 		if (mtu != dpkt->pkt_max_len &&
187d04ccbb3Scarlsonj 		    (v6 = realloc(dpkt->pkt, mtu)) != NULL) {
188d04ccbb3Scarlsonj 			/* LINTED: alignment known to be correct */
189d04ccbb3Scarlsonj 			dpkt->pkt = (PKT *)v6;
190d04ccbb3Scarlsonj 			dpkt->pkt_max_len = mtu;
191d04ccbb3Scarlsonj 		}
192d04ccbb3Scarlsonj 
193d04ccbb3Scarlsonj 		if (sizeof (*v6) > dpkt->pkt_max_len) {
194d04ccbb3Scarlsonj 			dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u",
195d04ccbb3Scarlsonj 			    mtu);
196d04ccbb3Scarlsonj 			return (NULL);
197d04ccbb3Scarlsonj 		}
198d04ccbb3Scarlsonj 
199d04ccbb3Scarlsonj 		v6 = (dhcpv6_message_t *)dpkt->pkt;
200d04ccbb3Scarlsonj 		dpkt->pkt_cur_len = sizeof (*v6);
2017c478bd9Sstevel@tonic-gate 
202d04ccbb3Scarlsonj 		(void) memset(v6, 0, dpkt->pkt_max_len);
2037c478bd9Sstevel@tonic-gate 
204d04ccbb3Scarlsonj 		v6->d6m_msg_type = type;
205d04ccbb3Scarlsonj 		DHCPV6_SET_TRANSID(v6, xid);
206d04ccbb3Scarlsonj 
207d04ccbb3Scarlsonj 		if (dsmp->dsm_cidlen > 0 &&
208d04ccbb3Scarlsonj 		    add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid,
209d04ccbb3Scarlsonj 		    dsmp->dsm_cidlen) == NULL) {
210d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
211d04ccbb3Scarlsonj 			    "init_pkt: cannot insert client ID");
212d04ccbb3Scarlsonj 			return (NULL);
213d04ccbb3Scarlsonj 		}
214d04ccbb3Scarlsonj 
215d04ccbb3Scarlsonj 		/* For v6, time starts with the creation of a transaction */
216d04ccbb3Scarlsonj 		dsmp->dsm_neg_hrtime = gethrtime();
217d04ccbb3Scarlsonj 		dsmp->dsm_newstart_monosec = monosec();
218d04ccbb3Scarlsonj 	} else {
219d04ccbb3Scarlsonj 		static uint8_t bootmagic[] = BOOTMAGIC;
220d04ccbb3Scarlsonj 		PKT *v4;
221d04ccbb3Scarlsonj 
222d04ccbb3Scarlsonj 		if (mtu != dpkt->pkt_max_len &&
223d04ccbb3Scarlsonj 		    (v4 = realloc(dpkt->pkt, mtu)) != NULL) {
224d04ccbb3Scarlsonj 			dpkt->pkt = v4;
225d04ccbb3Scarlsonj 			dpkt->pkt_max_len = mtu;
226d04ccbb3Scarlsonj 		}
227d04ccbb3Scarlsonj 
228d04ccbb3Scarlsonj 		if (offsetof(PKT, options) > dpkt->pkt_max_len) {
229d04ccbb3Scarlsonj 			dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u",
230d04ccbb3Scarlsonj 			    mtu);
231d04ccbb3Scarlsonj 			return (NULL);
232d04ccbb3Scarlsonj 		}
233d04ccbb3Scarlsonj 
234d04ccbb3Scarlsonj 		v4 = dpkt->pkt;
235d04ccbb3Scarlsonj 		dpkt->pkt_cur_len = offsetof(PKT, options);
236d04ccbb3Scarlsonj 
237d04ccbb3Scarlsonj 		(void) memset(v4, 0, dpkt->pkt_max_len);
238d04ccbb3Scarlsonj 		(void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic));
239d04ccbb3Scarlsonj 		if (pif->pif_hwlen <= sizeof (v4->chaddr)) {
240d04ccbb3Scarlsonj 			v4->hlen  = pif->pif_hwlen;
241d04ccbb3Scarlsonj 			(void) memcpy(v4->chaddr, pif->pif_hwaddr,
242d04ccbb3Scarlsonj 			    pif->pif_hwlen);
243d04ccbb3Scarlsonj 		} else {
244d04ccbb3Scarlsonj 			/*
245d04ccbb3Scarlsonj 			 * The mac address does not fit in the chaddr
246d04ccbb3Scarlsonj 			 * field, thus it can not be sent to the server,
247d04ccbb3Scarlsonj 			 * thus server can not unicast the reply. Per
248d04ccbb3Scarlsonj 			 * RFC 2131 4.4.1, client can set this bit in
249d04ccbb3Scarlsonj 			 * DISCOVER/REQUEST. If the client is already
250e704a8f2Smeem 			 * in a bound state, do not set this bit, as it
251e704a8f2Smeem 			 * can respond to unicast responses from server
252e704a8f2Smeem 			 * using the 'ciaddr' address.
253d04ccbb3Scarlsonj 			 */
254e704a8f2Smeem 			if (type == DISCOVER || (type == REQUEST &&
255e704a8f2Smeem 			    !is_bound_state(dsmp->dsm_state)))
256d04ccbb3Scarlsonj 				v4->flags = htons(BCAST_MASK);
257d04ccbb3Scarlsonj 		}
258d04ccbb3Scarlsonj 
259d04ccbb3Scarlsonj 		v4->xid   = xid;
260d04ccbb3Scarlsonj 		v4->op    = BOOTREQUEST;
261d04ccbb3Scarlsonj 		v4->htype = pif->pif_hwtype;
262d04ccbb3Scarlsonj 
263d04ccbb3Scarlsonj 		if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) {
264d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
265d04ccbb3Scarlsonj 			    "init_pkt: cannot set DHCP packet type");
266d04ccbb3Scarlsonj 			return (NULL);
267d04ccbb3Scarlsonj 		}
268d04ccbb3Scarlsonj 
269d04ccbb3Scarlsonj 		if (dsmp->dsm_cidlen > 0 &&
270d04ccbb3Scarlsonj 		    add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid,
271d04ccbb3Scarlsonj 		    dsmp->dsm_cidlen) == NULL) {
272d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
273d04ccbb3Scarlsonj 			    "init_pkt: cannot insert client ID");
274d04ccbb3Scarlsonj 			return (NULL);
275d04ccbb3Scarlsonj 		}
276d04ccbb3Scarlsonj 	}
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	return (dpkt);
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate 
281d04ccbb3Scarlsonj /*
282d04ccbb3Scarlsonj  * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t
283d04ccbb3Scarlsonj  *
284d04ccbb3Scarlsonj  *   input: dhcp_pkt_t *: the packet to remove the option from
285d04ccbb3Scarlsonj  *	    uint_t: the type of option being added
286d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
287d04ccbb3Scarlsonj  *    note: currently does not work with DHCPv6 suboptions, or to remove
288d04ccbb3Scarlsonj  *	    arbitrary option instances.
289d04ccbb3Scarlsonj  */
290d04ccbb3Scarlsonj 
291d04ccbb3Scarlsonj boolean_t
remove_pkt_opt(dhcp_pkt_t * dpkt,uint_t opt_type)292d04ccbb3Scarlsonj remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type)
293d04ccbb3Scarlsonj {
294d04ccbb3Scarlsonj 	uchar_t		*raw_pkt, *raw_end, *next;
295d04ccbb3Scarlsonj 	uint_t		len;
296d04ccbb3Scarlsonj 
297d04ccbb3Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
298d04ccbb3Scarlsonj 	raw_end = raw_pkt + dpkt->pkt_cur_len;
299d04ccbb3Scarlsonj 	if (dpkt->pkt_isv6) {
300d04ccbb3Scarlsonj 		dhcpv6_option_t d6o;
301d04ccbb3Scarlsonj 
302d04ccbb3Scarlsonj 		raw_pkt += sizeof (dhcpv6_message_t);
303d04ccbb3Scarlsonj 
304d04ccbb3Scarlsonj 		opt_type = htons(opt_type);
305d04ccbb3Scarlsonj 		while (raw_pkt + sizeof (d6o) <= raw_end) {
306d04ccbb3Scarlsonj 			(void) memcpy(&d6o, raw_pkt, sizeof (d6o));
307d04ccbb3Scarlsonj 			len = ntohs(d6o.d6o_len) + sizeof (d6o);
308d04ccbb3Scarlsonj 			if (len > raw_end - raw_pkt)
309d04ccbb3Scarlsonj 				break;
310d04ccbb3Scarlsonj 			next = raw_pkt + len;
311d04ccbb3Scarlsonj 			if (d6o.d6o_code == opt_type) {
312d04ccbb3Scarlsonj 				if (next < raw_end) {
313d04ccbb3Scarlsonj 					(void) memmove(raw_pkt, next,
314d04ccbb3Scarlsonj 					    raw_end - next);
315d04ccbb3Scarlsonj 				}
316d04ccbb3Scarlsonj 				dpkt->pkt_cur_len -= len;
317d04ccbb3Scarlsonj 				return (B_TRUE);
318d04ccbb3Scarlsonj 			}
319d04ccbb3Scarlsonj 			raw_pkt = next;
320d04ccbb3Scarlsonj 		}
321d04ccbb3Scarlsonj 	} else {
322d04ccbb3Scarlsonj 		uchar_t *pstart, *padrun;
323d04ccbb3Scarlsonj 
324d04ccbb3Scarlsonj 		raw_pkt += offsetof(PKT, options);
325d04ccbb3Scarlsonj 		pstart = raw_pkt;
326d04ccbb3Scarlsonj 
327d04ccbb3Scarlsonj 		if (opt_type == CD_END || opt_type == CD_PAD)
328d04ccbb3Scarlsonj 			return (B_FALSE);
329d04ccbb3Scarlsonj 
330d04ccbb3Scarlsonj 		padrun = NULL;
331d04ccbb3Scarlsonj 		while (raw_pkt + 1 <= raw_end) {
332d04ccbb3Scarlsonj 			if (*raw_pkt == CD_END)
333d04ccbb3Scarlsonj 				break;
334d04ccbb3Scarlsonj 			if (*raw_pkt == CD_PAD) {
335d04ccbb3Scarlsonj 				if (padrun == NULL)
336d04ccbb3Scarlsonj 					padrun = raw_pkt;
337d04ccbb3Scarlsonj 				raw_pkt++;
338d04ccbb3Scarlsonj 				continue;
339d04ccbb3Scarlsonj 			}
340d04ccbb3Scarlsonj 			if (raw_pkt + 2 > raw_end)
341d04ccbb3Scarlsonj 				break;
342d04ccbb3Scarlsonj 			len = raw_pkt[1];
343d04ccbb3Scarlsonj 			if (len > raw_end - raw_pkt || len < 2)
344d04ccbb3Scarlsonj 				break;
345d04ccbb3Scarlsonj 			next = raw_pkt + len;
346d04ccbb3Scarlsonj 			if (*raw_pkt == opt_type) {
347d04ccbb3Scarlsonj 				if (next < raw_end) {
348d04ccbb3Scarlsonj 					int toadd = (4 + ((next-pstart)&3) -
349d04ccbb3Scarlsonj 					    ((raw_pkt-pstart)&3)) & 3;
350d04ccbb3Scarlsonj 					int torem = 4 - toadd;
351d04ccbb3Scarlsonj 
352d04ccbb3Scarlsonj 					if (torem != 4 && padrun != NULL &&
353d04ccbb3Scarlsonj 					    (raw_pkt - padrun) >= torem) {
354d04ccbb3Scarlsonj 						raw_pkt -= torem;
355d04ccbb3Scarlsonj 						dpkt->pkt_cur_len -= torem;
356d04ccbb3Scarlsonj 					} else if (toadd > 0) {
357d04ccbb3Scarlsonj 						(void) memset(raw_pkt, CD_PAD,
358d04ccbb3Scarlsonj 						    toadd);
359d04ccbb3Scarlsonj 						raw_pkt += toadd;
360d04ccbb3Scarlsonj 						/* max is not an issue here */
361d04ccbb3Scarlsonj 						dpkt->pkt_cur_len += toadd;
362d04ccbb3Scarlsonj 					}
363d04ccbb3Scarlsonj 					if (raw_pkt != next) {
364d04ccbb3Scarlsonj 						(void) memmove(raw_pkt, next,
365d04ccbb3Scarlsonj 						    raw_end - next);
366d04ccbb3Scarlsonj 					}
367d04ccbb3Scarlsonj 				}
368d04ccbb3Scarlsonj 				dpkt->pkt_cur_len -= len;
369d04ccbb3Scarlsonj 				return (B_TRUE);
370d04ccbb3Scarlsonj 			}
371d04ccbb3Scarlsonj 			padrun = NULL;
372d04ccbb3Scarlsonj 			raw_pkt = next;
373d04ccbb3Scarlsonj 		}
374d04ccbb3Scarlsonj 	}
375d04ccbb3Scarlsonj 	return (B_FALSE);
376d04ccbb3Scarlsonj }
377d04ccbb3Scarlsonj 
378d04ccbb3Scarlsonj /*
379d04ccbb3Scarlsonj  * update_v6opt_len(): updates the length field of a DHCPv6 option.
380d04ccbb3Scarlsonj  *
381d04ccbb3Scarlsonj  *   input: dhcpv6_option_t *: option to be updated
382d04ccbb3Scarlsonj  *	    int: number of octets to add or subtract
383d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
384d04ccbb3Scarlsonj  */
385d04ccbb3Scarlsonj 
386d04ccbb3Scarlsonj boolean_t
update_v6opt_len(dhcpv6_option_t * opt,int adjust)387d04ccbb3Scarlsonj update_v6opt_len(dhcpv6_option_t *opt, int adjust)
388d04ccbb3Scarlsonj {
389d04ccbb3Scarlsonj 	dhcpv6_option_t optval;
390d04ccbb3Scarlsonj 
391d04ccbb3Scarlsonj 	(void) memcpy(&optval, opt, sizeof (optval));
392d04ccbb3Scarlsonj 	adjust += ntohs(optval.d6o_len);
393d04ccbb3Scarlsonj 	if (adjust < 0 || adjust > UINT16_MAX) {
394d04ccbb3Scarlsonj 		return (B_FALSE);
395d04ccbb3Scarlsonj 	} else {
396d04ccbb3Scarlsonj 		optval.d6o_len = htons(adjust);
397d04ccbb3Scarlsonj 		(void) memcpy(opt, &optval, sizeof (optval));
398d04ccbb3Scarlsonj 		return (B_TRUE);
399d04ccbb3Scarlsonj 	}
400d04ccbb3Scarlsonj }
401d04ccbb3Scarlsonj 
4027c478bd9Sstevel@tonic-gate /*
4037c478bd9Sstevel@tonic-gate  * add_pkt_opt(): adds an option to a dhcp_pkt_t
4047c478bd9Sstevel@tonic-gate  *
4057c478bd9Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
406d04ccbb3Scarlsonj  *	    uint_t: the type of option being added
4077c478bd9Sstevel@tonic-gate  *	    const void *: the value of that option
408d04ccbb3Scarlsonj  *	    uint_t: the length of the value of the option
409d04ccbb3Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
4107c478bd9Sstevel@tonic-gate  */
4117c478bd9Sstevel@tonic-gate 
412d04ccbb3Scarlsonj void *
add_pkt_opt(dhcp_pkt_t * dpkt,uint_t opt_type,const void * opt_val,uint_t opt_len)413d04ccbb3Scarlsonj add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val,
414d04ccbb3Scarlsonj     uint_t opt_len)
4157c478bd9Sstevel@tonic-gate {
416d04ccbb3Scarlsonj 	uchar_t		*raw_pkt;
417*b31320a7SChris Fraire 	size_t		req_len;
418d04ccbb3Scarlsonj 	void		*optr;
419d04ccbb3Scarlsonj 
420d04ccbb3Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
421d04ccbb3Scarlsonj 	optr = raw_pkt + dpkt->pkt_cur_len;
422d04ccbb3Scarlsonj 	if (dpkt->pkt_isv6) {
423*b31320a7SChris Fraire 		req_len = opt_len + sizeof (dhcpv6_option_t);
424d04ccbb3Scarlsonj 
425d04ccbb3Scarlsonj 		if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
426d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
427d04ccbb3Scarlsonj 			    "add_pkt_opt: not enough room for v6 option %u in "
428d04ccbb3Scarlsonj 			    "packet (%u + %u > %u)", opt_type,
429d04ccbb3Scarlsonj 			    dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
430d04ccbb3Scarlsonj 			return (NULL);
431d04ccbb3Scarlsonj 		}
432d04ccbb3Scarlsonj 	} else {
433*b31320a7SChris Fraire 		req_len = opt_len + DHCP_OPT_META_LEN;
434d04ccbb3Scarlsonj 
435d04ccbb3Scarlsonj 		/* CD_END and CD_PAD options don't have a length field */
436d04ccbb3Scarlsonj 		if (opt_type == CD_END || opt_type == CD_PAD) {
437d04ccbb3Scarlsonj 			req_len = 1;
438d04ccbb3Scarlsonj 		} else if (opt_val == NULL) {
439d04ccbb3Scarlsonj 			dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is "
440d04ccbb3Scarlsonj 			    "missing required value", opt_type);
441d04ccbb3Scarlsonj 			return (NULL);
442d04ccbb3Scarlsonj 		}
4437c478bd9Sstevel@tonic-gate 
444d04ccbb3Scarlsonj 		if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) {
445d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
446d04ccbb3Scarlsonj 			    "add_pkt_opt: not enough room for v4 option %u in "
447d04ccbb3Scarlsonj 			    "packet", opt_type);
448d04ccbb3Scarlsonj 			return (NULL);
449d04ccbb3Scarlsonj 		}
450*b31320a7SChris Fraire 	}
4517c478bd9Sstevel@tonic-gate 
452*b31320a7SChris Fraire 	req_len = encode_dhcp_opt(&raw_pkt[dpkt->pkt_cur_len], dpkt->pkt_isv6,
453*b31320a7SChris Fraire 	    opt_type, opt_val, opt_len);
454*b31320a7SChris Fraire 	dpkt->pkt_cur_len += req_len;
455d04ccbb3Scarlsonj 
456*b31320a7SChris Fraire 	return (optr);
457*b31320a7SChris Fraire }
458*b31320a7SChris Fraire 
459*b31320a7SChris Fraire /*
460*b31320a7SChris Fraire  * encode_dhcp_opt(): sets the fields of an allocated DHCP option buffer
461*b31320a7SChris Fraire  *
462*b31320a7SChris Fraire  *   input: void *: the buffer allocated for enough space for
463*b31320a7SChris Fraire  *		    (DHCPv6) dhcpv6_option_t and value, or for
464*b31320a7SChris Fraire  *		    (DHCPv4) opt_type + length + value (length/value are
465*b31320a7SChris Fraire  *		    skipped for CD_END or CD_PAD);
466*b31320a7SChris Fraire  *	    boolean_t: a value indicating whether DHCPv6 or not;
467*b31320a7SChris Fraire  *	    uint_t: the type of option being added;
468*b31320a7SChris Fraire  *	    const void *: the value of that option;
469*b31320a7SChris Fraire  *	    uint_t: the length of the value of the option
470*b31320a7SChris Fraire  *  output: size_t: the number of bytes written starting at opt.
471*b31320a7SChris Fraire  */
472*b31320a7SChris Fraire 
473*b31320a7SChris Fraire size_t
encode_dhcp_opt(void * dopt,boolean_t isv6,uint_t opt_type,const void * opt_val,uint_t opt_len)474*b31320a7SChris Fraire encode_dhcp_opt(void *dopt, boolean_t isv6, uint_t opt_type,
475*b31320a7SChris Fraire     const void *opt_val, uint_t opt_len)
476*b31320a7SChris Fraire {
477*b31320a7SChris Fraire 	boolean_t do_copy_value = B_FALSE;
478*b31320a7SChris Fraire 	size_t res_len = 0;
479*b31320a7SChris Fraire 	uint8_t *pval;
480*b31320a7SChris Fraire 
481*b31320a7SChris Fraire 	if (isv6) {
482*b31320a7SChris Fraire 		dhcpv6_option_t d6o;
483*b31320a7SChris Fraire 		d6o.d6o_code = htons(opt_type);
484*b31320a7SChris Fraire 		d6o.d6o_len = htons(opt_len);
485*b31320a7SChris Fraire 		(void) memcpy(dopt, &d6o, sizeof (d6o));
486*b31320a7SChris Fraire 		res_len += sizeof (d6o);
487*b31320a7SChris Fraire 
488*b31320a7SChris Fraire 		do_copy_value = B_TRUE;
489*b31320a7SChris Fraire 	} else {
490*b31320a7SChris Fraire 		pval = (uint8_t *)dopt;
491*b31320a7SChris Fraire 		pval[res_len++] = opt_type;
492*b31320a7SChris Fraire 
493*b31320a7SChris Fraire 		if (opt_type != CD_END && opt_type != CD_PAD) {
494*b31320a7SChris Fraire 			pval[res_len++] = opt_len;
495*b31320a7SChris Fraire 			do_copy_value = B_TRUE;
496d04ccbb3Scarlsonj 		}
4977c478bd9Sstevel@tonic-gate 	}
498*b31320a7SChris Fraire 
499*b31320a7SChris Fraire 	pval = (uint8_t *)dopt + res_len;
500*b31320a7SChris Fraire 	if (do_copy_value && opt_len > 0) {
501*b31320a7SChris Fraire 		(void) memcpy(pval, opt_val, opt_len);
502*b31320a7SChris Fraire 		res_len += opt_len;
503*b31320a7SChris Fraire 	}
504*b31320a7SChris Fraire 
505*b31320a7SChris Fraire 	return (res_len);
506d04ccbb3Scarlsonj }
5077c478bd9Sstevel@tonic-gate 
508d04ccbb3Scarlsonj /*
509d04ccbb3Scarlsonj  * add_pkt_subopt(): adds an option to a dhcp_pkt_t option.  DHCPv6-specific,
510d04ccbb3Scarlsonj  *		     but could be extended to IPv4 DHCP if necessary.  Assumes
511d04ccbb3Scarlsonj  *		     that if the parent isn't a top-level option, the caller
512d04ccbb3Scarlsonj  *		     will adjust any upper-level options recursively using
513d04ccbb3Scarlsonj  *		     update_v6opt_len.
514d04ccbb3Scarlsonj  *
515d04ccbb3Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the suboption to
516d04ccbb3Scarlsonj  *	    dhcpv6_option_t *: the start of the option to that should contain
517d04ccbb3Scarlsonj  *			       it (parent)
518d04ccbb3Scarlsonj  *	    uint_t: the type of suboption being added
519d04ccbb3Scarlsonj  *	    const void *: the value of that option
520d04ccbb3Scarlsonj  *	    uint_t: the length of the value of the option
521d04ccbb3Scarlsonj  *  output: void *: pointer to the suboption that was added, or NULL on
522d04ccbb3Scarlsonj  *		    failure.
523d04ccbb3Scarlsonj  */
5247c478bd9Sstevel@tonic-gate 
525d04ccbb3Scarlsonj void *
add_pkt_subopt(dhcp_pkt_t * dpkt,dhcpv6_option_t * parentopt,uint_t opt_type,const void * opt_val,uint_t opt_len)526d04ccbb3Scarlsonj add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type,
527d04ccbb3Scarlsonj     const void *opt_val, uint_t opt_len)
528d04ccbb3Scarlsonj {
529d04ccbb3Scarlsonj 	uchar_t		*raw_pkt;
530d04ccbb3Scarlsonj 	int		req_len;
531d04ccbb3Scarlsonj 	void		*optr;
532d04ccbb3Scarlsonj 	dhcpv6_option_t d6o;
533d04ccbb3Scarlsonj 	uchar_t		*optend;
534d04ccbb3Scarlsonj 	int		olen;
535d04ccbb3Scarlsonj 
536d04ccbb3Scarlsonj 	if (!dpkt->pkt_isv6)
537d04ccbb3Scarlsonj 		return (NULL);
538d04ccbb3Scarlsonj 
539d04ccbb3Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
540d04ccbb3Scarlsonj 	req_len = opt_len + sizeof (d6o);
541d04ccbb3Scarlsonj 
542d04ccbb3Scarlsonj 	if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
543d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING,
544d04ccbb3Scarlsonj 		    "add_pkt_subopt: not enough room for v6 suboption %u in "
545d04ccbb3Scarlsonj 		    "packet (%u + %u > %u)", opt_type,
546d04ccbb3Scarlsonj 		    dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
547d04ccbb3Scarlsonj 		return (NULL);
5487c478bd9Sstevel@tonic-gate 	}
549d04ccbb3Scarlsonj 
550d04ccbb3Scarlsonj 	/*
551d04ccbb3Scarlsonj 	 * Update the parent option to include room for this option,
552d04ccbb3Scarlsonj 	 * and compute the insertion point.
553d04ccbb3Scarlsonj 	 */
554d04ccbb3Scarlsonj 	(void) memcpy(&d6o, parentopt, sizeof (d6o));
555d04ccbb3Scarlsonj 	olen = ntohs(d6o.d6o_len);
556d04ccbb3Scarlsonj 	optend = (uchar_t *)(parentopt + 1) + olen;
557d04ccbb3Scarlsonj 	olen += req_len;
558d04ccbb3Scarlsonj 	d6o.d6o_len = htons(olen);
559d04ccbb3Scarlsonj 	(void) memcpy(parentopt, &d6o, sizeof (d6o));
560d04ccbb3Scarlsonj 
561d04ccbb3Scarlsonj 	/*
562d04ccbb3Scarlsonj 	 * If there's anything at the end to move, then move it.  Also bump up
563d04ccbb3Scarlsonj 	 * the packet size.
564d04ccbb3Scarlsonj 	 */
565d04ccbb3Scarlsonj 	if (optend < raw_pkt + dpkt->pkt_cur_len) {
566d04ccbb3Scarlsonj 		(void) memmove(optend + req_len, optend,
567d04ccbb3Scarlsonj 		    (raw_pkt + dpkt->pkt_cur_len) - optend);
568d04ccbb3Scarlsonj 	}
569d04ccbb3Scarlsonj 	dpkt->pkt_cur_len += req_len;
570d04ccbb3Scarlsonj 
571d04ccbb3Scarlsonj 	/*
572d04ccbb3Scarlsonj 	 * Now format the suboption and add it in.
573d04ccbb3Scarlsonj 	 */
574d04ccbb3Scarlsonj 	optr = optend;
575d04ccbb3Scarlsonj 	d6o.d6o_code = htons(opt_type);
576d04ccbb3Scarlsonj 	d6o.d6o_len = htons(opt_len);
577d04ccbb3Scarlsonj 	(void) memcpy(optend, &d6o, sizeof (d6o));
578d04ccbb3Scarlsonj 	if (opt_len > 0)
579d04ccbb3Scarlsonj 		(void) memcpy(optend + sizeof (d6o), opt_val, opt_len);
580d04ccbb3Scarlsonj 	return (optr);
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate  * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t
5857c478bd9Sstevel@tonic-gate  *
5867c478bd9Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
587d04ccbb3Scarlsonj  *	    uint_t: the type of option being added
5887c478bd9Sstevel@tonic-gate  *	    uint16_t: the value of that option
589d04ccbb3Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
5907c478bd9Sstevel@tonic-gate  */
5917c478bd9Sstevel@tonic-gate 
592d04ccbb3Scarlsonj void *
add_pkt_opt16(dhcp_pkt_t * dpkt,uint_t opt_type,uint16_t opt_value)593d04ccbb3Scarlsonj add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value)
5947c478bd9Sstevel@tonic-gate {
595d04ccbb3Scarlsonj 	return (add_pkt_opt(dpkt, opt_type, &opt_value, 2));
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate /*
5997c478bd9Sstevel@tonic-gate  * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t
6007c478bd9Sstevel@tonic-gate  *
6017c478bd9Sstevel@tonic-gate  *   input: dhcp_pkt_t *: the packet to add the option to
602d04ccbb3Scarlsonj  *	    uint_t: the type of option being added
6037c478bd9Sstevel@tonic-gate  *	    uint32_t: the value of that option
604d04ccbb3Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
6057c478bd9Sstevel@tonic-gate  */
6067c478bd9Sstevel@tonic-gate 
607d04ccbb3Scarlsonj void *
add_pkt_opt32(dhcp_pkt_t * dpkt,uint_t opt_type,uint32_t opt_value)608d04ccbb3Scarlsonj add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value)
6097c478bd9Sstevel@tonic-gate {
610d04ccbb3Scarlsonj 	return (add_pkt_opt(dpkt, opt_type, &opt_value, 4));
6117c478bd9Sstevel@tonic-gate }
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate /*
614d04ccbb3Scarlsonj  * add_pkt_prl(): adds the parameter request option to the packet
6157c478bd9Sstevel@tonic-gate  *
616d04ccbb3Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the option to
617d04ccbb3Scarlsonj  *	    dhcp_smach_t *: state machine with request option
618d04ccbb3Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
6197c478bd9Sstevel@tonic-gate  */
6207c478bd9Sstevel@tonic-gate 
621d04ccbb3Scarlsonj void *
add_pkt_prl(dhcp_pkt_t * dpkt,dhcp_smach_t * dsmp)622d04ccbb3Scarlsonj add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
6237c478bd9Sstevel@tonic-gate {
624d04ccbb3Scarlsonj 	uint_t len;
6257c478bd9Sstevel@tonic-gate 
626d04ccbb3Scarlsonj 	if (dsmp->dsm_prllen == 0)
627d04ccbb3Scarlsonj 		return (0);
6287c478bd9Sstevel@tonic-gate 
629d04ccbb3Scarlsonj 	if (dpkt->pkt_isv6) {
630d04ccbb3Scarlsonj 		uint16_t *prl;
6317c478bd9Sstevel@tonic-gate 
632d04ccbb3Scarlsonj 		/*
633d04ccbb3Scarlsonj 		 * RFC 3315 requires that we include the option, even if we
634d04ccbb3Scarlsonj 		 * have nothing to request.
635d04ccbb3Scarlsonj 		 */
636d04ccbb3Scarlsonj 		if (dsmp->dsm_prllen == 0)
637d04ccbb3Scarlsonj 			prl = NULL;
638d04ccbb3Scarlsonj 		else
639d04ccbb3Scarlsonj 			prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t));
6407c478bd9Sstevel@tonic-gate 
641d04ccbb3Scarlsonj 		for (len = 0; len < dsmp->dsm_prllen; len++)
642d04ccbb3Scarlsonj 			prl[len] = htons(dsmp->dsm_prl[len]);
643d04ccbb3Scarlsonj 		return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl,
644d04ccbb3Scarlsonj 		    len * sizeof (uint16_t)));
645d04ccbb3Scarlsonj 	} else {
646d04ccbb3Scarlsonj 		uint8_t *prl = alloca(dsmp->dsm_prllen);
6477c478bd9Sstevel@tonic-gate 
648d04ccbb3Scarlsonj 		for (len = 0; len < dsmp->dsm_prllen; len++)
649d04ccbb3Scarlsonj 			prl[len] = dsmp->dsm_prl[len];
650d04ccbb3Scarlsonj 		return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len));
6517c478bd9Sstevel@tonic-gate 	}
6527c478bd9Sstevel@tonic-gate }
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate /*
655d04ccbb3Scarlsonj  * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR
656d04ccbb3Scarlsonj  *		  (DHCPv6) options to the packet to represent the given LIF.
6577c478bd9Sstevel@tonic-gate  *
658d04ccbb3Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the options to
659d04ccbb3Scarlsonj  *	    dhcp_lif_t *: the logical interface to represent
660d04ccbb3Scarlsonj  *	    int: status code (unused for IPv4 DHCP)
661d04ccbb3Scarlsonj  *	    const char *: message to include with status option, or NULL
662d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
6637c478bd9Sstevel@tonic-gate  */
6647c478bd9Sstevel@tonic-gate 
665d04ccbb3Scarlsonj boolean_t
add_pkt_lif(dhcp_pkt_t * dpkt,dhcp_lif_t * lif,int status,const char * msg)666d04ccbb3Scarlsonj add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg)
6677c478bd9Sstevel@tonic-gate {
668d04ccbb3Scarlsonj 	if (lif->lif_pif->pif_isv6) {
669d04ccbb3Scarlsonj 		dhcp_smach_t *dsmp;
670d04ccbb3Scarlsonj 		dhcpv6_message_t *d6m;
671d04ccbb3Scarlsonj 		dhcpv6_ia_na_t d6in;
672d04ccbb3Scarlsonj 		dhcpv6_iaaddr_t d6ia;
673d04ccbb3Scarlsonj 		uint32_t iaid;
674d04ccbb3Scarlsonj 		uint16_t *statusopt;
675d04ccbb3Scarlsonj 		dhcpv6_option_t *d6o, *d6so;
676d04ccbb3Scarlsonj 		uint_t olen;
6777c478bd9Sstevel@tonic-gate 
678d04ccbb3Scarlsonj 		/*
679d04ccbb3Scarlsonj 		 * Currently, we support just one IAID related to the primary
680d04ccbb3Scarlsonj 		 * LIF on the state machine.
681d04ccbb3Scarlsonj 		 */
682d04ccbb3Scarlsonj 		dsmp = lif->lif_lease->dl_smach;
683d04ccbb3Scarlsonj 		iaid = dsmp->dsm_lif->lif_iaid;
684d04ccbb3Scarlsonj 		iaid = htonl(iaid);
6857c478bd9Sstevel@tonic-gate 
686d04ccbb3Scarlsonj 		d6m = (dhcpv6_message_t *)dpkt->pkt;
687d04ccbb3Scarlsonj 
688d04ccbb3Scarlsonj 		/*
689d04ccbb3Scarlsonj 		 * Find or create the IA_NA needed for this LIF.  If we
690d04ccbb3Scarlsonj 		 * supported IA_TA, we'd check the IFF_TEMPORARY bit here.
691d04ccbb3Scarlsonj 		 */
692d04ccbb3Scarlsonj 		d6o = NULL;
693d04ccbb3Scarlsonj 		while ((d6o = dhcpv6_find_option(d6m + 1,
694d04ccbb3Scarlsonj 		    dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA,
695d04ccbb3Scarlsonj 		    &olen)) != NULL) {
696d04ccbb3Scarlsonj 			if (olen < sizeof (d6in))
697d04ccbb3Scarlsonj 				continue;
698d04ccbb3Scarlsonj 			(void) memcpy(&d6in, d6o, sizeof (d6in));
699d04ccbb3Scarlsonj 			if (d6in.d6in_iaid == iaid)
700d04ccbb3Scarlsonj 				break;
701d04ccbb3Scarlsonj 		}
702d04ccbb3Scarlsonj 		if (d6o == NULL) {
703d04ccbb3Scarlsonj 			d6in.d6in_iaid = iaid;
704d04ccbb3Scarlsonj 			d6in.d6in_t1 = 0;
705d04ccbb3Scarlsonj 			d6in.d6in_t2 = 0;
706d04ccbb3Scarlsonj 			d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
707d04ccbb3Scarlsonj 			    (dhcpv6_option_t *)&d6in + 1,
708d04ccbb3Scarlsonj 			    sizeof (d6in) - sizeof (*d6o));
709d04ccbb3Scarlsonj 			if (d6o == NULL)
710d04ccbb3Scarlsonj 				return (B_FALSE);
711d04ccbb3Scarlsonj 		}
7127c478bd9Sstevel@tonic-gate 
713d04ccbb3Scarlsonj 		/*
714d04ccbb3Scarlsonj 		 * Now add the IAADDR suboption for this LIF.  No need to
715d04ccbb3Scarlsonj 		 * search here, as we know that this is unique.
716d04ccbb3Scarlsonj 		 */
717d04ccbb3Scarlsonj 		d6ia.d6ia_addr = lif->lif_v6addr;
718d04ccbb3Scarlsonj 
719d04ccbb3Scarlsonj 		/*
720d04ccbb3Scarlsonj 		 * For Release and Decline, we zero out the lifetime.  For
721d04ccbb3Scarlsonj 		 * Renew and Rebind, we report the original time as the
722d04ccbb3Scarlsonj 		 * preferred and valid lifetimes.
723d04ccbb3Scarlsonj 		 */
724d04ccbb3Scarlsonj 		if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE ||
725d04ccbb3Scarlsonj 		    d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) {
726d04ccbb3Scarlsonj 			d6ia.d6ia_preflife = 0;
727d04ccbb3Scarlsonj 			d6ia.d6ia_vallife = 0;
728d04ccbb3Scarlsonj 		} else {
729d04ccbb3Scarlsonj 			d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start);
730d04ccbb3Scarlsonj 			d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start);
731d04ccbb3Scarlsonj 		}
732d04ccbb3Scarlsonj 		d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR,
733d04ccbb3Scarlsonj 		    (dhcpv6_option_t *)&d6ia + 1,
734d04ccbb3Scarlsonj 		    sizeof (d6ia) - sizeof (*d6o));
735d04ccbb3Scarlsonj 		if (d6so == NULL)
736d04ccbb3Scarlsonj 			return (B_FALSE);
737d04ccbb3Scarlsonj 
738d04ccbb3Scarlsonj 		/*
739d04ccbb3Scarlsonj 		 * Add a status code suboption to the IAADDR to tell the server
740d04ccbb3Scarlsonj 		 * why we're declining the address.  Note that we must manually
741d04ccbb3Scarlsonj 		 * update the enclosing IA_NA, as add_pkt_subopt doesn't know
742d04ccbb3Scarlsonj 		 * how to do that.
743d04ccbb3Scarlsonj 		 */
744d04ccbb3Scarlsonj 		if (status != DHCPV6_STAT_SUCCESS || msg != NULL) {
745d04ccbb3Scarlsonj 			olen = sizeof (*statusopt) +
746d04ccbb3Scarlsonj 			    (msg == NULL ? 0 : strlen(msg));
747d04ccbb3Scarlsonj 			statusopt = alloca(olen);
748d04ccbb3Scarlsonj 			*statusopt = htons(status);
749d04ccbb3Scarlsonj 			if (msg != NULL) {
750d04ccbb3Scarlsonj 				(void) memcpy((char *)(statusopt + 1), msg,
751d04ccbb3Scarlsonj 				    olen - sizeof (*statusopt));
752d04ccbb3Scarlsonj 			}
753d04ccbb3Scarlsonj 			d6so = add_pkt_subopt(dpkt, d6so,
754d04ccbb3Scarlsonj 			    DHCPV6_OPT_STATUS_CODE, statusopt, olen);
755d04ccbb3Scarlsonj 			if (d6so != NULL) {
756d04ccbb3Scarlsonj 				/*
757d04ccbb3Scarlsonj 				 * Update for length of suboption header and
758d04ccbb3Scarlsonj 				 * suboption contents.
759d04ccbb3Scarlsonj 				 */
760d04ccbb3Scarlsonj 				(void) update_v6opt_len(d6o, sizeof (*d6so) +
761d04ccbb3Scarlsonj 				    olen);
762d04ccbb3Scarlsonj 			}
763d04ccbb3Scarlsonj 		}
764d04ccbb3Scarlsonj 	} else {
765d04ccbb3Scarlsonj 		/*
766d04ccbb3Scarlsonj 		 * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option.
767d04ccbb3Scarlsonj 		 * In all other cases (RELEASE and REQUEST), we need to set
768d04ccbb3Scarlsonj 		 * ciadr.
769d04ccbb3Scarlsonj 		 */
770d04ccbb3Scarlsonj 		if (pkt_send_type(dpkt) == DECLINE) {
771d04ccbb3Scarlsonj 			if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
772d04ccbb3Scarlsonj 			    lif->lif_addr))
773d04ccbb3Scarlsonj 				return (B_FALSE);
774d04ccbb3Scarlsonj 		} else {
775d04ccbb3Scarlsonj 			dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
776d04ccbb3Scarlsonj 		}
777d04ccbb3Scarlsonj 
778d04ccbb3Scarlsonj 		/*
779d04ccbb3Scarlsonj 		 * It's not too worrisome if the message fails to fit in the
780d04ccbb3Scarlsonj 		 * packet.  The result will still be valid.
781d04ccbb3Scarlsonj 		 */
782d04ccbb3Scarlsonj 		if (msg != NULL)
783d04ccbb3Scarlsonj 			(void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
784d04ccbb3Scarlsonj 			    strlen(msg) + 1);
7857c478bd9Sstevel@tonic-gate 	}
786d04ccbb3Scarlsonj 	return (B_TRUE);
7877c478bd9Sstevel@tonic-gate }
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate /*
790d04ccbb3Scarlsonj  * free_pkt_entry(): frees a packet list list entry
7917c478bd9Sstevel@tonic-gate  *
792d04ccbb3Scarlsonj  *   input: PKT_LIST *: the packet list entry to free
7937c478bd9Sstevel@tonic-gate  *  output: void
7947c478bd9Sstevel@tonic-gate  */
795d04ccbb3Scarlsonj void
free_pkt_entry(PKT_LIST * plp)796d04ccbb3Scarlsonj free_pkt_entry(PKT_LIST *plp)
7977c478bd9Sstevel@tonic-gate {
798d04ccbb3Scarlsonj 	if (plp != NULL) {
799d04ccbb3Scarlsonj 		free(plp->pkt);
800d04ccbb3Scarlsonj 		free(plp);
801d04ccbb3Scarlsonj 	}
8027c478bd9Sstevel@tonic-gate }
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate /*
805d04ccbb3Scarlsonj  * free_pkt_list(): frees an entire packet list
8067c478bd9Sstevel@tonic-gate  *
807d04ccbb3Scarlsonj  *   input: PKT_LIST **: the packet list to free
8087c478bd9Sstevel@tonic-gate  *  output: void
8097c478bd9Sstevel@tonic-gate  */
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate void
free_pkt_list(PKT_LIST ** head)812d04ccbb3Scarlsonj free_pkt_list(PKT_LIST **head)
8137c478bd9Sstevel@tonic-gate {
814d04ccbb3Scarlsonj 	PKT_LIST *plp;
8157c478bd9Sstevel@tonic-gate 
816d04ccbb3Scarlsonj 	while ((plp = *head) != NULL) {
817d04ccbb3Scarlsonj 		remque(plp);
818d04ccbb3Scarlsonj 		free_pkt_entry(plp);
8197c478bd9Sstevel@tonic-gate 	}
8207c478bd9Sstevel@tonic-gate }
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate /*
8237c478bd9Sstevel@tonic-gate  * send_pkt_internal(): sends a packet out on an interface
8247c478bd9Sstevel@tonic-gate  *
825d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine with a packet to send
826d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise
8277c478bd9Sstevel@tonic-gate  */
8287c478bd9Sstevel@tonic-gate 
829d04ccbb3Scarlsonj static boolean_t
send_pkt_internal(dhcp_smach_t * dsmp)830d04ccbb3Scarlsonj send_pkt_internal(dhcp_smach_t *dsmp)
8317c478bd9Sstevel@tonic-gate {
8327c478bd9Sstevel@tonic-gate 	ssize_t		n_bytes;
833d04ccbb3Scarlsonj 	dhcp_lif_t	*lif = dsmp->dsm_lif;
834d04ccbb3Scarlsonj 	dhcp_pkt_t	*dpkt = &dsmp->dsm_send_pkt;
835d04ccbb3Scarlsonj 	uchar_t		ptype = pkt_send_type(dpkt);
836d04ccbb3Scarlsonj 	const char	*pkt_name;
837d04ccbb3Scarlsonj 	struct iovec	iov;
838d04ccbb3Scarlsonj 	struct msghdr	msg;
839d04ccbb3Scarlsonj 	struct cmsghdr	*cmsg;
840d04ccbb3Scarlsonj 	struct in6_pktinfo *ipi6;
841d04ccbb3Scarlsonj 	boolean_t	ismcast;
842e704a8f2Smeem 	int		msgtype;
843d04ccbb3Scarlsonj 
844d04ccbb3Scarlsonj 	/*
845d04ccbb3Scarlsonj 	 * Timer should not be running at the point we go to send a packet.
846d04ccbb3Scarlsonj 	 */
847d04ccbb3Scarlsonj 	if (dsmp->dsm_retrans_timer != -1) {
848d04ccbb3Scarlsonj 		dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit "
849d04ccbb3Scarlsonj 		    "timer on %s", dsmp->dsm_name);
850d04ccbb3Scarlsonj 		stop_pkt_retransmission(dsmp);
851d04ccbb3Scarlsonj 	}
852d04ccbb3Scarlsonj 
853d04ccbb3Scarlsonj 	pkt_name = pkt_type_to_string(ptype, dpkt->pkt_isv6);
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	/*
8567c478bd9Sstevel@tonic-gate 	 * if needed, schedule a retransmission timer, then attempt to
8577c478bd9Sstevel@tonic-gate 	 * send the packet.  if we fail, then log the error.  our
8587c478bd9Sstevel@tonic-gate 	 * return value should indicate whether or not we were
8597c478bd9Sstevel@tonic-gate 	 * successful in sending the request, independent of whether
8607c478bd9Sstevel@tonic-gate 	 * we could schedule a timer.
8617c478bd9Sstevel@tonic-gate 	 */
8627c478bd9Sstevel@tonic-gate 
863d04ccbb3Scarlsonj 	if (dsmp->dsm_send_timeout != 0) {
864d04ccbb3Scarlsonj 		if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq,
865d04ccbb3Scarlsonj 		    dsmp->dsm_send_timeout, retransmit, dsmp)) == -1)
8667c478bd9Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot "
8677c478bd9Sstevel@tonic-gate 			    "schedule retransmit timer for %s packet",
8687c478bd9Sstevel@tonic-gate 			    pkt_name);
8697c478bd9Sstevel@tonic-gate 		else
870d04ccbb3Scarlsonj 			hold_smach(dsmp);
8717c478bd9Sstevel@tonic-gate 	}
8727c478bd9Sstevel@tonic-gate 
873d04ccbb3Scarlsonj 	if (dpkt->pkt_isv6) {
874d04ccbb3Scarlsonj 		hrtime_t delta;
8757c478bd9Sstevel@tonic-gate 
876d04ccbb3Scarlsonj 		/*
877d04ccbb3Scarlsonj 		 * Convert current time into centiseconds since transaction
878d04ccbb3Scarlsonj 		 * started.  This is what DHCPv6 expects to see in the Elapsed
879d04ccbb3Scarlsonj 		 * Time option.
880d04ccbb3Scarlsonj 		 */
881d04ccbb3Scarlsonj 		delta = (gethrtime() - dsmp->dsm_neg_hrtime) /
882d04ccbb3Scarlsonj 		    (NANOSEC / 100);
883d04ccbb3Scarlsonj 		if (delta > DHCPV6_FOREVER)
884d04ccbb3Scarlsonj 			delta = DHCPV6_FOREVER;
885d04ccbb3Scarlsonj 		(void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME);
886d04ccbb3Scarlsonj 		(void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME,
887d04ccbb3Scarlsonj 		    htons(delta));
888d04ccbb3Scarlsonj 	} else {
889d04ccbb3Scarlsonj 		/*
890d04ccbb3Scarlsonj 		 * set the `pkt->secs' field depending on the type of packet.
891d04ccbb3Scarlsonj 		 * it should be zero, except in the following cases:
892d04ccbb3Scarlsonj 		 *
893d04ccbb3Scarlsonj 		 * DISCOVER:	set to the number of seconds since we started
894d04ccbb3Scarlsonj 		 *		trying to obtain a lease.
895d04ccbb3Scarlsonj 		 *
896d04ccbb3Scarlsonj 		 * INFORM:	set to the number of seconds since we started
897d04ccbb3Scarlsonj 		 *		trying to get configuration parameters.
898d04ccbb3Scarlsonj 		 *
899d04ccbb3Scarlsonj 		 * REQUEST:	if in the REQUESTING state, then same value as
900d04ccbb3Scarlsonj 		 *		DISCOVER, otherwise the number of seconds
901d04ccbb3Scarlsonj 		 *		since we started trying to obtain a lease.
902d04ccbb3Scarlsonj 		 *
903d04ccbb3Scarlsonj 		 * we also set `dsm_newstart_monosec', to the time we sent a
904d04ccbb3Scarlsonj 		 * REQUEST or DISCOVER packet, so we know the lease start
905d04ccbb3Scarlsonj 		 * time (the DISCOVER case is for handling BOOTP servers).
906d04ccbb3Scarlsonj 		 */
9077c478bd9Sstevel@tonic-gate 
908d04ccbb3Scarlsonj 		switch (ptype) {
9097c478bd9Sstevel@tonic-gate 
910d04ccbb3Scarlsonj 		case DISCOVER:
911d04ccbb3Scarlsonj 			dsmp->dsm_newstart_monosec = monosec();
912d04ccbb3Scarlsonj 			dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec -
913d04ccbb3Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime);
914d04ccbb3Scarlsonj 			dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
915d04ccbb3Scarlsonj 			break;
9167c478bd9Sstevel@tonic-gate 
917d04ccbb3Scarlsonj 		case INFORM:
918d04ccbb3Scarlsonj 			dpkt->pkt->secs = htons(monosec() -
919d04ccbb3Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime));
920d04ccbb3Scarlsonj 			break;
9217c478bd9Sstevel@tonic-gate 
922d04ccbb3Scarlsonj 		case REQUEST:
923d04ccbb3Scarlsonj 			dsmp->dsm_newstart_monosec = monosec();
924d04ccbb3Scarlsonj 
925d04ccbb3Scarlsonj 			if (dsmp->dsm_state == REQUESTING) {
926d04ccbb3Scarlsonj 				dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
927d04ccbb3Scarlsonj 				break;
928d04ccbb3Scarlsonj 			}
929d04ccbb3Scarlsonj 
930d04ccbb3Scarlsonj 			dpkt->pkt->secs = htons(monosec() -
931d04ccbb3Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime));
932d04ccbb3Scarlsonj 			break;
933d04ccbb3Scarlsonj 
934d04ccbb3Scarlsonj 		default:
935d04ccbb3Scarlsonj 			dpkt->pkt->secs = htons(0);
9367c478bd9Sstevel@tonic-gate 			break;
9377c478bd9Sstevel@tonic-gate 		}
938d04ccbb3Scarlsonj 	}
9397c478bd9Sstevel@tonic-gate 
940d04ccbb3Scarlsonj 	if (dpkt->pkt_isv6) {
941d04ccbb3Scarlsonj 		struct sockaddr_in6 sin6;
9427c478bd9Sstevel@tonic-gate 
943d04ccbb3Scarlsonj 		(void) memset(&iov, 0, sizeof (iov));
944d04ccbb3Scarlsonj 		iov.iov_base = dpkt->pkt;
945d04ccbb3Scarlsonj 		iov.iov_len = dpkt->pkt_cur_len;
9467c478bd9Sstevel@tonic-gate 
947d04ccbb3Scarlsonj 		(void) memset(&msg, 0, sizeof (msg));
948d04ccbb3Scarlsonj 		msg.msg_name = &dsmp->dsm_send_dest.v6;
949d04ccbb3Scarlsonj 		msg.msg_namelen = sizeof (struct sockaddr_in6);
950d04ccbb3Scarlsonj 		msg.msg_iov = &iov;
951d04ccbb3Scarlsonj 		msg.msg_iovlen = 1;
9527c478bd9Sstevel@tonic-gate 
953d04ccbb3Scarlsonj 		/*
954d04ccbb3Scarlsonj 		 * If the address that's requested cannot be reached, then fall
955d04ccbb3Scarlsonj 		 * back to the multcast address.
956d04ccbb3Scarlsonj 		 */
957d04ccbb3Scarlsonj 		if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) {
958d04ccbb3Scarlsonj 			ismcast = B_TRUE;
959d04ccbb3Scarlsonj 		} else {
960d04ccbb3Scarlsonj 			struct dstinforeq dinfo;
961d04ccbb3Scarlsonj 			struct strioctl str;
962d04ccbb3Scarlsonj 
963d04ccbb3Scarlsonj 			ismcast = B_FALSE;
964d04ccbb3Scarlsonj 			(void) memset(&dinfo, 0, sizeof (dinfo));
965d04ccbb3Scarlsonj 			dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr;
966d04ccbb3Scarlsonj 			str.ic_cmd = SIOCGDSTINFO;
967d04ccbb3Scarlsonj 			str.ic_timout = 0;
968d04ccbb3Scarlsonj 			str.ic_len = sizeof (dinfo);
969d04ccbb3Scarlsonj 			str.ic_dp = (char *)&dinfo;
970d04ccbb3Scarlsonj 			if (ioctl(v6_sock_fd, I_STR, &str) == -1) {
971d04ccbb3Scarlsonj 				dhcpmsg(MSG_ERR,
972d04ccbb3Scarlsonj 				    "send_pkt_internal: ioctl SIOCGDSTINFO");
973d04ccbb3Scarlsonj 			} else if (!dinfo.dir_dreachable) {
974d04ccbb3Scarlsonj 				char abuf[INET6_ADDRSTRLEN];
975d04ccbb3Scarlsonj 
976d04ccbb3Scarlsonj 				dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is "
977d04ccbb3Scarlsonj 				    "not reachable; using multicast instead",
978d04ccbb3Scarlsonj 				    inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf,
979d04ccbb3Scarlsonj 				    sizeof (abuf)));
980d04ccbb3Scarlsonj 				sin6 = dsmp->dsm_send_dest.v6;
981d04ccbb3Scarlsonj 				sin6.sin6_addr =
982d04ccbb3Scarlsonj 				    ipv6_all_dhcp_relay_and_servers;
983d04ccbb3Scarlsonj 				msg.msg_name = &sin6;
984d04ccbb3Scarlsonj 				ismcast = B_TRUE;
985d04ccbb3Scarlsonj 			}
986d04ccbb3Scarlsonj 		}
9877c478bd9Sstevel@tonic-gate 
988d04ccbb3Scarlsonj 		/*
989d04ccbb3Scarlsonj 		 * Make room for our ancillary data option as well as a dummy
990d04ccbb3Scarlsonj 		 * option used by CMSG_NXTHDR.
991d04ccbb3Scarlsonj 		 */
992d04ccbb3Scarlsonj 		msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT +
993d04ccbb3Scarlsonj 		    sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg);
994d04ccbb3Scarlsonj 		msg.msg_control = alloca(msg.msg_controllen);
995d04ccbb3Scarlsonj 		cmsg = CMSG_FIRSTHDR(&msg);
996d04ccbb3Scarlsonj 		cmsg->cmsg_level = IPPROTO_IPV6;
997d04ccbb3Scarlsonj 		cmsg->cmsg_type = IPV6_PKTINFO;
998d04ccbb3Scarlsonj 		/* LINTED: alignment */
999d04ccbb3Scarlsonj 		ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
1000d04ccbb3Scarlsonj 		if (ismcast)
1001d04ccbb3Scarlsonj 			ipi6->ipi6_addr = lif->lif_v6addr;
1002d04ccbb3Scarlsonj 		else
1003d04ccbb3Scarlsonj 			ipi6->ipi6_addr = my_in6addr_any;
1004e11c3f44Smeem 		if (lif->lif_pif->pif_under_ipmp)
1005e11c3f44Smeem 			ipi6->ipi6_ifindex = lif->lif_pif->pif_grindex;
1006e11c3f44Smeem 		else
1007e11c3f44Smeem 			ipi6->ipi6_ifindex = lif->lif_pif->pif_index;
1008d04ccbb3Scarlsonj 		cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg;
1009d04ccbb3Scarlsonj 
1010d04ccbb3Scarlsonj 		/*
1011d04ccbb3Scarlsonj 		 * Now correct the control message length.
1012d04ccbb3Scarlsonj 		 */
1013d04ccbb3Scarlsonj 		cmsg = CMSG_NXTHDR(&msg, cmsg);
1014d04ccbb3Scarlsonj 		msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control;
1015d04ccbb3Scarlsonj 
1016d04ccbb3Scarlsonj 		n_bytes = sendmsg(v6_sock_fd, &msg, 0);
1017d04ccbb3Scarlsonj 	} else {
1018e704a8f2Smeem 		n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt,
1019e704a8f2Smeem 		    dpkt->pkt_cur_len, 0,
1020e704a8f2Smeem 		    (struct sockaddr *)&dsmp->dsm_send_dest.v4,
1021e704a8f2Smeem 		    sizeof (struct sockaddr_in));
10227c478bd9Sstevel@tonic-gate 	}
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	if (n_bytes != dpkt->pkt_cur_len) {
1025e704a8f2Smeem 		msgtype = (n_bytes == -1) ? MSG_ERR : MSG_WARNING;
1026d04ccbb3Scarlsonj 		if (dsmp->dsm_retrans_timer == -1)
1027e704a8f2Smeem 			dhcpmsg(msgtype, "send_pkt_internal: cannot send "
10287c478bd9Sstevel@tonic-gate 			    "%s packet to server", pkt_name);
10297c478bd9Sstevel@tonic-gate 		else
1030e704a8f2Smeem 			dhcpmsg(msgtype, "send_pkt_internal: cannot send "
10317c478bd9Sstevel@tonic-gate 			    "%s packet to server (will retry in %u seconds)",
1032d04ccbb3Scarlsonj 			    pkt_name, dsmp->dsm_send_timeout / MILLISEC);
1033d04ccbb3Scarlsonj 		return (B_FALSE);
10347c478bd9Sstevel@tonic-gate 	}
10357c478bd9Sstevel@tonic-gate 
1036d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name,
1037d04ccbb3Scarlsonj 	    pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name);
10387c478bd9Sstevel@tonic-gate 
1039d04ccbb3Scarlsonj 	dsmp->dsm_packet_sent++;
1040d04ccbb3Scarlsonj 	dsmp->dsm_sent++;
1041d04ccbb3Scarlsonj 	return (B_TRUE);
10427c478bd9Sstevel@tonic-gate }
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate /*
1045d04ccbb3Scarlsonj  * send_pkt(): sends a packet out
10467c478bd9Sstevel@tonic-gate  *
1047d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine sending the packet
10487c478bd9Sstevel@tonic-gate  *	    dhcp_pkt_t *: the packet to send out
10497c478bd9Sstevel@tonic-gate  *	    in_addr_t: the destination IP address for the packet
10507c478bd9Sstevel@tonic-gate  *	    stop_func_t *: a pointer to function to indicate when to stop
10517c478bd9Sstevel@tonic-gate  *			   retransmitting the packet (if NULL, packet is
10527c478bd9Sstevel@tonic-gate  *			   not retransmitted)
1053d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
10547c478bd9Sstevel@tonic-gate  */
10557c478bd9Sstevel@tonic-gate 
1056d04ccbb3Scarlsonj boolean_t
send_pkt(dhcp_smach_t * dsmp,dhcp_pkt_t * dpkt,in_addr_t dest,stop_func_t * stop)1057d04ccbb3Scarlsonj send_pkt(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in_addr_t dest,
10587c478bd9Sstevel@tonic-gate     stop_func_t *stop)
10597c478bd9Sstevel@tonic-gate {
10607c478bd9Sstevel@tonic-gate 	/*
10617c478bd9Sstevel@tonic-gate 	 * packets must be at least sizeof (PKT) or they may be dropped
10627c478bd9Sstevel@tonic-gate 	 * by routers.  pad out the packet in this case.
10637c478bd9Sstevel@tonic-gate 	 */
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate 	dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT));
10667c478bd9Sstevel@tonic-gate 
1067d04ccbb3Scarlsonj 	dsmp->dsm_packet_sent = 0;
1068d04ccbb3Scarlsonj 
1069d04ccbb3Scarlsonj 	(void) memset(&dsmp->dsm_send_dest.v4, 0,
1070d04ccbb3Scarlsonj 	    sizeof (dsmp->dsm_send_dest.v4));
1071d04ccbb3Scarlsonj 	dsmp->dsm_send_dest.v4.sin_addr.s_addr	= dest;
1072d04ccbb3Scarlsonj 	dsmp->dsm_send_dest.v4.sin_family	= AF_INET;
1073d04ccbb3Scarlsonj 	dsmp->dsm_send_dest.v4.sin_port		= htons(IPPORT_BOOTPS);
1074d04ccbb3Scarlsonj 	dsmp->dsm_send_stop_func		= stop;
1075d04ccbb3Scarlsonj 
1076d04ccbb3Scarlsonj 	/*
1077d04ccbb3Scarlsonj 	 * TODO: dispose of this gruesome assumption (there's no real
1078d04ccbb3Scarlsonj 	 * technical gain from doing so, but it would be cleaner)
1079d04ccbb3Scarlsonj 	 */
1080d04ccbb3Scarlsonj 
1081d04ccbb3Scarlsonj 	assert(dpkt == &dsmp->dsm_send_pkt);
1082d04ccbb3Scarlsonj 
1083d04ccbb3Scarlsonj 	/*
1084d04ccbb3Scarlsonj 	 * clear out any packets which had been previously received
1085d04ccbb3Scarlsonj 	 * but not pulled off of the recv_packet queue.
1086d04ccbb3Scarlsonj 	 */
1087d04ccbb3Scarlsonj 
1088d04ccbb3Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
1089d04ccbb3Scarlsonj 
1090d04ccbb3Scarlsonj 	if (stop == NULL)
1091d04ccbb3Scarlsonj 		dsmp->dsm_send_timeout = 0;	/* prevents retransmissions */
1092d04ccbb3Scarlsonj 	else
1093d04ccbb3Scarlsonj 		next_retransmission(dsmp, B_TRUE, B_FALSE);
1094d04ccbb3Scarlsonj 
1095d04ccbb3Scarlsonj 	return (send_pkt_internal(dsmp));
1096d04ccbb3Scarlsonj }
1097d04ccbb3Scarlsonj 
1098d04ccbb3Scarlsonj /*
1099d04ccbb3Scarlsonj  * send_pkt_v6(): sends a DHCPv6 packet out
1100d04ccbb3Scarlsonj  *
1101d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine sending the packet
1102d04ccbb3Scarlsonj  *	    dhcp_pkt_t *: the packet to send out
1103d04ccbb3Scarlsonj  *	    in6_addr_t: the destination IPv6 address for the packet
1104d04ccbb3Scarlsonj  *	    stop_func_t *: a pointer to function to indicate when to stop
1105d04ccbb3Scarlsonj  *			   retransmitting the packet (if NULL, packet is
1106d04ccbb3Scarlsonj  *			   not retransmitted)
1107d04ccbb3Scarlsonj  *	    uint_t: Initial Retransmit Timer value
1108d04ccbb3Scarlsonj  *	    uint_t: Maximum Retransmit Timer value, zero if none
1109d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
1110d04ccbb3Scarlsonj  */
11117c478bd9Sstevel@tonic-gate 
1112d04ccbb3Scarlsonj boolean_t
send_pkt_v6(dhcp_smach_t * dsmp,dhcp_pkt_t * dpkt,in6_addr_t dest,stop_func_t * stop,uint_t irt,uint_t mrt)1113d04ccbb3Scarlsonj send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest,
1114d04ccbb3Scarlsonj     stop_func_t *stop, uint_t irt, uint_t mrt)
1115d04ccbb3Scarlsonj {
1116d04ccbb3Scarlsonj 	dsmp->dsm_packet_sent = 0;
1117d04ccbb3Scarlsonj 
1118d04ccbb3Scarlsonj 	(void) memset(&dsmp->dsm_send_dest.v6, 0,
1119d04ccbb3Scarlsonj 	    sizeof (dsmp->dsm_send_dest.v6));
1120d04ccbb3Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_addr	= dest;
1121d04ccbb3Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_family	= AF_INET6;
1122d04ccbb3Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_port	= htons(IPPORT_DHCPV6S);
1123d04ccbb3Scarlsonj 	dsmp->dsm_send_stop_func		= stop;
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	/*
11267c478bd9Sstevel@tonic-gate 	 * TODO: dispose of this gruesome assumption (there's no real
11277c478bd9Sstevel@tonic-gate 	 * technical gain from doing so, but it would be cleaner)
11287c478bd9Sstevel@tonic-gate 	 */
11297c478bd9Sstevel@tonic-gate 
1130d04ccbb3Scarlsonj 	assert(dpkt == &dsmp->dsm_send_pkt);
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	/*
11337c478bd9Sstevel@tonic-gate 	 * clear out any packets which had been previously received
11347c478bd9Sstevel@tonic-gate 	 * but not pulled off of the recv_packet queue.
11357c478bd9Sstevel@tonic-gate 	 */
11367c478bd9Sstevel@tonic-gate 
1137d04ccbb3Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 	if (stop == NULL) {
1140d04ccbb3Scarlsonj 		dsmp->dsm_send_timeout = 0;	/* prevents retransmissions */
1141d04ccbb3Scarlsonj 	} else {
1142d04ccbb3Scarlsonj 		dsmp->dsm_send_timeout = irt;
1143d04ccbb3Scarlsonj 		dsmp->dsm_send_tcenter = mrt;
1144d04ccbb3Scarlsonj 		/*
1145d04ccbb3Scarlsonj 		 * This is quite ugly, but RFC 3315 section 17.1.2 requires
1146d04ccbb3Scarlsonj 		 * that the RAND value for the very first retransmission of a
1147d04ccbb3Scarlsonj 		 * Solicit message is strictly greater than zero.
1148d04ccbb3Scarlsonj 		 */
1149d04ccbb3Scarlsonj 		next_retransmission(dsmp, B_TRUE,
1150d04ccbb3Scarlsonj 		    pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT);
1151d04ccbb3Scarlsonj 	}
11527c478bd9Sstevel@tonic-gate 
1153d04ccbb3Scarlsonj 	return (send_pkt_internal(dsmp));
11547c478bd9Sstevel@tonic-gate }
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate /*
11577c478bd9Sstevel@tonic-gate  * retransmit(): retransmits the current packet on an interface
11587c478bd9Sstevel@tonic-gate  *
11597c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: unused
1160d04ccbb3Scarlsonj  *	    void *: the dhcp_smach_t * (state machine) sending a packet
11617c478bd9Sstevel@tonic-gate  *  output: void
11627c478bd9Sstevel@tonic-gate  */
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate /* ARGSUSED */
11657c478bd9Sstevel@tonic-gate static void
retransmit(iu_tq_t * tqp,void * arg)11667c478bd9Sstevel@tonic-gate retransmit(iu_tq_t *tqp, void *arg)
11677c478bd9Sstevel@tonic-gate {
1168d04ccbb3Scarlsonj 	dhcp_smach_t	*dsmp = arg;
1169d04ccbb3Scarlsonj 
1170d04ccbb3Scarlsonj 	dsmp->dsm_retrans_timer = -1;
11717c478bd9Sstevel@tonic-gate 
1172d04ccbb3Scarlsonj 	if (!verify_smach(dsmp))
11737c478bd9Sstevel@tonic-gate 		return;
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 	/*
1176d04ccbb3Scarlsonj 	 * Check the callback to see if we should keep sending retransmissions.
1177d04ccbb3Scarlsonj 	 * Compute the next retransmission time first, so that the callback can
1178d04ccbb3Scarlsonj 	 * cap the value if need be.  (Required for DHCPv6 Confirm messages.)
1179d04ccbb3Scarlsonj 	 *
1180d04ccbb3Scarlsonj 	 * Hold the state machine across the callback so that the called
1181d04ccbb3Scarlsonj 	 * function can remove the state machine from the system without
1182d04ccbb3Scarlsonj 	 * disturbing the string used subsequently for verbose logging.  The
1183d04ccbb3Scarlsonj 	 * Release function destroys the state machine when the retry count
1184d04ccbb3Scarlsonj 	 * expires.
11857c478bd9Sstevel@tonic-gate 	 */
11867c478bd9Sstevel@tonic-gate 
1187d04ccbb3Scarlsonj 	next_retransmission(dsmp, B_FALSE, B_FALSE);
1188d04ccbb3Scarlsonj 	hold_smach(dsmp);
1189d04ccbb3Scarlsonj 	if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) {
1190d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s",
1191d04ccbb3Scarlsonj 		    dsmp->dsm_name);
1192d04ccbb3Scarlsonj 	} else {
1193d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s",
1194d04ccbb3Scarlsonj 		    dsmp->dsm_name);
1195d04ccbb3Scarlsonj 		(void) send_pkt_internal(dsmp);
1196d04ccbb3Scarlsonj 	}
1197d04ccbb3Scarlsonj 	release_smach(dsmp);
11987c478bd9Sstevel@tonic-gate }
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate /*
12017c478bd9Sstevel@tonic-gate  * stop_pkt_retransmission(): stops retransmission of last sent packet
12027c478bd9Sstevel@tonic-gate  *
1203d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to stop retransmission on
1204d04ccbb3Scarlsonj  *  output: void
1205d04ccbb3Scarlsonj  */
1206d04ccbb3Scarlsonj 
1207d04ccbb3Scarlsonj void
stop_pkt_retransmission(dhcp_smach_t * dsmp)1208d04ccbb3Scarlsonj stop_pkt_retransmission(dhcp_smach_t *dsmp)
1209d04ccbb3Scarlsonj {
1210d04ccbb3Scarlsonj 	if (dsmp->dsm_retrans_timer != -1 &&
1211d04ccbb3Scarlsonj 	    iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) {
1212d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s",
1213d04ccbb3Scarlsonj 		    dsmp->dsm_name);
1214d04ccbb3Scarlsonj 		dsmp->dsm_retrans_timer = -1;
1215d04ccbb3Scarlsonj 		release_smach(dsmp);
1216d04ccbb3Scarlsonj 	}
1217d04ccbb3Scarlsonj }
1218d04ccbb3Scarlsonj 
1219d04ccbb3Scarlsonj /*
1220d04ccbb3Scarlsonj  * retransmit_now(): force a packet retransmission right now.  Used only with
1221d04ccbb3Scarlsonj  *		     the DHCPv6 UseMulticast status code.  Use with caution;
1222d04ccbb3Scarlsonj  *		     triggered retransmissions can cause packet storms.
1223d04ccbb3Scarlsonj  *
1224d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to force retransmission on
12257c478bd9Sstevel@tonic-gate  *  output: void
12267c478bd9Sstevel@tonic-gate  */
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate void
retransmit_now(dhcp_smach_t * dsmp)1229d04ccbb3Scarlsonj retransmit_now(dhcp_smach_t *dsmp)
1230d04ccbb3Scarlsonj {
1231d04ccbb3Scarlsonj 	stop_pkt_retransmission(dsmp);
1232d04ccbb3Scarlsonj 	(void) send_pkt_internal(dsmp);
1233d04ccbb3Scarlsonj }
1234d04ccbb3Scarlsonj 
1235d04ccbb3Scarlsonj /*
1236d04ccbb3Scarlsonj  * alloc_pkt_entry(): Allocates a packet list entry with a given data area
1237d04ccbb3Scarlsonj  *		      size.
1238d04ccbb3Scarlsonj  *
1239d04ccbb3Scarlsonj  *   input: size_t: size of data area for packet
1240d04ccbb3Scarlsonj  *	    boolean_t: B_TRUE for IPv6
1241d04ccbb3Scarlsonj  *  output: PKT_LIST *: allocated packet list entry
1242d04ccbb3Scarlsonj  */
1243d04ccbb3Scarlsonj 
1244d04ccbb3Scarlsonj PKT_LIST *
alloc_pkt_entry(size_t psize,boolean_t isv6)1245d04ccbb3Scarlsonj alloc_pkt_entry(size_t psize, boolean_t isv6)
1246d04ccbb3Scarlsonj {
1247d04ccbb3Scarlsonj 	PKT_LIST	*plp;
1248d04ccbb3Scarlsonj 
1249d04ccbb3Scarlsonj 	if ((plp = calloc(1, sizeof (*plp))) == NULL ||
1250d04ccbb3Scarlsonj 	    (plp->pkt = malloc(psize)) == NULL) {
1251d04ccbb3Scarlsonj 		free(plp);
1252d04ccbb3Scarlsonj 		plp = NULL;
1253d04ccbb3Scarlsonj 	} else {
1254d04ccbb3Scarlsonj 		plp->len = psize;
1255d04ccbb3Scarlsonj 		plp->isv6 = isv6;
1256d04ccbb3Scarlsonj 	}
1257d04ccbb3Scarlsonj 
1258d04ccbb3Scarlsonj 	return (plp);
1259d04ccbb3Scarlsonj }
1260d04ccbb3Scarlsonj 
1261d04ccbb3Scarlsonj /*
1262d04ccbb3Scarlsonj  * sock_recvpkt(): read from the given socket into an allocated buffer and
1263d04ccbb3Scarlsonj  *		   handles any ancillary data options.
1264d04ccbb3Scarlsonj  *
1265d04ccbb3Scarlsonj  *   input: int: file descriptor to read
1266d04ccbb3Scarlsonj  *	    PKT_LIST *: allocated buffer
1267d04ccbb3Scarlsonj  *  output: ssize_t: number of bytes read, or -1 on error
1268d04ccbb3Scarlsonj  */
1269d04ccbb3Scarlsonj 
1270d04ccbb3Scarlsonj static ssize_t
sock_recvpkt(int fd,PKT_LIST * plp)1271d04ccbb3Scarlsonj sock_recvpkt(int fd, PKT_LIST *plp)
12727c478bd9Sstevel@tonic-gate {
1273d04ccbb3Scarlsonj 	struct iovec iov;
1274d04ccbb3Scarlsonj 	struct msghdr msg;
12754ee71a50Scarlsonj 	int64_t ctrl[8192 / sizeof (int64_t)];
1276d04ccbb3Scarlsonj 	ssize_t msglen;
1277d04ccbb3Scarlsonj 
1278d04ccbb3Scarlsonj 	(void) memset(&iov, 0, sizeof (iov));
1279d04ccbb3Scarlsonj 	iov.iov_base = (caddr_t)plp->pkt;
1280d04ccbb3Scarlsonj 	iov.iov_len = plp->len;
1281d04ccbb3Scarlsonj 
1282d04ccbb3Scarlsonj 	(void) memset(&msg, 0, sizeof (msg));
1283d04ccbb3Scarlsonj 	msg.msg_name = &plp->pktfrom;
1284d04ccbb3Scarlsonj 	msg.msg_namelen = sizeof (plp->pktfrom);
1285d04ccbb3Scarlsonj 	msg.msg_iov = &iov;
1286d04ccbb3Scarlsonj 	msg.msg_iovlen = 1;
1287d04ccbb3Scarlsonj 	msg.msg_control = ctrl;
1288d04ccbb3Scarlsonj 	msg.msg_controllen = sizeof (ctrl);
1289d04ccbb3Scarlsonj 
1290d04ccbb3Scarlsonj 	if ((msglen = recvmsg(fd, &msg, 0)) != -1) {
1291d04ccbb3Scarlsonj 		struct cmsghdr *cmsg;
1292d04ccbb3Scarlsonj 
1293d04ccbb3Scarlsonj 		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
1294d04ccbb3Scarlsonj 		    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1295d04ccbb3Scarlsonj 			struct sockaddr_in *sinp;
1296d04ccbb3Scarlsonj 			struct sockaddr_in6 *sin6;
1297d04ccbb3Scarlsonj 			struct in6_pktinfo *ipi6;
1298d04ccbb3Scarlsonj 
1299d04ccbb3Scarlsonj 			switch (cmsg->cmsg_level) {
1300d04ccbb3Scarlsonj 			case IPPROTO_IP:
1301d04ccbb3Scarlsonj 				switch (cmsg->cmsg_type) {
1302d04ccbb3Scarlsonj 				case IP_RECVDSTADDR:
1303d04ccbb3Scarlsonj 					sinp = (struct sockaddr_in *)
1304d04ccbb3Scarlsonj 					    &plp->pktto;
1305d04ccbb3Scarlsonj 					sinp->sin_family = AF_INET;
1306d04ccbb3Scarlsonj 					(void) memcpy(&sinp->sin_addr.s_addr,
1307d04ccbb3Scarlsonj 					    CMSG_DATA(cmsg),
1308d04ccbb3Scarlsonj 					    sizeof (ipaddr_t));
1309d04ccbb3Scarlsonj 					break;
1310d04ccbb3Scarlsonj 
1311d04ccbb3Scarlsonj 				case IP_RECVIF:
1312d04ccbb3Scarlsonj 					(void) memcpy(&plp->ifindex,
1313d04ccbb3Scarlsonj 					    CMSG_DATA(cmsg), sizeof (uint_t));
1314d04ccbb3Scarlsonj 					break;
1315d04ccbb3Scarlsonj 				}
1316d04ccbb3Scarlsonj 				break;
1317d04ccbb3Scarlsonj 
1318d04ccbb3Scarlsonj 			case IPPROTO_IPV6:
1319d04ccbb3Scarlsonj 				switch (cmsg->cmsg_type) {
1320d04ccbb3Scarlsonj 				case IPV6_PKTINFO:
1321d04ccbb3Scarlsonj 					/* LINTED: alignment */
1322d04ccbb3Scarlsonj 					ipi6 = (struct in6_pktinfo *)
1323d04ccbb3Scarlsonj 					    CMSG_DATA(cmsg);
1324d04ccbb3Scarlsonj 					sin6 = (struct sockaddr_in6 *)
1325d04ccbb3Scarlsonj 					    &plp->pktto;
1326d04ccbb3Scarlsonj 					sin6->sin6_family = AF_INET6;
1327d04ccbb3Scarlsonj 					(void) memcpy(&sin6->sin6_addr,
1328d04ccbb3Scarlsonj 					    &ipi6->ipi6_addr,
1329d04ccbb3Scarlsonj 					    sizeof (ipi6->ipi6_addr));
1330d04ccbb3Scarlsonj 					(void) memcpy(&plp->ifindex,
1331d04ccbb3Scarlsonj 					    &ipi6->ipi6_ifindex,
1332d04ccbb3Scarlsonj 					    sizeof (uint_t));
1333d04ccbb3Scarlsonj 					break;
1334d04ccbb3Scarlsonj 				}
1335d04ccbb3Scarlsonj 			}
13367c478bd9Sstevel@tonic-gate 		}
13377c478bd9Sstevel@tonic-gate 	}
1338d04ccbb3Scarlsonj 	return (msglen);
13397c478bd9Sstevel@tonic-gate }
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate /*
1342d04ccbb3Scarlsonj  * recv_pkt(): receives a single DHCP packet on a given file descriptor.
13437c478bd9Sstevel@tonic-gate  *
1344e704a8f2Smeem  *   input: int: the file descriptor to receive the packet from
1345d04ccbb3Scarlsonj  *	    int: the maximum packet size to allow
1346d04ccbb3Scarlsonj  *	    boolean_t: B_TRUE for IPv6
1347d04ccbb3Scarlsonj  *  output: PKT_LIST *: the received packet
13487c478bd9Sstevel@tonic-gate  */
13497c478bd9Sstevel@tonic-gate 
1350d04ccbb3Scarlsonj PKT_LIST *
recv_pkt(int fd,int mtu,boolean_t isv6)1351e704a8f2Smeem recv_pkt(int fd, int mtu, boolean_t isv6)
13527c478bd9Sstevel@tonic-gate {
13537c478bd9Sstevel@tonic-gate 	PKT_LIST	*plp;
13547c478bd9Sstevel@tonic-gate 	ssize_t		retval;
13557c478bd9Sstevel@tonic-gate 
1356d04ccbb3Scarlsonj 	if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) {
1357d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERROR,
1358d04ccbb3Scarlsonj 		    "recv_pkt: allocation failure; dropped packet");
1359d04ccbb3Scarlsonj 		return (NULL);
13607c478bd9Sstevel@tonic-gate 	}
13617c478bd9Sstevel@tonic-gate 
1362e704a8f2Smeem 	retval = sock_recvpkt(fd, plp);
1363e704a8f2Smeem 	if (retval == -1) {
1364e704a8f2Smeem 		dhcpmsg(MSG_ERR, "recv_pkt: recvfrom v%d failed, dropped",
1365e704a8f2Smeem 		    isv6 ? 6 : 4);
1366e704a8f2Smeem 		goto failure;
1367e704a8f2Smeem 	}
13687c478bd9Sstevel@tonic-gate 
1369e704a8f2Smeem 	plp->len = retval;
13707c478bd9Sstevel@tonic-gate 
1371e704a8f2Smeem 	if (isv6) {
1372d04ccbb3Scarlsonj 		if (retval < sizeof (dhcpv6_message_t)) {
1373d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "recv_pkt: runt message");
1374d04ccbb3Scarlsonj 			goto failure;
1375d04ccbb3Scarlsonj 		}
1376d04ccbb3Scarlsonj 	} else {
1377d04ccbb3Scarlsonj 		switch (dhcp_options_scan(plp, B_TRUE)) {
13787c478bd9Sstevel@tonic-gate 
1379d04ccbb3Scarlsonj 		case DHCP_WRONG_MSG_TYPE:
1380d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
1381d04ccbb3Scarlsonj 			    "recv_pkt: unexpected DHCP message");
1382d04ccbb3Scarlsonj 			goto failure;
13837c478bd9Sstevel@tonic-gate 
1384d04ccbb3Scarlsonj 		case DHCP_GARBLED_MSG_TYPE:
1385d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
1386d04ccbb3Scarlsonj 			    "recv_pkt: garbled DHCP message type");
1387d04ccbb3Scarlsonj 			goto failure;
13887c478bd9Sstevel@tonic-gate 
1389d04ccbb3Scarlsonj 		case DHCP_BAD_OPT_OVLD:
1390d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload");
1391d04ccbb3Scarlsonj 			goto failure;
13927c478bd9Sstevel@tonic-gate 
1393d04ccbb3Scarlsonj 		case 0:
1394d04ccbb3Scarlsonj 			break;
13957c478bd9Sstevel@tonic-gate 
1396d04ccbb3Scarlsonj 		default:
1397d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
1398d04ccbb3Scarlsonj 			    "recv_pkt: packet corrupted, dropped");
1399d04ccbb3Scarlsonj 			goto failure;
1400d04ccbb3Scarlsonj 		}
14017c478bd9Sstevel@tonic-gate 	}
1402d04ccbb3Scarlsonj 	return (plp);
1403d04ccbb3Scarlsonj 
1404d04ccbb3Scarlsonj failure:
1405d04ccbb3Scarlsonj 	free_pkt_entry(plp);
1406d04ccbb3Scarlsonj 	return (NULL);
1407d04ccbb3Scarlsonj }
1408d04ccbb3Scarlsonj 
1409d04ccbb3Scarlsonj /*
1410d04ccbb3Scarlsonj  * pkt_v4_match(): check if a given DHCPv4 message type is in a given set
1411d04ccbb3Scarlsonj  *
1412d04ccbb3Scarlsonj  *   input: uchar_t: packet type
1413d04ccbb3Scarlsonj  *	    dhcp_message_type_t: bit-wise OR of DHCP_P* values.
1414d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE if packet type is in the set
1415d04ccbb3Scarlsonj  */
14167c478bd9Sstevel@tonic-gate 
1417d04ccbb3Scarlsonj boolean_t
pkt_v4_match(uchar_t type,dhcp_message_type_t match_type)1418d04ccbb3Scarlsonj pkt_v4_match(uchar_t type, dhcp_message_type_t match_type)
1419d04ccbb3Scarlsonj {
14207c478bd9Sstevel@tonic-gate 	/*
1421d04ccbb3Scarlsonj 	 * note: the ordering here allows direct indexing of the table
1422d04ccbb3Scarlsonj 	 *	 based on the RFC2131 packet type value passed in.
14237c478bd9Sstevel@tonic-gate 	 */
14247c478bd9Sstevel@tonic-gate 
1425d04ccbb3Scarlsonj 	static dhcp_message_type_t type_map[] = {
1426d04ccbb3Scarlsonj 		DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST,
1427d04ccbb3Scarlsonj 		DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE,
1428d04ccbb3Scarlsonj 		DHCP_PINFORM
1429d04ccbb3Scarlsonj 	};
1430d04ccbb3Scarlsonj 
1431d04ccbb3Scarlsonj 	if (type < (sizeof (type_map) / sizeof (*type_map)))
1432d04ccbb3Scarlsonj 		return ((type_map[type] & match_type) ? B_TRUE : B_FALSE);
14337c478bd9Sstevel@tonic-gate 	else
1434d04ccbb3Scarlsonj 		return (B_FALSE);
1435d04ccbb3Scarlsonj }
1436d04ccbb3Scarlsonj 
1437d04ccbb3Scarlsonj /*
1438d04ccbb3Scarlsonj  * pkt_smach_enqueue(): enqueue a packet on a given state machine
1439d04ccbb3Scarlsonj  *
1440d04ccbb3Scarlsonj  *   input: dhcp_smach_t: state machine
1441d04ccbb3Scarlsonj  *	    PKT_LIST *: packet to enqueue
1442d04ccbb3Scarlsonj  *  output: none
1443d04ccbb3Scarlsonj  */
14447c478bd9Sstevel@tonic-gate 
1445d04ccbb3Scarlsonj void
pkt_smach_enqueue(dhcp_smach_t * dsmp,PKT_LIST * plp)1446d04ccbb3Scarlsonj pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp)
1447d04ccbb3Scarlsonj {
1448d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s",
1449d04ccbb3Scarlsonj 	    pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6),
1450d04ccbb3Scarlsonj 	    dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name);
14517c478bd9Sstevel@tonic-gate 
1452d04ccbb3Scarlsonj 	/* add to front of list */
1453d04ccbb3Scarlsonj 	insque(plp, &dsmp->dsm_recv_pkt_list);
1454d04ccbb3Scarlsonj }
14557c478bd9Sstevel@tonic-gate 
1456d04ccbb3Scarlsonj /*
1457d04ccbb3Scarlsonj  * next_retransmission(): computes the number of seconds until the next
1458d04ccbb3Scarlsonj  *			  retransmission, based on the algorithms in RFCs 2131
1459d04ccbb3Scarlsonj  *			  3315.
1460d04ccbb3Scarlsonj  *
1461d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: state machine that needs a new timer
1462d04ccbb3Scarlsonj  *	    boolean_t: B_TRUE if this is the first time sending the message
1463d04ccbb3Scarlsonj  *	    boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2)
1464d04ccbb3Scarlsonj  *  output: none
1465d04ccbb3Scarlsonj  */
14667c478bd9Sstevel@tonic-gate 
1467d04ccbb3Scarlsonj static void
next_retransmission(dhcp_smach_t * dsmp,boolean_t first_send,boolean_t positive_only)1468d04ccbb3Scarlsonj next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send,
1469d04ccbb3Scarlsonj     boolean_t positive_only)
1470d04ccbb3Scarlsonj {
1471d04ccbb3Scarlsonj 	uint32_t timeout_ms;
1472d04ccbb3Scarlsonj 
1473d04ccbb3Scarlsonj 	if (dsmp->dsm_isv6) {
1474d04ccbb3Scarlsonj 		double randval;
1475d04ccbb3Scarlsonj 
1476d04ccbb3Scarlsonj 		/*
1477d04ccbb3Scarlsonj 		 * The RFC specifies 0 to 10% jitter for the initial
1478d04ccbb3Scarlsonj 		 * solicitation, and plus or minus 10% jitter for all others.
1479d04ccbb3Scarlsonj 		 * This works out to 100 milliseconds on the shortest timer we
1480d04ccbb3Scarlsonj 		 * use.
1481d04ccbb3Scarlsonj 		 */
1482d04ccbb3Scarlsonj 		if (positive_only)
1483d04ccbb3Scarlsonj 			randval = drand48() / 10.0;
1484d04ccbb3Scarlsonj 		else
1485d04ccbb3Scarlsonj 			randval = (drand48() - 0.5) / 5.0;
1486d04ccbb3Scarlsonj 
1487d04ccbb3Scarlsonj 		/* The RFC specifies doubling *after* the first transmission */
1488d04ccbb3Scarlsonj 		timeout_ms = dsmp->dsm_send_timeout;
1489d04ccbb3Scarlsonj 		if (!first_send)
1490d04ccbb3Scarlsonj 			timeout_ms *= 2;
1491d04ccbb3Scarlsonj 		timeout_ms += (int)(randval * dsmp->dsm_send_timeout);
1492d04ccbb3Scarlsonj 
1493d04ccbb3Scarlsonj 		/* This checks the MRT (maximum retransmission time) */
1494d04ccbb3Scarlsonj 		if (dsmp->dsm_send_tcenter != 0 &&
1495d04ccbb3Scarlsonj 		    timeout_ms > dsmp->dsm_send_tcenter) {
1496d04ccbb3Scarlsonj 			timeout_ms = dsmp->dsm_send_tcenter +
1497d04ccbb3Scarlsonj 			    (uint_t)(randval * dsmp->dsm_send_tcenter);
14987c478bd9Sstevel@tonic-gate 		}
14997c478bd9Sstevel@tonic-gate 
1500d04ccbb3Scarlsonj 		dsmp->dsm_send_timeout = timeout_ms;
1501d04ccbb3Scarlsonj 	} else {
1502d04ccbb3Scarlsonj 		if (dsmp->dsm_state == RENEWING ||
1503d04ccbb3Scarlsonj 		    dsmp->dsm_state == REBINDING) {
1504d04ccbb3Scarlsonj 			monosec_t mono;
1505d04ccbb3Scarlsonj 
1506d04ccbb3Scarlsonj 			timeout_ms = dsmp->dsm_state == RENEWING ?
1507d04ccbb3Scarlsonj 			    dsmp->dsm_leases->dl_t2.dt_start :
1508d04ccbb3Scarlsonj 			    dsmp->dsm_leases->dl_lifs->lif_expire.dt_start;
1509d04ccbb3Scarlsonj 			timeout_ms += dsmp->dsm_curstart_monosec;
1510d04ccbb3Scarlsonj 			mono = monosec();
1511d04ccbb3Scarlsonj 			if (mono > timeout_ms)
1512d04ccbb3Scarlsonj 				timeout_ms = 0;
1513d04ccbb3Scarlsonj 			else
1514d04ccbb3Scarlsonj 				timeout_ms -= mono;
1515d04ccbb3Scarlsonj 			timeout_ms *= MILLISEC / 2;
1516d04ccbb3Scarlsonj 		} else {
1517d04ccbb3Scarlsonj 			/*
1518d04ccbb3Scarlsonj 			 * Start at 4, and increase by a factor of 2 up to 64.
1519d04ccbb3Scarlsonj 			 */
1520d04ccbb3Scarlsonj 			if (first_send) {
1521d04ccbb3Scarlsonj 				timeout_ms = 4 * MILLISEC;
1522d04ccbb3Scarlsonj 			} else {
1523d04ccbb3Scarlsonj 				timeout_ms = MIN(dsmp->dsm_send_tcenter << 1,
1524d04ccbb3Scarlsonj 				    64 * MILLISEC);
1525d04ccbb3Scarlsonj 			}
1526d04ccbb3Scarlsonj 		}
15277c478bd9Sstevel@tonic-gate 
1528d04ccbb3Scarlsonj 		dsmp->dsm_send_tcenter = timeout_ms;
15297c478bd9Sstevel@tonic-gate 
1530d04ccbb3Scarlsonj 		/*
1531d04ccbb3Scarlsonj 		 * At each iteration, jitter the timeout by some fraction of a
1532d04ccbb3Scarlsonj 		 * second.
1533d04ccbb3Scarlsonj 		 */
1534d04ccbb3Scarlsonj 		dsmp->dsm_send_timeout = timeout_ms +
1535d04ccbb3Scarlsonj 		    ((lrand48() % (2 * MILLISEC)) - MILLISEC);
1536d04ccbb3Scarlsonj 	}
15377c478bd9Sstevel@tonic-gate }
15387c478bd9Sstevel@tonic-gate 
15397c478bd9Sstevel@tonic-gate /*
1540d04ccbb3Scarlsonj  * dhcp_ip_default(): open and bind the default IP sockets used for I/O and
1541d04ccbb3Scarlsonj  *		      interface control.
15427c478bd9Sstevel@tonic-gate  *
1543d04ccbb3Scarlsonj  *   input: none
1544d04ccbb3Scarlsonj  *  output: B_TRUE on success
15457c478bd9Sstevel@tonic-gate  */
15467c478bd9Sstevel@tonic-gate 
1547d04ccbb3Scarlsonj boolean_t
dhcp_ip_default(void)1548d04ccbb3Scarlsonj dhcp_ip_default(void)
15497c478bd9Sstevel@tonic-gate {
1550b5e45829Scarlsonj 	int on = 1;
15517c478bd9Sstevel@tonic-gate 
1552d04ccbb3Scarlsonj 	if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1553d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR,
1554d04ccbb3Scarlsonj 		    "dhcp_ip_default: unable to create IPv4 socket");
1555d04ccbb3Scarlsonj 		return (B_FALSE);
1556d04ccbb3Scarlsonj 	}
1557d04ccbb3Scarlsonj 
1558d04ccbb3Scarlsonj 	if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
1559d04ccbb3Scarlsonj 	    sizeof (on)) == -1) {
1560d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR,
1561d04ccbb3Scarlsonj 		    "dhcp_ip_default: unable to enable IP_RECVDSTADDR");
1562d04ccbb3Scarlsonj 		return (B_FALSE);
1563d04ccbb3Scarlsonj 	}
1564d04ccbb3Scarlsonj 
1565d04ccbb3Scarlsonj 	if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) ==
1566d04ccbb3Scarlsonj 	    -1) {
1567d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR,
1568d04ccbb3Scarlsonj 		    "dhcp_ip_default: unable to enable IP_RECVIF");
1569d04ccbb3Scarlsonj 		return (B_FALSE);
1570d04ccbb3Scarlsonj 	}
1571d04ccbb3Scarlsonj 
1572d04ccbb3Scarlsonj 	if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) {
1573d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERROR,
1574d04ccbb3Scarlsonj 		    "dhcp_ip_default: unable to bind IPv4 socket to port %d",
1575d04ccbb3Scarlsonj 		    IPPORT_BOOTPC);
1576d04ccbb3Scarlsonj 		return (B_FALSE);
1577d04ccbb3Scarlsonj 	}
1578d04ccbb3Scarlsonj 
1579e704a8f2Smeem 	if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_global,
1580d04ccbb3Scarlsonj 	    NULL) == -1) {
1581d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1582d04ccbb3Scarlsonj 		    "receive IPv4 broadcasts");
1583d04ccbb3Scarlsonj 		return (B_FALSE);
1584d04ccbb3Scarlsonj 	}
1585d04ccbb3Scarlsonj 
1586d04ccbb3Scarlsonj 	if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
1587d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR,
1588d04ccbb3Scarlsonj 		    "dhcp_ip_default: unable to create IPv6 socket");
1589d04ccbb3Scarlsonj 		return (B_FALSE);
1590d04ccbb3Scarlsonj 	}
1591d04ccbb3Scarlsonj 
1592d04ccbb3Scarlsonj 	if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
1593d04ccbb3Scarlsonj 	    sizeof (on)) == -1) {
1594d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR,
1595d04ccbb3Scarlsonj 		    "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO");
1596d04ccbb3Scarlsonj 		return (B_FALSE);
1597d04ccbb3Scarlsonj 	}
1598d04ccbb3Scarlsonj 
1599d04ccbb3Scarlsonj 	if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) {
1600d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERROR,
1601d04ccbb3Scarlsonj 		    "dhcp_ip_default: unable to bind IPv6 socket to port %d",
1602d04ccbb3Scarlsonj 		    IPPORT_DHCPV6C);
1603d04ccbb3Scarlsonj 		return (B_FALSE);
1604d04ccbb3Scarlsonj 	}
1605d04ccbb3Scarlsonj 
1606e704a8f2Smeem 	if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_global,
1607d04ccbb3Scarlsonj 	    NULL) == -1) {
1608d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1609d04ccbb3Scarlsonj 		    "receive IPv6 packets");
1610d04ccbb3Scarlsonj 		return (B_FALSE);
1611d04ccbb3Scarlsonj 	}
16127c478bd9Sstevel@tonic-gate 
1613d04ccbb3Scarlsonj 	return (B_TRUE);
16147c478bd9Sstevel@tonic-gate }
1615