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