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