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