xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c (revision 3431:9f2d277dcffa)
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
5*3431Scarlsonj  * Common Development and Distribution License (the "License").
6*3431Scarlsonj  * 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 /*
22*3431Scarlsonj  * 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>
34*3431Scarlsonj #include <search.h>
35*3431Scarlsonj #include <alloca.h>
36*3431Scarlsonj #include <limits.h>
37*3431Scarlsonj #include <stropts.h>
38*3431Scarlsonj #include <netinet/dhcp6.h>
39*3431Scarlsonj #include <arpa/inet.h>
40*3431Scarlsonj #include <sys/sysmacros.h>
41*3431Scarlsonj #include <sys/sockio.h>
42*3431Scarlsonj #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"
49*3431Scarlsonj #include "dlpi_io.h"
500Sstevel@tonic-gate 
51*3431Scarlsonj int v6_sock_fd = -1;
52*3431Scarlsonj int v4_sock_fd = -1;
53*3431Scarlsonj 
54*3431Scarlsonj const in6_addr_t ipv6_all_dhcp_relay_and_servers = {
55*3431Scarlsonj 	0xff, 0x02, 0x00, 0x00,
56*3431Scarlsonj 	0x00, 0x00, 0x00, 0x00,
57*3431Scarlsonj 	0x00, 0x00, 0x00, 0x00,
58*3431Scarlsonj 	0x00, 0x01, 0x00, 0x02
59*3431Scarlsonj };
600Sstevel@tonic-gate 
610Sstevel@tonic-gate /*
62*3431Scarlsonj  * We have our own version of this constant because dhcpagent is compiled with
63*3431Scarlsonj  * -lxnet.
640Sstevel@tonic-gate  */
65*3431Scarlsonj const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT;
660Sstevel@tonic-gate 
67*3431Scarlsonj static void 	retransmit(iu_tq_t *, void *);
68*3431Scarlsonj static void	next_retransmission(dhcp_smach_t *, boolean_t, boolean_t);
69*3431Scarlsonj static boolean_t send_pkt_internal(dhcp_smach_t *);
700Sstevel@tonic-gate 
710Sstevel@tonic-gate /*
72*3431Scarlsonj  * pkt_send_type(): returns an integer representing the packet's type; only
73*3431Scarlsonj  *		    for use with outbound packets.
740Sstevel@tonic-gate  *
75*3431Scarlsonj  *   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
80*3431Scarlsonj pkt_send_type(const dhcp_pkt_t *dpkt)
810Sstevel@tonic-gate {
82*3431Scarlsonj 	const uchar_t *option;
83*3431Scarlsonj 
84*3431Scarlsonj 	if (dpkt->pkt_isv6)
85*3431Scarlsonj 		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 
93*3431Scarlsonj 	option = dpkt->pkt->options;
94*3431Scarlsonj 	for (;;) {
95*3431Scarlsonj 		if (*option == CD_PAD) {
96*3431Scarlsonj 			option++;
97*3431Scarlsonj 			continue;
98*3431Scarlsonj 		}
99*3431Scarlsonj 		if (*option == CD_END ||
100*3431Scarlsonj 		    option + 2 - dpkt->pkt->options >=
101*3431Scarlsonj 		    sizeof (dpkt->pkt->options))
1020Sstevel@tonic-gate 			return (0);
103*3431Scarlsonj 		if (*option == CD_DHCP_TYPE)
104*3431Scarlsonj 			break;
1050Sstevel@tonic-gate 		option++;
106*3431Scarlsonj 		option += *option + 1;
1070Sstevel@tonic-gate 	}
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	return (option[2]);
1100Sstevel@tonic-gate }
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate /*
113*3431Scarlsonj  * pkt_recv_type(): returns an integer representing the packet's type; only
114*3431Scarlsonj  *		    for use with inbound packets.
115*3431Scarlsonj  *
116*3431Scarlsonj  *   input: dhcp_pkt_t *: the packet to examine
117*3431Scarlsonj  *  output: uchar_t: the packet type (0 if unknown)
118*3431Scarlsonj  */
119*3431Scarlsonj 
120*3431Scarlsonj uchar_t
121*3431Scarlsonj pkt_recv_type(const PKT_LIST *plp)
122*3431Scarlsonj {
123*3431Scarlsonj 	if (plp->isv6)
124*3431Scarlsonj 		return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type);
125*3431Scarlsonj 	else if (plp->opts[CD_DHCP_TYPE] != NULL)
126*3431Scarlsonj 		return (plp->opts[CD_DHCP_TYPE]->value[0]);
127*3431Scarlsonj 	else
128*3431Scarlsonj 		return (0);
129*3431Scarlsonj }
130*3431Scarlsonj 
131*3431Scarlsonj /*
132*3431Scarlsonj  * pkt_get_xid(): returns transaction ID from a DHCP packet.
133*3431Scarlsonj  *
134*3431Scarlsonj  *   input: const PKT *: the packet to examine
135*3431Scarlsonj  *  output: uint_t: the transaction ID (0 if unknown)
136*3431Scarlsonj  */
137*3431Scarlsonj 
138*3431Scarlsonj uint_t
139*3431Scarlsonj pkt_get_xid(const PKT *pkt, boolean_t isv6)
140*3431Scarlsonj {
141*3431Scarlsonj 	if (pkt == NULL)
142*3431Scarlsonj 		return (0);
143*3431Scarlsonj 	if (isv6)
144*3431Scarlsonj 		return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt));
145*3431Scarlsonj 	else
146*3431Scarlsonj 		return (pkt->xid);
147*3431Scarlsonj }
148*3431Scarlsonj 
149*3431Scarlsonj /*
1500Sstevel@tonic-gate  * init_pkt(): initializes and returns a packet of a given type
1510Sstevel@tonic-gate  *
152*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine that will send the packet
1530Sstevel@tonic-gate  *	    uchar_t: the packet type (DHCP message type)
154*3431Scarlsonj  *  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 *
158*3431Scarlsonj init_pkt(dhcp_smach_t *dsmp, uchar_t type)
1590Sstevel@tonic-gate {
160*3431Scarlsonj 	uint_t		mtu;
161*3431Scarlsonj 	dhcp_pkt_t	*dpkt = &dsmp->dsm_send_pkt;
162*3431Scarlsonj 	dhcp_lif_t	*lif = dsmp->dsm_lif;
163*3431Scarlsonj 	dhcp_pif_t	*pif = lif->lif_pif;
1640Sstevel@tonic-gate 	uint32_t	xid;
165*3431Scarlsonj 	boolean_t	isv6;
1660Sstevel@tonic-gate 
167*3431Scarlsonj 	mtu = dsmp->dsm_using_dlpi ? pif->pif_max : lif->lif_max;
168*3431Scarlsonj 	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.
173*3431Scarlsonj 	 *
174*3431Scarlsonj 	 * Note that transaction ID zero is intentionally never assigned.
175*3431Scarlsonj 	 * That's used to represent "no ID."  Also note that transaction IDs
176*3431Scarlsonj 	 * are only 24 bits long in DHCPv6.
1770Sstevel@tonic-gate 	 */
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	do {
1800Sstevel@tonic-gate 		xid = mrand48();
181*3431Scarlsonj 		if (isv6)
182*3431Scarlsonj 			xid &= 0xFFFFFF;
183*3431Scarlsonj 	} while (xid == 0 ||
184*3431Scarlsonj 	    lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL);
185*3431Scarlsonj 
186*3431Scarlsonj 	if (isv6) {
187*3431Scarlsonj 		dhcpv6_message_t *v6;
188*3431Scarlsonj 
189*3431Scarlsonj 		if (mtu != dpkt->pkt_max_len &&
190*3431Scarlsonj 		    (v6 = realloc(dpkt->pkt, mtu)) != NULL) {
191*3431Scarlsonj 			/* LINTED: alignment known to be correct */
192*3431Scarlsonj 			dpkt->pkt = (PKT *)v6;
193*3431Scarlsonj 			dpkt->pkt_max_len = mtu;
194*3431Scarlsonj 		}
195*3431Scarlsonj 
196*3431Scarlsonj 		if (sizeof (*v6) > dpkt->pkt_max_len) {
197*3431Scarlsonj 			dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u",
198*3431Scarlsonj 			    mtu);
199*3431Scarlsonj 			return (NULL);
200*3431Scarlsonj 		}
201*3431Scarlsonj 
202*3431Scarlsonj 		v6 = (dhcpv6_message_t *)dpkt->pkt;
203*3431Scarlsonj 		dpkt->pkt_cur_len = sizeof (*v6);
204*3431Scarlsonj 
205*3431Scarlsonj 		(void) memset(v6, 0, dpkt->pkt_max_len);
206*3431Scarlsonj 
207*3431Scarlsonj 		v6->d6m_msg_type = type;
208*3431Scarlsonj 		DHCPV6_SET_TRANSID(v6, xid);
209*3431Scarlsonj 
210*3431Scarlsonj 		if (dsmp->dsm_cidlen > 0 &&
211*3431Scarlsonj 		    add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid,
212*3431Scarlsonj 		    dsmp->dsm_cidlen) == NULL) {
213*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
214*3431Scarlsonj 			    "init_pkt: cannot insert client ID");
215*3431Scarlsonj 			return (NULL);
216*3431Scarlsonj 		}
217*3431Scarlsonj 
218*3431Scarlsonj 		/* For v6, time starts with the creation of a transaction */
219*3431Scarlsonj 		dsmp->dsm_neg_hrtime = gethrtime();
220*3431Scarlsonj 		dsmp->dsm_newstart_monosec = monosec();
221*3431Scarlsonj 	} else {
222*3431Scarlsonj 		static uint8_t bootmagic[] = BOOTMAGIC;
223*3431Scarlsonj 		PKT *v4;
224*3431Scarlsonj 
225*3431Scarlsonj 		if (mtu != dpkt->pkt_max_len &&
226*3431Scarlsonj 		    (v4 = realloc(dpkt->pkt, mtu)) != NULL) {
227*3431Scarlsonj 			dpkt->pkt = v4;
228*3431Scarlsonj 			dpkt->pkt_max_len = mtu;
229*3431Scarlsonj 		}
2300Sstevel@tonic-gate 
231*3431Scarlsonj 		if (offsetof(PKT, options) > dpkt->pkt_max_len) {
232*3431Scarlsonj 			dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u",
233*3431Scarlsonj 			    mtu);
234*3431Scarlsonj 			return (NULL);
235*3431Scarlsonj 		}
236*3431Scarlsonj 
237*3431Scarlsonj 		v4 = dpkt->pkt;
238*3431Scarlsonj 		dpkt->pkt_cur_len = offsetof(PKT, options);
2390Sstevel@tonic-gate 
240*3431Scarlsonj 		(void) memset(v4, 0, dpkt->pkt_max_len);
241*3431Scarlsonj 		(void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic));
242*3431Scarlsonj 		if (pif->pif_hwlen <= sizeof (v4->chaddr)) {
243*3431Scarlsonj 			v4->hlen  = pif->pif_hwlen;
244*3431Scarlsonj 			(void) memcpy(v4->chaddr, pif->pif_hwaddr,
245*3431Scarlsonj 			    pif->pif_hwlen);
246*3431Scarlsonj 		} else {
247*3431Scarlsonj 			/*
248*3431Scarlsonj 			 * The mac address does not fit in the chaddr
249*3431Scarlsonj 			 * field, thus it can not be sent to the server,
250*3431Scarlsonj 			 * thus server can not unicast the reply. Per
251*3431Scarlsonj 			 * RFC 2131 4.4.1, client can set this bit in
252*3431Scarlsonj 			 * DISCOVER/REQUEST. If the client is already
253*3431Scarlsonj 			 * in BOUND/REBINDING/RENEWING state, do not set
254*3431Scarlsonj 			 * this bit, as it can respond to unicast responses
255*3431Scarlsonj 			 * from server using the 'ciaddr' address.
256*3431Scarlsonj 			 */
257*3431Scarlsonj 			if (type == DISCOVER ||
258*3431Scarlsonj 			    (type == REQUEST && dsmp->dsm_state != RENEWING &&
259*3431Scarlsonj 			    dsmp->dsm_state != REBINDING &&
260*3431Scarlsonj 			    dsmp->dsm_state != BOUND))
261*3431Scarlsonj 				v4->flags = htons(BCAST_MASK);
262*3431Scarlsonj 		}
263*3431Scarlsonj 
264*3431Scarlsonj 		v4->xid   = xid;
265*3431Scarlsonj 		v4->op    = BOOTREQUEST;
266*3431Scarlsonj 		v4->htype = pif->pif_hwtype;
267*3431Scarlsonj 
268*3431Scarlsonj 		if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) {
269*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
270*3431Scarlsonj 			    "init_pkt: cannot set DHCP packet type");
271*3431Scarlsonj 			return (NULL);
272*3431Scarlsonj 		}
273*3431Scarlsonj 
274*3431Scarlsonj 		if (dsmp->dsm_cidlen > 0 &&
275*3431Scarlsonj 		    add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid,
276*3431Scarlsonj 		    dsmp->dsm_cidlen) == NULL) {
277*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
278*3431Scarlsonj 			    "init_pkt: cannot insert client ID");
279*3431Scarlsonj 			return (NULL);
280*3431Scarlsonj 		}
281*3431Scarlsonj 	}
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	return (dpkt);
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate /*
287*3431Scarlsonj  * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t
288*3431Scarlsonj  *
289*3431Scarlsonj  *   input: dhcp_pkt_t *: the packet to remove the option from
290*3431Scarlsonj  *	    uint_t: the type of option being added
291*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
292*3431Scarlsonj  *    note: currently does not work with DHCPv6 suboptions, or to remove
293*3431Scarlsonj  *	    arbitrary option instances.
294*3431Scarlsonj  */
295*3431Scarlsonj 
296*3431Scarlsonj boolean_t
297*3431Scarlsonj remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type)
298*3431Scarlsonj {
299*3431Scarlsonj 	uchar_t		*raw_pkt, *raw_end, *next;
300*3431Scarlsonj 	uint_t		len;
301*3431Scarlsonj 
302*3431Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
303*3431Scarlsonj 	raw_end = raw_pkt + dpkt->pkt_cur_len;
304*3431Scarlsonj 	if (dpkt->pkt_isv6) {
305*3431Scarlsonj 		dhcpv6_option_t d6o;
306*3431Scarlsonj 
307*3431Scarlsonj 		raw_pkt += sizeof (dhcpv6_message_t);
308*3431Scarlsonj 
309*3431Scarlsonj 		opt_type = htons(opt_type);
310*3431Scarlsonj 		while (raw_pkt + sizeof (d6o) <= raw_end) {
311*3431Scarlsonj 			(void) memcpy(&d6o, raw_pkt, sizeof (d6o));
312*3431Scarlsonj 			len = ntohs(d6o.d6o_len) + sizeof (d6o);
313*3431Scarlsonj 			if (len > raw_end - raw_pkt)
314*3431Scarlsonj 				break;
315*3431Scarlsonj 			next = raw_pkt + len;
316*3431Scarlsonj 			if (d6o.d6o_code == opt_type) {
317*3431Scarlsonj 				if (next < raw_end) {
318*3431Scarlsonj 					(void) memmove(raw_pkt, next,
319*3431Scarlsonj 					    raw_end - next);
320*3431Scarlsonj 				}
321*3431Scarlsonj 				dpkt->pkt_cur_len -= len;
322*3431Scarlsonj 				return (B_TRUE);
323*3431Scarlsonj 			}
324*3431Scarlsonj 			raw_pkt = next;
325*3431Scarlsonj 		}
326*3431Scarlsonj 	} else {
327*3431Scarlsonj 		uchar_t *pstart, *padrun;
328*3431Scarlsonj 
329*3431Scarlsonj 		raw_pkt += offsetof(PKT, options);
330*3431Scarlsonj 		pstart = raw_pkt;
331*3431Scarlsonj 
332*3431Scarlsonj 		if (opt_type == CD_END || opt_type == CD_PAD)
333*3431Scarlsonj 			return (B_FALSE);
334*3431Scarlsonj 
335*3431Scarlsonj 		padrun = NULL;
336*3431Scarlsonj 		while (raw_pkt + 1 <= raw_end) {
337*3431Scarlsonj 			if (*raw_pkt == CD_END)
338*3431Scarlsonj 				break;
339*3431Scarlsonj 			if (*raw_pkt == CD_PAD) {
340*3431Scarlsonj 				if (padrun == NULL)
341*3431Scarlsonj 					padrun = raw_pkt;
342*3431Scarlsonj 				raw_pkt++;
343*3431Scarlsonj 				continue;
344*3431Scarlsonj 			}
345*3431Scarlsonj 			if (raw_pkt + 2 > raw_end)
346*3431Scarlsonj 				break;
347*3431Scarlsonj 			len = raw_pkt[1];
348*3431Scarlsonj 			if (len > raw_end - raw_pkt || len < 2)
349*3431Scarlsonj 				break;
350*3431Scarlsonj 			next = raw_pkt + len;
351*3431Scarlsonj 			if (*raw_pkt == opt_type) {
352*3431Scarlsonj 				if (next < raw_end) {
353*3431Scarlsonj 					int toadd = (4 + ((next-pstart)&3) -
354*3431Scarlsonj 					    ((raw_pkt-pstart)&3)) & 3;
355*3431Scarlsonj 					int torem = 4 - toadd;
356*3431Scarlsonj 
357*3431Scarlsonj 					if (torem != 4 && padrun != NULL &&
358*3431Scarlsonj 					    (raw_pkt - padrun) >= torem) {
359*3431Scarlsonj 						raw_pkt -= torem;
360*3431Scarlsonj 						dpkt->pkt_cur_len -= torem;
361*3431Scarlsonj 					} else if (toadd > 0) {
362*3431Scarlsonj 						(void) memset(raw_pkt, CD_PAD,
363*3431Scarlsonj 						    toadd);
364*3431Scarlsonj 						raw_pkt += toadd;
365*3431Scarlsonj 						/* max is not an issue here */
366*3431Scarlsonj 						dpkt->pkt_cur_len += toadd;
367*3431Scarlsonj 					}
368*3431Scarlsonj 					if (raw_pkt != next) {
369*3431Scarlsonj 						(void) memmove(raw_pkt, next,
370*3431Scarlsonj 						    raw_end - next);
371*3431Scarlsonj 					}
372*3431Scarlsonj 				}
373*3431Scarlsonj 				dpkt->pkt_cur_len -= len;
374*3431Scarlsonj 				return (B_TRUE);
375*3431Scarlsonj 			}
376*3431Scarlsonj 			padrun = NULL;
377*3431Scarlsonj 			raw_pkt = next;
378*3431Scarlsonj 		}
379*3431Scarlsonj 	}
380*3431Scarlsonj 	return (B_FALSE);
381*3431Scarlsonj }
382*3431Scarlsonj 
383*3431Scarlsonj /*
384*3431Scarlsonj  * update_v6opt_len(): updates the length field of a DHCPv6 option.
385*3431Scarlsonj  *
386*3431Scarlsonj  *   input: dhcpv6_option_t *: option to be updated
387*3431Scarlsonj  *	    int: number of octets to add or subtract
388*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
389*3431Scarlsonj  */
390*3431Scarlsonj 
391*3431Scarlsonj boolean_t
392*3431Scarlsonj update_v6opt_len(dhcpv6_option_t *opt, int adjust)
393*3431Scarlsonj {
394*3431Scarlsonj 	dhcpv6_option_t optval;
395*3431Scarlsonj 
396*3431Scarlsonj 	(void) memcpy(&optval, opt, sizeof (optval));
397*3431Scarlsonj 	adjust += ntohs(optval.d6o_len);
398*3431Scarlsonj 	if (adjust < 0 || adjust > UINT16_MAX) {
399*3431Scarlsonj 		return (B_FALSE);
400*3431Scarlsonj 	} else {
401*3431Scarlsonj 		optval.d6o_len = htons(adjust);
402*3431Scarlsonj 		(void) memcpy(opt, &optval, sizeof (optval));
403*3431Scarlsonj 		return (B_TRUE);
404*3431Scarlsonj 	}
405*3431Scarlsonj }
406*3431Scarlsonj 
407*3431Scarlsonj /*
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
411*3431Scarlsonj  *	    uint_t: the type of option being added
4120Sstevel@tonic-gate  *	    const void *: the value of that option
413*3431Scarlsonj  *	    uint_t: the length of the value of the option
414*3431Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
4150Sstevel@tonic-gate  */
4160Sstevel@tonic-gate 
417*3431Scarlsonj void *
418*3431Scarlsonj add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val,
419*3431Scarlsonj     uint_t opt_len)
4200Sstevel@tonic-gate {
421*3431Scarlsonj 	uchar_t		*raw_pkt;
422*3431Scarlsonj 	int		req_len;
423*3431Scarlsonj 	void		*optr;
424*3431Scarlsonj 
425*3431Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
426*3431Scarlsonj 	optr = raw_pkt + dpkt->pkt_cur_len;
427*3431Scarlsonj 	if (dpkt->pkt_isv6) {
428*3431Scarlsonj 		dhcpv6_option_t d6o;
429*3431Scarlsonj 
430*3431Scarlsonj 		req_len = opt_len + sizeof (d6o);
431*3431Scarlsonj 
432*3431Scarlsonj 		if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
433*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
434*3431Scarlsonj 			    "add_pkt_opt: not enough room for v6 option %u in "
435*3431Scarlsonj 			    "packet (%u + %u > %u)", opt_type,
436*3431Scarlsonj 			    dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
437*3431Scarlsonj 			return (NULL);
438*3431Scarlsonj 		}
439*3431Scarlsonj 		d6o.d6o_code = htons(opt_type);
440*3431Scarlsonj 		d6o.d6o_len = htons(opt_len);
441*3431Scarlsonj 		(void) memcpy(&raw_pkt[dpkt->pkt_cur_len], &d6o, sizeof (d6o));
442*3431Scarlsonj 		dpkt->pkt_cur_len += sizeof (d6o);
443*3431Scarlsonj 		if (opt_len > 0) {
444*3431Scarlsonj 			(void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val,
445*3431Scarlsonj 			    opt_len);
446*3431Scarlsonj 			dpkt->pkt_cur_len += opt_len;
447*3431Scarlsonj 		}
448*3431Scarlsonj 	} else {
449*3431Scarlsonj 		req_len = opt_len + 2; /* + 2 for code & length bytes */
450*3431Scarlsonj 
451*3431Scarlsonj 		/* CD_END and CD_PAD options don't have a length field */
452*3431Scarlsonj 		if (opt_type == CD_END || opt_type == CD_PAD) {
453*3431Scarlsonj 			req_len = 1;
454*3431Scarlsonj 		} else if (opt_val == NULL) {
455*3431Scarlsonj 			dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is "
456*3431Scarlsonj 			    "missing required value", opt_type);
457*3431Scarlsonj 			return (NULL);
458*3431Scarlsonj 		}
459*3431Scarlsonj 
460*3431Scarlsonj 		if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) {
461*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
462*3431Scarlsonj 			    "add_pkt_opt: not enough room for v4 option %u in "
463*3431Scarlsonj 			    "packet", opt_type);
464*3431Scarlsonj 			return (NULL);
465*3431Scarlsonj 		}
466*3431Scarlsonj 
467*3431Scarlsonj 		raw_pkt[dpkt->pkt_cur_len++] = opt_type;
4680Sstevel@tonic-gate 
469*3431Scarlsonj 		if (req_len > 1) {
470*3431Scarlsonj 			raw_pkt[dpkt->pkt_cur_len++] = opt_len;
471*3431Scarlsonj 			if (opt_len > 0) {
472*3431Scarlsonj 				(void) memcpy(&raw_pkt[dpkt->pkt_cur_len],
473*3431Scarlsonj 				    opt_val, opt_len);
474*3431Scarlsonj 				dpkt->pkt_cur_len += opt_len;
475*3431Scarlsonj 			}
476*3431Scarlsonj 		}
477*3431Scarlsonj 	}
478*3431Scarlsonj 	return (optr);
479*3431Scarlsonj }
4800Sstevel@tonic-gate 
481*3431Scarlsonj /*
482*3431Scarlsonj  * add_pkt_subopt(): adds an option to a dhcp_pkt_t option.  DHCPv6-specific,
483*3431Scarlsonj  *		     but could be extended to IPv4 DHCP if necessary.  Assumes
484*3431Scarlsonj  *		     that if the parent isn't a top-level option, the caller
485*3431Scarlsonj  *		     will adjust any upper-level options recursively using
486*3431Scarlsonj  *		     update_v6opt_len.
487*3431Scarlsonj  *
488*3431Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the suboption to
489*3431Scarlsonj  *	    dhcpv6_option_t *: the start of the option to that should contain
490*3431Scarlsonj  *			       it (parent)
491*3431Scarlsonj  *	    uint_t: the type of suboption being added
492*3431Scarlsonj  *	    const void *: the value of that option
493*3431Scarlsonj  *	    uint_t: the length of the value of the option
494*3431Scarlsonj  *  output: void *: pointer to the suboption that was added, or NULL on
495*3431Scarlsonj  *		    failure.
496*3431Scarlsonj  */
497*3431Scarlsonj 
498*3431Scarlsonj void *
499*3431Scarlsonj add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type,
500*3431Scarlsonj     const void *opt_val, uint_t opt_len)
501*3431Scarlsonj {
502*3431Scarlsonj 	uchar_t		*raw_pkt;
503*3431Scarlsonj 	int		req_len;
504*3431Scarlsonj 	void		*optr;
505*3431Scarlsonj 	dhcpv6_option_t d6o;
506*3431Scarlsonj 	uchar_t		*optend;
507*3431Scarlsonj 	int		olen;
508*3431Scarlsonj 
509*3431Scarlsonj 	if (!dpkt->pkt_isv6)
510*3431Scarlsonj 		return (NULL);
511*3431Scarlsonj 
512*3431Scarlsonj 	raw_pkt = (uchar_t *)dpkt->pkt;
513*3431Scarlsonj 	req_len = opt_len + sizeof (d6o);
514*3431Scarlsonj 
515*3431Scarlsonj 	if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
516*3431Scarlsonj 		dhcpmsg(MSG_WARNING,
517*3431Scarlsonj 		    "add_pkt_subopt: not enough room for v6 suboption %u in "
518*3431Scarlsonj 		    "packet (%u + %u > %u)", opt_type,
519*3431Scarlsonj 		    dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
520*3431Scarlsonj 		return (NULL);
5210Sstevel@tonic-gate 	}
5220Sstevel@tonic-gate 
523*3431Scarlsonj 	/*
524*3431Scarlsonj 	 * Update the parent option to include room for this option,
525*3431Scarlsonj 	 * and compute the insertion point.
526*3431Scarlsonj 	 */
527*3431Scarlsonj 	(void) memcpy(&d6o, parentopt, sizeof (d6o));
528*3431Scarlsonj 	olen = ntohs(d6o.d6o_len);
529*3431Scarlsonj 	optend = (uchar_t *)(parentopt + 1) + olen;
530*3431Scarlsonj 	olen += req_len;
531*3431Scarlsonj 	d6o.d6o_len = htons(olen);
532*3431Scarlsonj 	(void) memcpy(parentopt, &d6o, sizeof (d6o));
5330Sstevel@tonic-gate 
534*3431Scarlsonj 	/*
535*3431Scarlsonj 	 * If there's anything at the end to move, then move it.  Also bump up
536*3431Scarlsonj 	 * the packet size.
537*3431Scarlsonj 	 */
538*3431Scarlsonj 	if (optend < raw_pkt + dpkt->pkt_cur_len) {
539*3431Scarlsonj 		(void) memmove(optend + req_len, optend,
540*3431Scarlsonj 		    (raw_pkt + dpkt->pkt_cur_len) - optend);
5410Sstevel@tonic-gate 	}
542*3431Scarlsonj 	dpkt->pkt_cur_len += req_len;
543*3431Scarlsonj 
544*3431Scarlsonj 	/*
545*3431Scarlsonj 	 * Now format the suboption and add it in.
546*3431Scarlsonj 	 */
547*3431Scarlsonj 	optr = optend;
548*3431Scarlsonj 	d6o.d6o_code = htons(opt_type);
549*3431Scarlsonj 	d6o.d6o_len = htons(opt_len);
550*3431Scarlsonj 	(void) memcpy(optend, &d6o, sizeof (d6o));
551*3431Scarlsonj 	if (opt_len > 0)
552*3431Scarlsonj 		(void) memcpy(optend + sizeof (d6o), opt_val, opt_len);
553*3431Scarlsonj 	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
560*3431Scarlsonj  *	    uint_t: the type of option being added
5610Sstevel@tonic-gate  *	    uint16_t: the value of that option
562*3431Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
5630Sstevel@tonic-gate  */
5640Sstevel@tonic-gate 
565*3431Scarlsonj void *
566*3431Scarlsonj add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value)
5670Sstevel@tonic-gate {
568*3431Scarlsonj 	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
575*3431Scarlsonj  *	    uint_t: the type of option being added
5760Sstevel@tonic-gate  *	    uint32_t: the value of that option
577*3431Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
578*3431Scarlsonj  */
579*3431Scarlsonj 
580*3431Scarlsonj void *
581*3431Scarlsonj add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value)
582*3431Scarlsonj {
583*3431Scarlsonj 	return (add_pkt_opt(dpkt, opt_type, &opt_value, 4));
584*3431Scarlsonj }
585*3431Scarlsonj 
586*3431Scarlsonj /*
587*3431Scarlsonj  * add_pkt_prl(): adds the parameter request option to the packet
588*3431Scarlsonj  *
589*3431Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the option to
590*3431Scarlsonj  *	    dhcp_smach_t *: state machine with request option
591*3431Scarlsonj  *  output: void *: pointer to the option that was added, or NULL on failure.
5920Sstevel@tonic-gate  */
5930Sstevel@tonic-gate 
594*3431Scarlsonj void *
595*3431Scarlsonj add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
5960Sstevel@tonic-gate {
597*3431Scarlsonj 	uint_t len;
598*3431Scarlsonj 
599*3431Scarlsonj 	if (dsmp->dsm_prllen == 0)
600*3431Scarlsonj 		return (0);
601*3431Scarlsonj 
602*3431Scarlsonj 	if (dpkt->pkt_isv6) {
603*3431Scarlsonj 		uint16_t *prl;
604*3431Scarlsonj 
605*3431Scarlsonj 		/*
606*3431Scarlsonj 		 * RFC 3315 requires that we include the option, even if we
607*3431Scarlsonj 		 * have nothing to request.
608*3431Scarlsonj 		 */
609*3431Scarlsonj 		if (dsmp->dsm_prllen == 0)
610*3431Scarlsonj 			prl = NULL;
611*3431Scarlsonj 		else
612*3431Scarlsonj 			prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t));
613*3431Scarlsonj 
614*3431Scarlsonj 		for (len = 0; len < dsmp->dsm_prllen; len++)
615*3431Scarlsonj 			prl[len] = htons(dsmp->dsm_prl[len]);
616*3431Scarlsonj 		return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl,
617*3431Scarlsonj 		    len * sizeof (uint16_t)));
618*3431Scarlsonj 	} else {
619*3431Scarlsonj 		uint8_t *prl = alloca(dsmp->dsm_prllen);
620*3431Scarlsonj 
621*3431Scarlsonj 		for (len = 0; len < dsmp->dsm_prllen; len++)
622*3431Scarlsonj 			prl[len] = dsmp->dsm_prl[len];
623*3431Scarlsonj 		return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len));
624*3431Scarlsonj 	}
6250Sstevel@tonic-gate }
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate /*
628*3431Scarlsonj  * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR
629*3431Scarlsonj  *		  (DHCPv6) options to the packet to represent the given LIF.
6300Sstevel@tonic-gate  *
631*3431Scarlsonj  *   input: dhcp_pkt_t *: the packet to add the options to
632*3431Scarlsonj  *	    dhcp_lif_t *: the logical interface to represent
633*3431Scarlsonj  *	    int: status code (unused for IPv4 DHCP)
634*3431Scarlsonj  *	    const char *: message to include with status option, or NULL
635*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
6360Sstevel@tonic-gate  */
6370Sstevel@tonic-gate 
638*3431Scarlsonj boolean_t
639*3431Scarlsonj add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg)
6400Sstevel@tonic-gate {
641*3431Scarlsonj 	if (lif->lif_pif->pif_isv6) {
642*3431Scarlsonj 		dhcp_smach_t *dsmp;
643*3431Scarlsonj 		dhcpv6_message_t *d6m;
644*3431Scarlsonj 		dhcpv6_ia_na_t d6in;
645*3431Scarlsonj 		dhcpv6_iaaddr_t d6ia;
646*3431Scarlsonj 		uint32_t iaid;
647*3431Scarlsonj 		uint16_t *statusopt;
648*3431Scarlsonj 		dhcpv6_option_t *d6o, *d6so;
649*3431Scarlsonj 		uint_t olen;
650*3431Scarlsonj 
651*3431Scarlsonj 		/*
652*3431Scarlsonj 		 * Currently, we support just one IAID related to the primary
653*3431Scarlsonj 		 * LIF on the state machine.
654*3431Scarlsonj 		 */
655*3431Scarlsonj 		dsmp = lif->lif_lease->dl_smach;
656*3431Scarlsonj 		iaid = dsmp->dsm_lif->lif_iaid;
657*3431Scarlsonj 		iaid = htonl(iaid);
658*3431Scarlsonj 
659*3431Scarlsonj 		d6m = (dhcpv6_message_t *)dpkt->pkt;
6600Sstevel@tonic-gate 
661*3431Scarlsonj 		/*
662*3431Scarlsonj 		 * Find or create the IA_NA needed for this LIF.  If we
663*3431Scarlsonj 		 * supported IA_TA, we'd check the IFF_TEMPORARY bit here.
664*3431Scarlsonj 		 */
665*3431Scarlsonj 		d6o = NULL;
666*3431Scarlsonj 		while ((d6o = dhcpv6_find_option(d6m + 1,
667*3431Scarlsonj 		    dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA,
668*3431Scarlsonj 		    &olen)) != NULL) {
669*3431Scarlsonj 			if (olen < sizeof (d6in))
670*3431Scarlsonj 				continue;
671*3431Scarlsonj 			(void) memcpy(&d6in, d6o, sizeof (d6in));
672*3431Scarlsonj 			if (d6in.d6in_iaid == iaid)
673*3431Scarlsonj 				break;
674*3431Scarlsonj 		}
675*3431Scarlsonj 		if (d6o == NULL) {
676*3431Scarlsonj 			d6in.d6in_iaid = iaid;
677*3431Scarlsonj 			d6in.d6in_t1 = 0;
678*3431Scarlsonj 			d6in.d6in_t2 = 0;
679*3431Scarlsonj 			d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
680*3431Scarlsonj 			    (dhcpv6_option_t *)&d6in + 1,
681*3431Scarlsonj 			    sizeof (d6in) - sizeof (*d6o));
682*3431Scarlsonj 			if (d6o == NULL)
683*3431Scarlsonj 				return (B_FALSE);
684*3431Scarlsonj 		}
6850Sstevel@tonic-gate 
686*3431Scarlsonj 		/*
687*3431Scarlsonj 		 * Now add the IAADDR suboption for this LIF.  No need to
688*3431Scarlsonj 		 * search here, as we know that this is unique.
689*3431Scarlsonj 		 */
690*3431Scarlsonj 		d6ia.d6ia_addr = lif->lif_v6addr;
6910Sstevel@tonic-gate 
692*3431Scarlsonj 		/*
693*3431Scarlsonj 		 * For Release and Decline, we zero out the lifetime.  For
694*3431Scarlsonj 		 * Renew and Rebind, we report the original time as the
695*3431Scarlsonj 		 * preferred and valid lifetimes.
696*3431Scarlsonj 		 */
697*3431Scarlsonj 		if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE ||
698*3431Scarlsonj 		    d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) {
699*3431Scarlsonj 			d6ia.d6ia_preflife = 0;
700*3431Scarlsonj 			d6ia.d6ia_vallife = 0;
701*3431Scarlsonj 		} else {
702*3431Scarlsonj 			d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start);
703*3431Scarlsonj 			d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start);
704*3431Scarlsonj 		}
705*3431Scarlsonj 		d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR,
706*3431Scarlsonj 		    (dhcpv6_option_t *)&d6ia + 1,
707*3431Scarlsonj 		    sizeof (d6ia) - sizeof (*d6o));
708*3431Scarlsonj 		if (d6so == NULL)
709*3431Scarlsonj 			return (B_FALSE);
7100Sstevel@tonic-gate 
711*3431Scarlsonj 		/*
712*3431Scarlsonj 		 * Add a status code suboption to the IAADDR to tell the server
713*3431Scarlsonj 		 * why we're declining the address.  Note that we must manually
714*3431Scarlsonj 		 * update the enclosing IA_NA, as add_pkt_subopt doesn't know
715*3431Scarlsonj 		 * how to do that.
716*3431Scarlsonj 		 */
717*3431Scarlsonj 		if (status != DHCPV6_STAT_SUCCESS || msg != NULL) {
718*3431Scarlsonj 			olen = sizeof (*statusopt) +
719*3431Scarlsonj 			    (msg == NULL ? 0 : strlen(msg));
720*3431Scarlsonj 			statusopt = alloca(olen);
721*3431Scarlsonj 			*statusopt = htons(status);
722*3431Scarlsonj 			if (msg != NULL) {
723*3431Scarlsonj 				(void) memcpy((char *)(statusopt + 1), msg,
724*3431Scarlsonj 				    olen - sizeof (*statusopt));
725*3431Scarlsonj 			}
726*3431Scarlsonj 			d6so = add_pkt_subopt(dpkt, d6so,
727*3431Scarlsonj 			    DHCPV6_OPT_STATUS_CODE, statusopt, olen);
728*3431Scarlsonj 			if (d6so != NULL) {
729*3431Scarlsonj 				/*
730*3431Scarlsonj 				 * Update for length of suboption header and
731*3431Scarlsonj 				 * suboption contents.
732*3431Scarlsonj 				 */
733*3431Scarlsonj 				(void) update_v6opt_len(d6o, sizeof (*d6so) +
734*3431Scarlsonj 				    olen);
735*3431Scarlsonj 			}
736*3431Scarlsonj 		}
737*3431Scarlsonj 	} else {
738*3431Scarlsonj 		/*
739*3431Scarlsonj 		 * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option.
740*3431Scarlsonj 		 * In all other cases (RELEASE and REQUEST), we need to set
741*3431Scarlsonj 		 * ciadr.
742*3431Scarlsonj 		 */
743*3431Scarlsonj 		if (pkt_send_type(dpkt) == DECLINE) {
744*3431Scarlsonj 			if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
745*3431Scarlsonj 			    lif->lif_addr))
746*3431Scarlsonj 				return (B_FALSE);
747*3431Scarlsonj 		} else {
748*3431Scarlsonj 			dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
749*3431Scarlsonj 		}
750*3431Scarlsonj 
751*3431Scarlsonj 		/*
752*3431Scarlsonj 		 * It's not too worrisome if the message fails to fit in the
753*3431Scarlsonj 		 * packet.  The result will still be valid.
754*3431Scarlsonj 		 */
755*3431Scarlsonj 		if (msg != NULL)
756*3431Scarlsonj 			(void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
757*3431Scarlsonj 			    strlen(msg) + 1);
7580Sstevel@tonic-gate 	}
759*3431Scarlsonj 	return (B_TRUE);
7600Sstevel@tonic-gate }
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate /*
763*3431Scarlsonj  * free_pkt_entry(): frees a packet list list entry
7640Sstevel@tonic-gate  *
765*3431Scarlsonj  *   input: PKT_LIST *: the packet list entry to free
766*3431Scarlsonj  *  output: void
7670Sstevel@tonic-gate  */
768*3431Scarlsonj void
769*3431Scarlsonj free_pkt_entry(PKT_LIST *plp)
7700Sstevel@tonic-gate {
771*3431Scarlsonj 	if (plp != NULL) {
772*3431Scarlsonj 		free(plp->pkt);
773*3431Scarlsonj 		free(plp);
774*3431Scarlsonj 	}
7750Sstevel@tonic-gate }
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate /*
778*3431Scarlsonj  * 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
785*3431Scarlsonj free_pkt_list(PKT_LIST **head)
7860Sstevel@tonic-gate {
787*3431Scarlsonj 	PKT_LIST *plp;
7880Sstevel@tonic-gate 
789*3431Scarlsonj 	while ((plp = *head) != NULL) {
790*3431Scarlsonj 		remque(plp);
791*3431Scarlsonj 		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  *
798*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine with a packet to send
799*3431Scarlsonj  *  output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise
8000Sstevel@tonic-gate  */
8010Sstevel@tonic-gate 
802*3431Scarlsonj static boolean_t
803*3431Scarlsonj send_pkt_internal(dhcp_smach_t *dsmp)
8040Sstevel@tonic-gate {
8050Sstevel@tonic-gate 	ssize_t		n_bytes;
806*3431Scarlsonj 	dhcp_lif_t	*lif = dsmp->dsm_lif;
807*3431Scarlsonj 	dhcp_pif_t	*pif = lif->lif_pif;
808*3431Scarlsonj 	dhcp_pkt_t	*dpkt = &dsmp->dsm_send_pkt;
809*3431Scarlsonj 	uchar_t		ptype = pkt_send_type(dpkt);
810*3431Scarlsonj 	const char	*pkt_name;
811*3431Scarlsonj 	struct iovec	iov;
812*3431Scarlsonj 	struct msghdr	msg;
813*3431Scarlsonj 	struct cmsghdr	*cmsg;
814*3431Scarlsonj 	struct in6_pktinfo *ipi6;
815*3431Scarlsonj 	boolean_t	ismcast;
816*3431Scarlsonj 
817*3431Scarlsonj 	/*
818*3431Scarlsonj 	 * Timer should not be running at the point we go to send a packet.
819*3431Scarlsonj 	 */
820*3431Scarlsonj 	if (dsmp->dsm_retrans_timer != -1) {
821*3431Scarlsonj 		dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit "
822*3431Scarlsonj 		    "timer on %s", dsmp->dsm_name);
823*3431Scarlsonj 		stop_pkt_retransmission(dsmp);
824*3431Scarlsonj 	}
825*3431Scarlsonj 
826*3431Scarlsonj 	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 
836*3431Scarlsonj 	if (dsmp->dsm_send_timeout != 0) {
837*3431Scarlsonj 		if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq,
838*3431Scarlsonj 		    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
843*3431Scarlsonj 			hold_smach(dsmp);
844*3431Scarlsonj 	}
845*3431Scarlsonj 
846*3431Scarlsonj 	if (dpkt->pkt_isv6) {
847*3431Scarlsonj 		hrtime_t delta;
848*3431Scarlsonj 
849*3431Scarlsonj 		/*
850*3431Scarlsonj 		 * Convert current time into centiseconds since transaction
851*3431Scarlsonj 		 * started.  This is what DHCPv6 expects to see in the Elapsed
852*3431Scarlsonj 		 * Time option.
853*3431Scarlsonj 		 */
854*3431Scarlsonj 		delta = (gethrtime() - dsmp->dsm_neg_hrtime) /
855*3431Scarlsonj 		    (NANOSEC / 100);
856*3431Scarlsonj 		if (delta > DHCPV6_FOREVER)
857*3431Scarlsonj 			delta = DHCPV6_FOREVER;
858*3431Scarlsonj 		(void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME);
859*3431Scarlsonj 		(void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME,
860*3431Scarlsonj 		    htons(delta));
861*3431Scarlsonj 	} else {
862*3431Scarlsonj 		/*
863*3431Scarlsonj 		 * set the `pkt->secs' field depending on the type of packet.
864*3431Scarlsonj 		 * it should be zero, except in the following cases:
865*3431Scarlsonj 		 *
866*3431Scarlsonj 		 * DISCOVER:	set to the number of seconds since we started
867*3431Scarlsonj 		 *		trying to obtain a lease.
868*3431Scarlsonj 		 *
869*3431Scarlsonj 		 * INFORM:	set to the number of seconds since we started
870*3431Scarlsonj 		 *		trying to get configuration parameters.
871*3431Scarlsonj 		 *
872*3431Scarlsonj 		 * REQUEST:	if in the REQUESTING state, then same value as
873*3431Scarlsonj 		 *		DISCOVER, otherwise the number of seconds
874*3431Scarlsonj 		 *		since we started trying to obtain a lease.
875*3431Scarlsonj 		 *
876*3431Scarlsonj 		 * we also set `dsm_newstart_monosec', to the time we sent a
877*3431Scarlsonj 		 * REQUEST or DISCOVER packet, so we know the lease start
878*3431Scarlsonj 		 * time (the DISCOVER case is for handling BOOTP servers).
879*3431Scarlsonj 		 */
880*3431Scarlsonj 
881*3431Scarlsonj 		switch (ptype) {
882*3431Scarlsonj 
883*3431Scarlsonj 		case DISCOVER:
884*3431Scarlsonj 			dsmp->dsm_newstart_monosec = monosec();
885*3431Scarlsonj 			dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec -
886*3431Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime);
887*3431Scarlsonj 			dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
888*3431Scarlsonj 			break;
889*3431Scarlsonj 
890*3431Scarlsonj 		case INFORM:
891*3431Scarlsonj 			dpkt->pkt->secs = htons(monosec() -
892*3431Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime));
893*3431Scarlsonj 			break;
894*3431Scarlsonj 
895*3431Scarlsonj 		case REQUEST:
896*3431Scarlsonj 			dsmp->dsm_newstart_monosec = monosec();
897*3431Scarlsonj 
898*3431Scarlsonj 			if (dsmp->dsm_state == REQUESTING) {
899*3431Scarlsonj 				dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
900*3431Scarlsonj 				break;
901*3431Scarlsonj 			}
902*3431Scarlsonj 
903*3431Scarlsonj 			dpkt->pkt->secs = htons(monosec() -
904*3431Scarlsonj 			    hrtime_to_monosec(dsmp->dsm_neg_hrtime));
905*3431Scarlsonj 			break;
906*3431Scarlsonj 
907*3431Scarlsonj 		default:
908*3431Scarlsonj 			dpkt->pkt->secs = htons(0);
909*3431Scarlsonj 			break;
910*3431Scarlsonj 		}
9110Sstevel@tonic-gate 	}
9120Sstevel@tonic-gate 
913*3431Scarlsonj 	if (dpkt->pkt_isv6) {
914*3431Scarlsonj 		struct sockaddr_in6 sin6;
915*3431Scarlsonj 
916*3431Scarlsonj 		(void) memset(&iov, 0, sizeof (iov));
917*3431Scarlsonj 		iov.iov_base = dpkt->pkt;
918*3431Scarlsonj 		iov.iov_len = dpkt->pkt_cur_len;
919*3431Scarlsonj 
920*3431Scarlsonj 		(void) memset(&msg, 0, sizeof (msg));
921*3431Scarlsonj 		msg.msg_name = &dsmp->dsm_send_dest.v6;
922*3431Scarlsonj 		msg.msg_namelen = sizeof (struct sockaddr_in6);
923*3431Scarlsonj 		msg.msg_iov = &iov;
924*3431Scarlsonj 		msg.msg_iovlen = 1;
925*3431Scarlsonj 
926*3431Scarlsonj 		/*
927*3431Scarlsonj 		 * If the address that's requested cannot be reached, then fall
928*3431Scarlsonj 		 * back to the multcast address.
929*3431Scarlsonj 		 */
930*3431Scarlsonj 		if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) {
931*3431Scarlsonj 			ismcast = B_TRUE;
932*3431Scarlsonj 		} else {
933*3431Scarlsonj 			struct dstinforeq dinfo;
934*3431Scarlsonj 			struct strioctl str;
9350Sstevel@tonic-gate 
936*3431Scarlsonj 			ismcast = B_FALSE;
937*3431Scarlsonj 			(void) memset(&dinfo, 0, sizeof (dinfo));
938*3431Scarlsonj 			dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr;
939*3431Scarlsonj 			str.ic_cmd = SIOCGDSTINFO;
940*3431Scarlsonj 			str.ic_timout = 0;
941*3431Scarlsonj 			str.ic_len = sizeof (dinfo);
942*3431Scarlsonj 			str.ic_dp = (char *)&dinfo;
943*3431Scarlsonj 			if (ioctl(v6_sock_fd, I_STR, &str) == -1) {
944*3431Scarlsonj 				dhcpmsg(MSG_ERR,
945*3431Scarlsonj 				    "send_pkt_internal: ioctl SIOCGDSTINFO");
946*3431Scarlsonj 			} else if (!dinfo.dir_dreachable) {
947*3431Scarlsonj 				char abuf[INET6_ADDRSTRLEN];
9480Sstevel@tonic-gate 
949*3431Scarlsonj 				dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is "
950*3431Scarlsonj 				    "not reachable; using multicast instead",
951*3431Scarlsonj 				    inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf,
952*3431Scarlsonj 				    sizeof (abuf)));
953*3431Scarlsonj 				sin6 = dsmp->dsm_send_dest.v6;
954*3431Scarlsonj 				sin6.sin6_addr =
955*3431Scarlsonj 				    ipv6_all_dhcp_relay_and_servers;
956*3431Scarlsonj 				msg.msg_name = &sin6;
957*3431Scarlsonj 				ismcast = B_TRUE;
958*3431Scarlsonj 			}
9590Sstevel@tonic-gate 		}
9600Sstevel@tonic-gate 
961*3431Scarlsonj 		/*
962*3431Scarlsonj 		 * Make room for our ancillary data option as well as a dummy
963*3431Scarlsonj 		 * option used by CMSG_NXTHDR.
964*3431Scarlsonj 		 */
965*3431Scarlsonj 		msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT +
966*3431Scarlsonj 		    sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg);
967*3431Scarlsonj 		msg.msg_control = alloca(msg.msg_controllen);
968*3431Scarlsonj 		cmsg = CMSG_FIRSTHDR(&msg);
969*3431Scarlsonj 		cmsg->cmsg_level = IPPROTO_IPV6;
970*3431Scarlsonj 		cmsg->cmsg_type = IPV6_PKTINFO;
971*3431Scarlsonj 		/* LINTED: alignment */
972*3431Scarlsonj 		ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
973*3431Scarlsonj 		if (ismcast)
974*3431Scarlsonj 			ipi6->ipi6_addr = lif->lif_v6addr;
975*3431Scarlsonj 		else
976*3431Scarlsonj 			ipi6->ipi6_addr = my_in6addr_any;
977*3431Scarlsonj 		ipi6->ipi6_ifindex = lif->lif_pif->pif_index;
978*3431Scarlsonj 		cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg;
9790Sstevel@tonic-gate 
980*3431Scarlsonj 		/*
981*3431Scarlsonj 		 * Now correct the control message length.
982*3431Scarlsonj 		 */
983*3431Scarlsonj 		cmsg = CMSG_NXTHDR(&msg, cmsg);
984*3431Scarlsonj 		msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control;
9850Sstevel@tonic-gate 
986*3431Scarlsonj 		n_bytes = sendmsg(v6_sock_fd, &msg, 0);
987*3431Scarlsonj 	} else {
988*3431Scarlsonj 		if (dsmp->dsm_using_dlpi) {
989*3431Scarlsonj 			n_bytes = dlpi_sendto(pif->pif_dlpi_fd, dpkt->pkt,
990*3431Scarlsonj 			    dpkt->pkt_cur_len, &dsmp->dsm_send_dest.v4,
991*3431Scarlsonj 			    pif->pif_daddr, pif->pif_dlen);
992*3431Scarlsonj 			/* dlpi_sendto calls putmsg */
993*3431Scarlsonj 			if (n_bytes == 0)
994*3431Scarlsonj 				n_bytes = dpkt->pkt_cur_len;
995*3431Scarlsonj 		} else {
996*3431Scarlsonj 			n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt,
997*3431Scarlsonj 			    dpkt->pkt_cur_len, 0,
998*3431Scarlsonj 			    (struct sockaddr *)&dsmp->dsm_send_dest.v4,
999*3431Scarlsonj 			    sizeof (struct sockaddr_in));
1000*3431Scarlsonj 		}
10010Sstevel@tonic-gate 	}
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	if (n_bytes != dpkt->pkt_cur_len) {
1004*3431Scarlsonj 		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)",
1010*3431Scarlsonj 			    pkt_name, dsmp->dsm_send_timeout / MILLISEC);
1011*3431Scarlsonj 		return (B_FALSE);
10120Sstevel@tonic-gate 	}
10130Sstevel@tonic-gate 
1014*3431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name,
1015*3431Scarlsonj 	    pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name);
10160Sstevel@tonic-gate 
1017*3431Scarlsonj 	dsmp->dsm_packet_sent++;
1018*3431Scarlsonj 	dsmp->dsm_sent++;
1019*3431Scarlsonj 	return (B_TRUE);
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate /*
1023*3431Scarlsonj  * send_pkt(): sends a packet out
10240Sstevel@tonic-gate  *
1025*3431Scarlsonj  *   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)
1031*3431Scarlsonj  *  output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
10320Sstevel@tonic-gate  */
10330Sstevel@tonic-gate 
1034*3431Scarlsonj boolean_t
1035*3431Scarlsonj 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 
1045*3431Scarlsonj 	dsmp->dsm_packet_sent = 0;
10460Sstevel@tonic-gate 
1047*3431Scarlsonj 	(void) memset(&dsmp->dsm_send_dest.v4, 0,
1048*3431Scarlsonj 	    sizeof (dsmp->dsm_send_dest.v4));
1049*3431Scarlsonj 	dsmp->dsm_send_dest.v4.sin_addr.s_addr	= dest;
1050*3431Scarlsonj 	dsmp->dsm_send_dest.v4.sin_family	= AF_INET;
1051*3431Scarlsonj 	dsmp->dsm_send_dest.v4.sin_port		= htons(IPPORT_BOOTPS);
1052*3431Scarlsonj 	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 
1059*3431Scarlsonj 	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 
1066*3431Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
1067*3431Scarlsonj 
1068*3431Scarlsonj 	if (stop == NULL)
1069*3431Scarlsonj 		dsmp->dsm_send_timeout = 0;	/* prevents retransmissions */
1070*3431Scarlsonj 	else
1071*3431Scarlsonj 		next_retransmission(dsmp, B_TRUE, B_FALSE);
1072*3431Scarlsonj 
1073*3431Scarlsonj 	return (send_pkt_internal(dsmp));
1074*3431Scarlsonj }
1075*3431Scarlsonj 
1076*3431Scarlsonj /*
1077*3431Scarlsonj  * send_pkt_v6(): sends a DHCPv6 packet out
1078*3431Scarlsonj  *
1079*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine sending the packet
1080*3431Scarlsonj  *	    dhcp_pkt_t *: the packet to send out
1081*3431Scarlsonj  *	    in6_addr_t: the destination IPv6 address for the packet
1082*3431Scarlsonj  *	    stop_func_t *: a pointer to function to indicate when to stop
1083*3431Scarlsonj  *			   retransmitting the packet (if NULL, packet is
1084*3431Scarlsonj  *			   not retransmitted)
1085*3431Scarlsonj  *	    uint_t: Initial Retransmit Timer value
1086*3431Scarlsonj  *	    uint_t: Maximum Retransmit Timer value, zero if none
1087*3431Scarlsonj  *  output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
1088*3431Scarlsonj  */
1089*3431Scarlsonj 
1090*3431Scarlsonj boolean_t
1091*3431Scarlsonj send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest,
1092*3431Scarlsonj     stop_func_t *stop, uint_t irt, uint_t mrt)
1093*3431Scarlsonj {
1094*3431Scarlsonj 	dsmp->dsm_packet_sent = 0;
1095*3431Scarlsonj 
1096*3431Scarlsonj 	(void) memset(&dsmp->dsm_send_dest.v6, 0,
1097*3431Scarlsonj 	    sizeof (dsmp->dsm_send_dest.v6));
1098*3431Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_addr	= dest;
1099*3431Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_family	= AF_INET6;
1100*3431Scarlsonj 	dsmp->dsm_send_dest.v6.sin6_port	= htons(IPPORT_DHCPV6S);
1101*3431Scarlsonj 	dsmp->dsm_send_stop_func		= stop;
1102*3431Scarlsonj 
1103*3431Scarlsonj 	/*
1104*3431Scarlsonj 	 * TODO: dispose of this gruesome assumption (there's no real
1105*3431Scarlsonj 	 * technical gain from doing so, but it would be cleaner)
1106*3431Scarlsonj 	 */
1107*3431Scarlsonj 
1108*3431Scarlsonj 	assert(dpkt == &dsmp->dsm_send_pkt);
1109*3431Scarlsonj 
1110*3431Scarlsonj 	/*
1111*3431Scarlsonj 	 * clear out any packets which had been previously received
1112*3431Scarlsonj 	 * but not pulled off of the recv_packet queue.
1113*3431Scarlsonj 	 */
1114*3431Scarlsonj 
1115*3431Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 	if (stop == NULL) {
1118*3431Scarlsonj 		dsmp->dsm_send_timeout = 0;	/* prevents retransmissions */
1119*3431Scarlsonj 	} else {
1120*3431Scarlsonj 		dsmp->dsm_send_timeout = irt;
1121*3431Scarlsonj 		dsmp->dsm_send_tcenter = mrt;
1122*3431Scarlsonj 		/*
1123*3431Scarlsonj 		 * This is quite ugly, but RFC 3315 section 17.1.2 requires
1124*3431Scarlsonj 		 * that the RAND value for the very first retransmission of a
1125*3431Scarlsonj 		 * Solicit message is strictly greater than zero.
1126*3431Scarlsonj 		 */
1127*3431Scarlsonj 		next_retransmission(dsmp, B_TRUE,
1128*3431Scarlsonj 		    pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT);
1129*3431Scarlsonj 	}
11300Sstevel@tonic-gate 
1131*3431Scarlsonj 	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
1138*3431Scarlsonj  *	    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 {
1146*3431Scarlsonj 	dhcp_smach_t	*dsmp = arg;
11470Sstevel@tonic-gate 
1148*3431Scarlsonj 	dsmp->dsm_retrans_timer = -1;
1149*3431Scarlsonj 
1150*3431Scarlsonj 	if (!verify_smach(dsmp))
11510Sstevel@tonic-gate 		return;
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	/*
1154*3431Scarlsonj 	 * Check the callback to see if we should keep sending retransmissions.
1155*3431Scarlsonj 	 * Compute the next retransmission time first, so that the callback can
1156*3431Scarlsonj 	 * cap the value if need be.  (Required for DHCPv6 Confirm messages.)
1157*3431Scarlsonj 	 *
1158*3431Scarlsonj 	 * Hold the state machine across the callback so that the called
1159*3431Scarlsonj 	 * function can remove the state machine from the system without
1160*3431Scarlsonj 	 * disturbing the string used subsequently for verbose logging.  The
1161*3431Scarlsonj 	 * Release function destroys the state machine when the retry count
1162*3431Scarlsonj 	 * expires.
11630Sstevel@tonic-gate 	 */
11640Sstevel@tonic-gate 
1165*3431Scarlsonj 	next_retransmission(dsmp, B_FALSE, B_FALSE);
1166*3431Scarlsonj 	hold_smach(dsmp);
1167*3431Scarlsonj 	if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) {
1168*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s",
1169*3431Scarlsonj 		    dsmp->dsm_name);
1170*3431Scarlsonj 	} else {
1171*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s",
1172*3431Scarlsonj 		    dsmp->dsm_name);
1173*3431Scarlsonj 		(void) send_pkt_internal(dsmp);
1174*3431Scarlsonj 	}
1175*3431Scarlsonj 	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  *
1181*3431Scarlsonj  *   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
1186*3431Scarlsonj stop_pkt_retransmission(dhcp_smach_t *dsmp)
11870Sstevel@tonic-gate {
1188*3431Scarlsonj 	if (dsmp->dsm_retrans_timer != -1 &&
1189*3431Scarlsonj 	    iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) {
1190*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s",
1191*3431Scarlsonj 		    dsmp->dsm_name);
1192*3431Scarlsonj 		dsmp->dsm_retrans_timer = -1;
1193*3431Scarlsonj 		release_smach(dsmp);
11940Sstevel@tonic-gate 	}
11950Sstevel@tonic-gate }
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate /*
1198*3431Scarlsonj  * retransmit_now(): force a packet retransmission right now.  Used only with
1199*3431Scarlsonj  *		     the DHCPv6 UseMulticast status code.  Use with caution;
1200*3431Scarlsonj  *		     triggered retransmissions can cause packet storms.
12010Sstevel@tonic-gate  *
1202*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine to force retransmission on
1203*3431Scarlsonj  *  output: void
12040Sstevel@tonic-gate  */
12050Sstevel@tonic-gate 
1206*3431Scarlsonj void
1207*3431Scarlsonj retransmit_now(dhcp_smach_t *dsmp)
1208*3431Scarlsonj {
1209*3431Scarlsonj 	stop_pkt_retransmission(dsmp);
1210*3431Scarlsonj 	(void) send_pkt_internal(dsmp);
1211*3431Scarlsonj }
1212*3431Scarlsonj 
1213*3431Scarlsonj /*
1214*3431Scarlsonj  * alloc_pkt_entry(): Allocates a packet list entry with a given data area
1215*3431Scarlsonj  *		      size.
1216*3431Scarlsonj  *
1217*3431Scarlsonj  *   input: size_t: size of data area for packet
1218*3431Scarlsonj  *	    boolean_t: B_TRUE for IPv6
1219*3431Scarlsonj  *  output: PKT_LIST *: allocated packet list entry
1220*3431Scarlsonj  */
1221*3431Scarlsonj 
1222*3431Scarlsonj PKT_LIST *
1223*3431Scarlsonj alloc_pkt_entry(size_t psize, boolean_t isv6)
12240Sstevel@tonic-gate {
12250Sstevel@tonic-gate 	PKT_LIST	*plp;
12260Sstevel@tonic-gate 
1227*3431Scarlsonj 	if ((plp = calloc(1, sizeof (*plp))) == NULL ||
1228*3431Scarlsonj 	    (plp->pkt = malloc(psize)) == NULL) {
1229*3431Scarlsonj 		free(plp);
1230*3431Scarlsonj 		plp = NULL;
1231*3431Scarlsonj 	} else {
1232*3431Scarlsonj 		plp->len = psize;
1233*3431Scarlsonj 		plp->isv6 = isv6;
12340Sstevel@tonic-gate 	}
12350Sstevel@tonic-gate 
1236*3431Scarlsonj 	return (plp);
1237*3431Scarlsonj }
12380Sstevel@tonic-gate 
1239*3431Scarlsonj /*
1240*3431Scarlsonj  * sock_recvpkt(): read from the given socket into an allocated buffer and
1241*3431Scarlsonj  *		   handles any ancillary data options.
1242*3431Scarlsonj  *
1243*3431Scarlsonj  *   input: int: file descriptor to read
1244*3431Scarlsonj  *	    PKT_LIST *: allocated buffer
1245*3431Scarlsonj  *  output: ssize_t: number of bytes read, or -1 on error
1246*3431Scarlsonj  */
1247*3431Scarlsonj 
1248*3431Scarlsonj static ssize_t
1249*3431Scarlsonj sock_recvpkt(int fd, PKT_LIST *plp)
1250*3431Scarlsonj {
1251*3431Scarlsonj 	struct iovec iov;
1252*3431Scarlsonj 	struct msghdr msg;
1253*3431Scarlsonj 	long ctrl[8192 / sizeof (long)];
1254*3431Scarlsonj 	ssize_t msglen;
12550Sstevel@tonic-gate 
1256*3431Scarlsonj 	(void) memset(&iov, 0, sizeof (iov));
1257*3431Scarlsonj 	iov.iov_base = (caddr_t)plp->pkt;
1258*3431Scarlsonj 	iov.iov_len = plp->len;
1259*3431Scarlsonj 
1260*3431Scarlsonj 	(void) memset(&msg, 0, sizeof (msg));
1261*3431Scarlsonj 	msg.msg_name = &plp->pktfrom;
1262*3431Scarlsonj 	msg.msg_namelen = sizeof (plp->pktfrom);
1263*3431Scarlsonj 	msg.msg_iov = &iov;
1264*3431Scarlsonj 	msg.msg_iovlen = 1;
1265*3431Scarlsonj 	msg.msg_control = ctrl;
1266*3431Scarlsonj 	msg.msg_controllen = sizeof (ctrl);
1267*3431Scarlsonj 
1268*3431Scarlsonj 	if ((msglen = recvmsg(fd, &msg, 0)) != -1) {
1269*3431Scarlsonj 		struct cmsghdr *cmsg;
1270*3431Scarlsonj 
1271*3431Scarlsonj 		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
1272*3431Scarlsonj 		    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1273*3431Scarlsonj 			struct sockaddr_in *sinp;
1274*3431Scarlsonj 			struct sockaddr_in6 *sin6;
1275*3431Scarlsonj 			struct in6_pktinfo *ipi6;
12760Sstevel@tonic-gate 
1277*3431Scarlsonj 			switch (cmsg->cmsg_level) {
1278*3431Scarlsonj 			case IPPROTO_IP:
1279*3431Scarlsonj 				switch (cmsg->cmsg_type) {
1280*3431Scarlsonj 				case IP_RECVDSTADDR:
1281*3431Scarlsonj 					sinp = (struct sockaddr_in *)
1282*3431Scarlsonj 					    &plp->pktto;
1283*3431Scarlsonj 					sinp->sin_family = AF_INET;
1284*3431Scarlsonj 					(void) memcpy(&sinp->sin_addr.s_addr,
1285*3431Scarlsonj 					    CMSG_DATA(cmsg),
1286*3431Scarlsonj 					    sizeof (ipaddr_t));
1287*3431Scarlsonj 					break;
1288*3431Scarlsonj 
1289*3431Scarlsonj 				case IP_RECVIF:
1290*3431Scarlsonj 					(void) memcpy(&plp->ifindex,
1291*3431Scarlsonj 					    CMSG_DATA(cmsg), sizeof (uint_t));
1292*3431Scarlsonj 					break;
1293*3431Scarlsonj 				}
1294*3431Scarlsonj 				break;
12950Sstevel@tonic-gate 
1296*3431Scarlsonj 			case IPPROTO_IPV6:
1297*3431Scarlsonj 				switch (cmsg->cmsg_type) {
1298*3431Scarlsonj 				case IPV6_PKTINFO:
1299*3431Scarlsonj 					/* LINTED: alignment */
1300*3431Scarlsonj 					ipi6 = (struct in6_pktinfo *)
1301*3431Scarlsonj 					    CMSG_DATA(cmsg);
1302*3431Scarlsonj 					sin6 = (struct sockaddr_in6 *)
1303*3431Scarlsonj 					    &plp->pktto;
1304*3431Scarlsonj 					sin6->sin6_family = AF_INET6;
1305*3431Scarlsonj 					(void) memcpy(&sin6->sin6_addr,
1306*3431Scarlsonj 					    &ipi6->ipi6_addr,
1307*3431Scarlsonj 					    sizeof (ipi6->ipi6_addr));
1308*3431Scarlsonj 					(void) memcpy(&plp->ifindex,
1309*3431Scarlsonj 					    &ipi6->ipi6_ifindex,
1310*3431Scarlsonj 					    sizeof (uint_t));
1311*3431Scarlsonj 					break;
1312*3431Scarlsonj 				}
1313*3431Scarlsonj 			}
1314*3431Scarlsonj 		}
1315*3431Scarlsonj 	}
1316*3431Scarlsonj 	return (msglen);
1317*3431Scarlsonj }
13180Sstevel@tonic-gate 
1319*3431Scarlsonj /*
1320*3431Scarlsonj  * recv_pkt(): receives a single DHCP packet on a given file descriptor.
1321*3431Scarlsonj  *
1322*3431Scarlsonj  *   input: int: the file descriptor to receive the packet
1323*3431Scarlsonj  *	    int: the maximum packet size to allow
1324*3431Scarlsonj  *	    boolean_t: B_TRUE for IPv6
1325*3431Scarlsonj  *	    boolean_t: B_TRUE if using DLPI
1326*3431Scarlsonj  *  output: PKT_LIST *: the received packet
1327*3431Scarlsonj  */
13280Sstevel@tonic-gate 
1329*3431Scarlsonj PKT_LIST *
1330*3431Scarlsonj recv_pkt(int fd, int mtu, boolean_t isv6, boolean_t isdlpi)
1331*3431Scarlsonj {
1332*3431Scarlsonj 	PKT_LIST	*plp;
1333*3431Scarlsonj 	ssize_t		retval;
1334*3431Scarlsonj 
1335*3431Scarlsonj 	if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) {
1336*3431Scarlsonj 		dhcpmsg(MSG_ERROR,
1337*3431Scarlsonj 		    "recv_pkt: allocation failure; dropped packet");
1338*3431Scarlsonj 		return (NULL);
13390Sstevel@tonic-gate 	}
13400Sstevel@tonic-gate 
1341*3431Scarlsonj 	if (isv6) {
1342*3431Scarlsonj 		retval = sock_recvpkt(fd, plp);
1343*3431Scarlsonj 
1344*3431Scarlsonj 		if (retval == -1) {
1345*3431Scarlsonj 			dhcpmsg(MSG_ERR,
1346*3431Scarlsonj 			    "recv_pkt: recvfrom v6 failed, dropped");
1347*3431Scarlsonj 			goto failure;
1348*3431Scarlsonj 		}
1349*3431Scarlsonj 
1350*3431Scarlsonj 		plp->len = retval;
13510Sstevel@tonic-gate 
1352*3431Scarlsonj 		if (retval < sizeof (dhcpv6_message_t)) {
1353*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "recv_pkt: runt message");
1354*3431Scarlsonj 			goto failure;
1355*3431Scarlsonj 		}
1356*3431Scarlsonj 	} else {
1357*3431Scarlsonj 		if (isdlpi) {
1358*3431Scarlsonj 			retval = dlpi_recvfrom(fd, plp->pkt, mtu,
1359*3431Scarlsonj 			    (struct sockaddr_in *)&plp->pktfrom,
1360*3431Scarlsonj 			    (struct sockaddr_in *)&plp->pktto);
1361*3431Scarlsonj 		} else {
1362*3431Scarlsonj 			retval = sock_recvpkt(fd, plp);
1363*3431Scarlsonj 		}
13640Sstevel@tonic-gate 
1365*3431Scarlsonj 		if (retval == -1) {
1366*3431Scarlsonj 			dhcpmsg(MSG_ERR,
1367*3431Scarlsonj 			    "recv_pkt: %srecvfrom v4 failed, dropped",
1368*3431Scarlsonj 			    isdlpi ? "dlpi_" : "");
1369*3431Scarlsonj 			goto failure;
1370*3431Scarlsonj 		}
1371*3431Scarlsonj 
1372*3431Scarlsonj 		plp->len = retval;
1373*3431Scarlsonj 
1374*3431Scarlsonj 		switch (dhcp_options_scan(plp, B_TRUE)) {
1375*3431Scarlsonj 
1376*3431Scarlsonj 		case DHCP_WRONG_MSG_TYPE:
1377*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
1378*3431Scarlsonj 			    "recv_pkt: unexpected DHCP message");
1379*3431Scarlsonj 			goto failure;
13800Sstevel@tonic-gate 
1381*3431Scarlsonj 		case DHCP_GARBLED_MSG_TYPE:
1382*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
1383*3431Scarlsonj 			    "recv_pkt: garbled DHCP message type");
1384*3431Scarlsonj 			goto failure;
13850Sstevel@tonic-gate 
1386*3431Scarlsonj 		case DHCP_BAD_OPT_OVLD:
1387*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload");
1388*3431Scarlsonj 			goto failure;
1389*3431Scarlsonj 
1390*3431Scarlsonj 		case 0:
1391*3431Scarlsonj 			break;
1392*3431Scarlsonj 
1393*3431Scarlsonj 		default:
1394*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
1395*3431Scarlsonj 			    "recv_pkt: packet corrupted, dropped");
13960Sstevel@tonic-gate 			goto failure;
13970Sstevel@tonic-gate 		}
13980Sstevel@tonic-gate 	}
1399*3431Scarlsonj 	return (plp);
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate failure:
1402*3431Scarlsonj 	free_pkt_entry(plp);
1403*3431Scarlsonj 	return (NULL);
1404*3431Scarlsonj }
1405*3431Scarlsonj 
1406*3431Scarlsonj /*
1407*3431Scarlsonj  * pkt_v4_match(): check if a given DHCPv4 message type is in a given set
1408*3431Scarlsonj  *
1409*3431Scarlsonj  *   input: uchar_t: packet type
1410*3431Scarlsonj  *	    dhcp_message_type_t: bit-wise OR of DHCP_P* values.
1411*3431Scarlsonj  *  output: boolean_t: B_TRUE if packet type is in the set
1412*3431Scarlsonj  */
1413*3431Scarlsonj 
1414*3431Scarlsonj boolean_t
1415*3431Scarlsonj pkt_v4_match(uchar_t type, dhcp_message_type_t match_type)
1416*3431Scarlsonj {
1417*3431Scarlsonj 	/*
1418*3431Scarlsonj 	 * note: the ordering here allows direct indexing of the table
1419*3431Scarlsonj 	 *	 based on the RFC2131 packet type value passed in.
1420*3431Scarlsonj 	 */
1421*3431Scarlsonj 
1422*3431Scarlsonj 	static dhcp_message_type_t type_map[] = {
1423*3431Scarlsonj 		DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST,
1424*3431Scarlsonj 		DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE,
1425*3431Scarlsonj 		DHCP_PINFORM
1426*3431Scarlsonj 	};
1427*3431Scarlsonj 
1428*3431Scarlsonj 	if (type < (sizeof (type_map) / sizeof (*type_map)))
1429*3431Scarlsonj 		return ((type_map[type] & match_type) ? B_TRUE : B_FALSE);
1430*3431Scarlsonj 	else
1431*3431Scarlsonj 		return (B_FALSE);
1432*3431Scarlsonj }
1433*3431Scarlsonj 
1434*3431Scarlsonj /*
1435*3431Scarlsonj  * pkt_smach_enqueue(): enqueue a packet on a given state machine
1436*3431Scarlsonj  *
1437*3431Scarlsonj  *   input: dhcp_smach_t: state machine
1438*3431Scarlsonj  *	    PKT_LIST *: packet to enqueue
1439*3431Scarlsonj  *  output: none
1440*3431Scarlsonj  */
1441*3431Scarlsonj 
1442*3431Scarlsonj void
1443*3431Scarlsonj pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp)
1444*3431Scarlsonj {
1445*3431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s",
1446*3431Scarlsonj 	    pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6),
1447*3431Scarlsonj 	    dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name);
1448*3431Scarlsonj 
1449*3431Scarlsonj 	/* add to front of list */
1450*3431Scarlsonj 	insque(plp, &dsmp->dsm_recv_pkt_list);
1451*3431Scarlsonj 	dsmp->dsm_received++;
14520Sstevel@tonic-gate }
14530Sstevel@tonic-gate 
14540Sstevel@tonic-gate /*
1455*3431Scarlsonj  * next_retransmission(): computes the number of seconds until the next
1456*3431Scarlsonj  *			  retransmission, based on the algorithms in RFCs 2131
1457*3431Scarlsonj  *			  3315.
14580Sstevel@tonic-gate  *
1459*3431Scarlsonj  *   input: dhcp_smach_t *: state machine that needs a new timer
1460*3431Scarlsonj  *	    boolean_t: B_TRUE if this is the first time sending the message
1461*3431Scarlsonj  *	    boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2)
1462*3431Scarlsonj  *  output: none
14630Sstevel@tonic-gate  */
14640Sstevel@tonic-gate 
1465*3431Scarlsonj static void
1466*3431Scarlsonj next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send,
1467*3431Scarlsonj     boolean_t positive_only)
14680Sstevel@tonic-gate {
1469*3431Scarlsonj 	uint32_t timeout_ms;
1470*3431Scarlsonj 
1471*3431Scarlsonj 	if (dsmp->dsm_isv6) {
1472*3431Scarlsonj 		double randval;
1473*3431Scarlsonj 
1474*3431Scarlsonj 		/*
1475*3431Scarlsonj 		 * The RFC specifies 0 to 10% jitter for the initial
1476*3431Scarlsonj 		 * solicitation, and plus or minus 10% jitter for all others.
1477*3431Scarlsonj 		 * This works out to 100 milliseconds on the shortest timer we
1478*3431Scarlsonj 		 * use.
1479*3431Scarlsonj 		 */
1480*3431Scarlsonj 		if (positive_only)
1481*3431Scarlsonj 			randval = drand48() / 10.0;
1482*3431Scarlsonj 		else
1483*3431Scarlsonj 			randval = (drand48() - 0.5) / 5.0;
1484*3431Scarlsonj 
1485*3431Scarlsonj 		/* The RFC specifies doubling *after* the first transmission */
1486*3431Scarlsonj 		timeout_ms = dsmp->dsm_send_timeout;
1487*3431Scarlsonj 		if (!first_send)
1488*3431Scarlsonj 			timeout_ms *= 2;
1489*3431Scarlsonj 		timeout_ms += (int)(randval * dsmp->dsm_send_timeout);
1490*3431Scarlsonj 
1491*3431Scarlsonj 		/* This checks the MRT (maximum retransmission time) */
1492*3431Scarlsonj 		if (dsmp->dsm_send_tcenter != 0 &&
1493*3431Scarlsonj 		    timeout_ms > dsmp->dsm_send_tcenter) {
1494*3431Scarlsonj 			timeout_ms = dsmp->dsm_send_tcenter +
1495*3431Scarlsonj 			    (uint_t)(randval * dsmp->dsm_send_tcenter);
1496*3431Scarlsonj 		}
1497*3431Scarlsonj 
1498*3431Scarlsonj 		dsmp->dsm_send_timeout = timeout_ms;
1499*3431Scarlsonj 	} else {
1500*3431Scarlsonj 		if (dsmp->dsm_state == RENEWING ||
1501*3431Scarlsonj 		    dsmp->dsm_state == REBINDING) {
1502*3431Scarlsonj 			monosec_t mono;
1503*3431Scarlsonj 
1504*3431Scarlsonj 			timeout_ms = dsmp->dsm_state == RENEWING ?
1505*3431Scarlsonj 			    dsmp->dsm_leases->dl_t2.dt_start :
1506*3431Scarlsonj 			    dsmp->dsm_leases->dl_lifs->lif_expire.dt_start;
1507*3431Scarlsonj 			timeout_ms += dsmp->dsm_curstart_monosec;
1508*3431Scarlsonj 			mono = monosec();
1509*3431Scarlsonj 			if (mono > timeout_ms)
1510*3431Scarlsonj 				timeout_ms = 0;
1511*3431Scarlsonj 			else
1512*3431Scarlsonj 				timeout_ms -= mono;
1513*3431Scarlsonj 			timeout_ms *= MILLISEC / 2;
1514*3431Scarlsonj 		} else {
1515*3431Scarlsonj 			/*
1516*3431Scarlsonj 			 * Start at 4, and increase by a factor of 2 up to 64.
1517*3431Scarlsonj 			 */
1518*3431Scarlsonj 			if (first_send) {
1519*3431Scarlsonj 				timeout_ms = 4 * MILLISEC;
1520*3431Scarlsonj 			} else {
1521*3431Scarlsonj 				timeout_ms = MIN(dsmp->dsm_send_tcenter << 1,
1522*3431Scarlsonj 				    64 * MILLISEC);
1523*3431Scarlsonj 			}
1524*3431Scarlsonj 		}
1525*3431Scarlsonj 
1526*3431Scarlsonj 		dsmp->dsm_send_tcenter = timeout_ms;
1527*3431Scarlsonj 
1528*3431Scarlsonj 		/*
1529*3431Scarlsonj 		 * At each iteration, jitter the timeout by some fraction of a
1530*3431Scarlsonj 		 * second.
1531*3431Scarlsonj 		 */
1532*3431Scarlsonj 		dsmp->dsm_send_timeout = timeout_ms +
1533*3431Scarlsonj 		    ((lrand48() % (2 * MILLISEC)) - MILLISEC);
1534*3431Scarlsonj 	}
1535*3431Scarlsonj }
15360Sstevel@tonic-gate 
1537*3431Scarlsonj /*
1538*3431Scarlsonj  * dhcp_ip_default(): open and bind the default IP sockets used for I/O and
1539*3431Scarlsonj  *		      interface control.
1540*3431Scarlsonj  *
1541*3431Scarlsonj  *   input: none
1542*3431Scarlsonj  *  output: B_TRUE on success
1543*3431Scarlsonj  */
1544*3431Scarlsonj 
1545*3431Scarlsonj boolean_t
1546*3431Scarlsonj dhcp_ip_default(void)
1547*3431Scarlsonj {
1548*3431Scarlsonj 	int on;
1549*3431Scarlsonj 
1550*3431Scarlsonj 	if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1551*3431Scarlsonj 		dhcpmsg(MSG_ERR,
1552*3431Scarlsonj 		    "dhcp_ip_default: unable to create IPv4 socket");
1553*3431Scarlsonj 		return (B_FALSE);
1554*3431Scarlsonj 	}
1555*3431Scarlsonj 
1556*3431Scarlsonj 	if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
1557*3431Scarlsonj 	    sizeof (on)) == -1) {
1558*3431Scarlsonj 		dhcpmsg(MSG_ERR,
1559*3431Scarlsonj 		    "dhcp_ip_default: unable to enable IP_RECVDSTADDR");
1560*3431Scarlsonj 		return (B_FALSE);
1561*3431Scarlsonj 	}
1562*3431Scarlsonj 
1563*3431Scarlsonj 	if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) ==
1564*3431Scarlsonj 	    -1) {
1565*3431Scarlsonj 		dhcpmsg(MSG_ERR,
1566*3431Scarlsonj 		    "dhcp_ip_default: unable to enable IP_RECVIF");
1567*3431Scarlsonj 		return (B_FALSE);
1568*3431Scarlsonj 	}
15690Sstevel@tonic-gate 
1570*3431Scarlsonj 	if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) {
1571*3431Scarlsonj 		dhcpmsg(MSG_ERROR,
1572*3431Scarlsonj 		    "dhcp_ip_default: unable to bind IPv4 socket to port %d",
1573*3431Scarlsonj 		    IPPORT_BOOTPC);
1574*3431Scarlsonj 		return (B_FALSE);
1575*3431Scarlsonj 	}
1576*3431Scarlsonj 
1577*3431Scarlsonj 	if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_common,
1578*3431Scarlsonj 	    NULL) == -1) {
1579*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1580*3431Scarlsonj 		    "receive IPv4 broadcasts");
1581*3431Scarlsonj 		return (B_FALSE);
1582*3431Scarlsonj 	}
1583*3431Scarlsonj 
1584*3431Scarlsonj 	if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
1585*3431Scarlsonj 		dhcpmsg(MSG_ERR,
1586*3431Scarlsonj 		    "dhcp_ip_default: unable to create IPv6 socket");
1587*3431Scarlsonj 		return (B_FALSE);
1588*3431Scarlsonj 	}
1589*3431Scarlsonj 
1590*3431Scarlsonj 	if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
1591*3431Scarlsonj 	    sizeof (on)) == -1) {
1592*3431Scarlsonj 		dhcpmsg(MSG_ERR,
1593*3431Scarlsonj 		    "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO");
1594*3431Scarlsonj 		return (B_FALSE);
1595*3431Scarlsonj 	}
1596*3431Scarlsonj 
1597*3431Scarlsonj 	if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) {
1598*3431Scarlsonj 		dhcpmsg(MSG_ERROR,
1599*3431Scarlsonj 		    "dhcp_ip_default: unable to bind IPv6 socket to port %d",
1600*3431Scarlsonj 		    IPPORT_DHCPV6C);
1601*3431Scarlsonj 		return (B_FALSE);
1602*3431Scarlsonj 	}
1603*3431Scarlsonj 
1604*3431Scarlsonj 	if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_common,
1605*3431Scarlsonj 	    NULL) == -1) {
1606*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1607*3431Scarlsonj 		    "receive IPv6 packets");
1608*3431Scarlsonj 		return (B_FALSE);
1609*3431Scarlsonj 	}
1610*3431Scarlsonj 
1611*3431Scarlsonj 	return (B_TRUE);
16120Sstevel@tonic-gate }
1613