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