xref: /illumos-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c (revision e8d3efa1c56e5f2b5368600a2baeb7b1d54a07f8)
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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
24  * Copyright 2019 Joshua M. Clulow <josh@sysmgr.org>
25  * Copyright 2026 Oxide Computer Company
26  *
27  * BOUND state of the DHCP client state machine.
28  */
29 
30 #include <sys/socket.h>
31 #include <sys/types.h>
32 #include <sys/debug.h>
33 #include <string.h>
34 #include <netinet/in.h>
35 #include <sys/sockio.h>
36 #include <unistd.h>
37 #include <time.h>
38 #include <arpa/inet.h>
39 #include <stdlib.h>
40 #include <search.h>
41 #include <sys/sysmacros.h>
42 #include <dhcp_hostconf.h>
43 #include <dhcpagent_util.h>
44 #include <dhcpmsg.h>
45 
46 #include "states.h"
47 #include "packet.h"
48 #include "util.h"
49 #include "agent.h"
50 #include "interface.h"
51 #include "script_handler.h"
52 #include "defaults.h"
53 
54 /*
55  * Possible outcomes for IPv6 binding attempt.
56  */
57 enum v6_bind_result {
58 	v6Restart,		/* report failure and restart state machine */
59 	v6Resent,		/* new Request message has been sent */
60 	v6Done			/* successful binding */
61 };
62 
63 static enum v6_bind_result configure_v6_leases(dhcp_smach_t *);
64 static boolean_t configure_v4_lease(dhcp_smach_t *);
65 static boolean_t configure_v4_timers(dhcp_smach_t *);
66 
67 /*
68  * bound_event_cb(): callback for script_start on the event EVENT_BOUND
69  *
70  *   input: dhcp_smach_t *: the state machine configured
71  *	    void *: unused
72  *  output: int: always 1
73  */
74 
75 /* ARGSUSED1 */
76 static int
77 bound_event_cb(dhcp_smach_t *dsmp, void *arg)
78 {
79 	if (dsmp->dsm_ia.ia_fd != -1)
80 		ipc_action_finish(dsmp, DHCP_IPC_SUCCESS);
81 	else
82 		async_finish(dsmp);
83 	return (1);
84 }
85 
86 /*
87  * dhcp_bound(): configures an state machine and interfaces using information
88  *		 contained in the ACK/Reply packet and sets up lease timers.
89  *		 Before starting, the requested address is verified by
90  *		 Duplicate Address Detection to make sure it's not in use.
91  *
92  *   input: dhcp_smach_t *: the state machine to move to bound
93  *	    PKT_LIST *: the ACK/Reply packet, or NULL to use dsmp->dsm_ack
94  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
95  */
96 
97 boolean_t
98 dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack)
99 {
100 	DHCPSTATE	oldstate;
101 	lease_t		new_lease;
102 	dhcp_lif_t	*lif;
103 	dhcp_lease_t	*dlp;
104 	enum v6_bind_result v6b;
105 
106 	if (ack != NULL) {
107 		/* If ack we're replacing is not the original, then free it */
108 		if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
109 			free_pkt_entry(dsmp->dsm_ack);
110 		dsmp->dsm_ack = ack;
111 		/* Save the first ack as the original */
112 		if (dsmp->dsm_orig_ack == NULL)
113 			dsmp->dsm_orig_ack = ack;
114 
115 		save_domainname(dsmp, ack);
116 	}
117 
118 	oldstate = dsmp->dsm_state;
119 	switch (oldstate) {
120 
121 	case ADOPTING:
122 		/* Note that adoption occurs only for IPv4 DHCP. */
123 
124 		/* Ignore BOOTP */
125 		if (ack->opts[CD_DHCP_TYPE] == NULL)
126 			return (B_FALSE);
127 
128 		/*
129 		 * if we're adopting a lease, the lease timers
130 		 * only provide an upper bound since we don't know
131 		 * from what time they are relative to.  assume we
132 		 * have a lease time of at most DHCP_ADOPT_LEASE_MAX.
133 		 */
134 		(void) memcpy(&new_lease, ack->opts[CD_LEASE_TIME]->value,
135 		    sizeof (lease_t));
136 
137 		new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX));
138 
139 		(void) memcpy(ack->opts[CD_LEASE_TIME]->value, &new_lease,
140 		    sizeof (lease_t));
141 
142 		/*
143 		 * we have no idea when the REQUEST that generated
144 		 * this ACK was sent, but for diagnostic purposes
145 		 * we'll assume its close to the current time.
146 		 */
147 		dsmp->dsm_newstart_monosec = monosec();
148 
149 		if (dsmp->dsm_isv6) {
150 			if ((v6b = configure_v6_leases(dsmp)) != v6Done)
151 				return (v6b == v6Resent);
152 		} else {
153 			if (!configure_v4_lease(dsmp))
154 				return (B_FALSE);
155 
156 			if (!configure_v4_timers(dsmp))
157 				return (B_FALSE);
158 		}
159 
160 		dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
161 		write_lease_to_hostconf(dsmp);
162 		break;
163 
164 	case SELECTING:
165 	case REQUESTING:
166 	case INIT_REBOOT:
167 
168 		if (dsmp->dsm_isv6) {
169 			if ((v6b = configure_v6_leases(dsmp)) != v6Done)
170 				return (v6b == v6Resent);
171 		} else {
172 			if (!configure_v4_lease(dsmp))
173 				return (B_FALSE);
174 
175 			if (!configure_v4_timers(dsmp))
176 				return (B_FALSE);
177 
178 			if (!clear_lif_deprecated(dsmp->dsm_lif))
179 				return (B_FALSE);
180 		}
181 
182 		/* Stop sending requests now */
183 		stop_pkt_retransmission(dsmp);
184 
185 		/*
186 		 * If we didn't end up with any usable leases, then we have a
187 		 * problem.
188 		 */
189 		if (dsmp->dsm_leases == NULL) {
190 			dhcpmsg(MSG_WARNING,
191 			    "dhcp_bound: no address lease established");
192 			return (B_FALSE);
193 		}
194 
195 		/*
196 		 * If this is a Rapid-Commit (selecting state) or if we're
197 		 * dealing with a reboot (init-reboot), then we will have a new
198 		 * server ID to save.
199 		 */
200 		if (ack != NULL &&
201 		    (oldstate == SELECTING || oldstate == INIT_REBOOT) &&
202 		    dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
203 			dhcpmsg(MSG_ERROR,
204 			    "dhcp_bound: unable to save server ID on %s",
205 			    dsmp->dsm_name);
206 			return (B_FALSE);
207 		}
208 
209 		/*
210 		 * We will continue configuring the interfaces via
211 		 * dhcp_bound_complete, once kernel DAD completes.  If no new
212 		 * leases were created (which can happen on an init-reboot used
213 		 * for link-up confirmation), then go straight to bound state.
214 		 */
215 		if (!set_smach_state(dsmp, PRE_BOUND))
216 			return (B_FALSE);
217 		if (dsmp->dsm_lif_wait == 0)
218 			dhcp_bound_complete(dsmp);
219 		break;
220 
221 	case PRE_BOUND:
222 	case BOUND:
223 	case INFORMATION:
224 		/* This is just a duplicate ack; silently ignore it */
225 		return (B_TRUE);
226 
227 	case RENEWING:
228 	case REBINDING:
229 
230 		if (dsmp->dsm_isv6) {
231 			if ((v6b = configure_v6_leases(dsmp)) != v6Done)
232 				return (v6b == v6Resent);
233 		} else {
234 			if (!configure_v4_timers(dsmp))
235 				return (B_FALSE);
236 			if (!clear_lif_deprecated(dsmp->dsm_lif))
237 				return (B_FALSE);
238 		}
239 
240 		/*
241 		 * If some or all of the leases were torn down by the server,
242 		 * then handle that as an expiry.  When the script is done
243 		 * running for the LOSS6 event, we'll end up back here.
244 		 */
245 		if ((lif = find_expired_lif(dsmp)) != NULL) {
246 			hold_lif(lif);
247 			dhcp_expire(NULL, lif);
248 			while ((lif = find_expired_lif(dsmp)) != NULL) {
249 				dlp = lif->lif_lease;
250 				unplumb_lif(lif);
251 				if (dlp->dl_nlifs == 0)
252 					remove_lease(dlp);
253 			}
254 			if (dsmp->dsm_leases == NULL)
255 				return (B_FALSE);
256 		}
257 
258 		if (oldstate == REBINDING && dsmp->dsm_isv6 &&
259 		    !save_server_id(dsmp, ack)) {
260 			return (B_FALSE);
261 		}
262 
263 		/*
264 		 * Handle Renew/Rebind that fails to address one of our leases.
265 		 * (Should just never happen, but RFC 3315 section 18.1.8
266 		 * requires it, and TAHI tests for it.)
267 		 */
268 		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
269 			if (dlp->dl_stale && dlp->dl_nlifs > 0)
270 				break;
271 		}
272 		if (dlp != NULL) {
273 			dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; "
274 			    "allow retransmit");
275 			return (B_TRUE);
276 		}
277 
278 		if (!set_smach_state(dsmp, BOUND))
279 			return (B_FALSE);
280 
281 		(void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 :
282 		    EVENT_EXTEND, bound_event_cb, NULL, NULL);
283 
284 		dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
285 		write_lease_to_hostconf(dsmp);
286 
287 		/* Stop sending requests now */
288 		stop_pkt_retransmission(dsmp);
289 		break;
290 
291 	case INFORM_SENT:
292 
293 		if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
294 			return (B_FALSE);
295 		}
296 
297 		(void) bound_event_cb(dsmp, NULL);
298 		if (!set_smach_state(dsmp, INFORMATION))
299 			return (B_FALSE);
300 
301 		/* Stop sending requests now */
302 		stop_pkt_retransmission(dsmp);
303 		break;
304 
305 	default:
306 		/* something is really bizarre... */
307 		dhcpmsg(MSG_DEBUG,
308 		    "dhcp_bound: called in unexpected state: %s",
309 		    dhcp_state_to_string(dsmp->dsm_state));
310 		return (B_FALSE);
311 	}
312 
313 	return (B_TRUE);
314 }
315 
316 static int
317 dhcp_route_add(dhcp_smach_t *dsmp, struct in_addr *network, uint8_t prefix,
318     struct in_addr *nexthop, boolean_t interface, dhcp_route_source_t source)
319 {
320 	dhcp_route_t *dhr, *found;
321 	avl_index_t where;
322 
323 	if ((dhr = calloc(1, sizeof (*dhr))) == NULL) {
324 		return (-1);
325 	}
326 
327 	VERIFY3U(prefix, <=, 32);
328 	if (prefix != 0) {
329 		dhr->dhr_network = *network;
330 		dhr->dhr_prefix = prefix;
331 	}
332 	dhr->dhr_nexthop = *nexthop;
333 	dhr->dhr_installed = B_FALSE;
334 	dhr->dhr_interface = interface;
335 	dhr->dhr_source = source;
336 
337 	if ((found = avl_find(&dsmp->dsm_routes, dhr, &where)) != NULL) {
338 		/*
339 		 * The route is already in the list.
340 		 */
341 		found->dhr_source |= source;
342 		free(dhr);
343 		return (0);
344 	}
345 
346 	avl_insert(&dsmp->dsm_routes, dhr, where);
347 	return (0);
348 }
349 
350 /*
351  * Parse the "Classless Route" option from RFC3442.  The option is formatted as
352  * a sequence of pairs of destination prefix and next hop router address.
353  */
354 static int
355 dhcp_classless_routes_add(dhcp_smach_t *dsmp, DHCP_OPT *routes, ipaddr_t ifip)
356 {
357 	uint_t pos = 0;
358 	uint_t nsigocts = 0;
359 	uint_t c = 0;
360 	uint8_t network[4];
361 	uint8_t nexthop[4];
362 	uint_t pfx = 0;
363 	enum {
364 		RPST_REST = 1,
365 		RPST_NETWORK,
366 		RPST_NEXTHOP
367 	} state = RPST_REST;
368 
369 	VERIFY3U(routes->code, ==, CD_CLASSLESS_ROUTES);
370 
371 	while (pos < routes->len) {
372 		uint8_t b = routes->value[pos];
373 
374 		switch (state) {
375 		case RPST_REST:
376 			/*
377 			 * The first octet of a destination descriptor is the
378 			 * number of prefix bits in the subnet mask.  This also
379 			 * determines the number of octets of that will follow
380 			 * to completely specify the destination prefix.
381 			 */
382 			if (b == 0) {
383 				nsigocts = 0;
384 			} else if (b >= 1 && b <= 8) {
385 				nsigocts = 1;
386 			} else if (b >= 9 && b <= 16) {
387 				nsigocts = 2;
388 			} else if (b >= 17 && b <= 24) {
389 				nsigocts = 3;
390 			} else if (b >= 25 && b <= 32) {
391 				nsigocts = 4;
392 			} else {
393 				errno = EPROTO;
394 				return (-1);
395 			}
396 			pfx = b;
397 			(void) memset(network, 0, sizeof (network));
398 			(void) memset(nexthop, 0, sizeof (nexthop));
399 			c = 0;
400 			pos++;
401 			state = RPST_NETWORK;
402 			break;
403 
404 		case RPST_NETWORK:
405 			if (c < nsigocts) {
406 				network[c++] = b;
407 				pos++;
408 				break;
409 			}
410 			c = 0;
411 			state = RPST_NEXTHOP;
412 			break;
413 
414 		case RPST_NEXTHOP:
415 			/*
416 			 * The next hop router address appears directly after
417 			 * the variable length destination descriptor.
418 			 */
419 			nexthop[c++] = b;
420 			pos++;
421 			if (c >= 4) {
422 				boolean_t iface;
423 				struct in_addr ia_net, ia_hop;
424 				(void) memcpy(&ia_net.s_addr, network,
425 				    sizeof (ipaddr_t));
426 				if (nexthop[0] == 0 && nexthop[1] == 0 &&
427 				    nexthop[2] == 0 && nexthop[3] == 0) {
428 					/*
429 					 * A next hop address of all zeroes in
430 					 * the option value means this is an
431 					 * interface route and we need to use
432 					 * the address of the interface.
433 					 */
434 					iface = B_TRUE;
435 					(void) memcpy(&ia_hop.s_addr, &ifip,
436 					    sizeof (ipaddr_t));
437 				} else {
438 					iface = B_FALSE;
439 					(void) memcpy(&ia_hop.s_addr, nexthop,
440 					    sizeof (ipaddr_t));
441 				}
442 
443 				(void) dhcp_route_add(dsmp, &ia_net, pfx,
444 				    &ia_hop, iface, DHR_SRC_CLASSLESS_ROUTES);
445 
446 				state = RPST_REST;
447 			}
448 			break;
449 		}
450 	}
451 
452 	if (state != RPST_REST) {
453 		errno = EPROTO;
454 		return (-1);
455 	}
456 
457 	return (0);
458 }
459 
460 /*
461  * dhcp_bound_complete(): complete interface configuration after DAD
462  *
463  *   input: dhcp_smach_t *: the state machine now ready
464  *  output: none
465  */
466 
467 void
468 dhcp_bound_complete(dhcp_smach_t *dsmp)
469 {
470 	PKT_LIST *ack = dsmp->dsm_ack;
471 	DHCP_OPT *routes;
472 	DHCP_OPT *router_list;
473 	DHCPSTATE oldstate;
474 	dhcp_lif_t *lif = dsmp->dsm_lif;
475 	boolean_t ignore_mtu = B_FALSE;
476 	boolean_t ignore_routes = B_FALSE, ignore_router_list = B_FALSE;
477 	boolean_t manage_mtu, manage_classless_routes;
478 
479 	/*
480 	 * Do bound state entry processing only if running IPv4.  There's no
481 	 * need for this with DHCPv6 because link-locals are used for I/O and
482 	 * because DHCPv6 isn't entangled with routing.
483 	 */
484 	if (dsmp->dsm_isv6) {
485 		(void) set_smach_state(dsmp, BOUND);
486 		dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s",
487 		    dsmp->dsm_name);
488 		(void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL,
489 		    NULL);
490 		dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
491 		write_lease_to_hostconf(dsmp);
492 		return;
493 	}
494 
495 	manage_mtu = df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_SET_MTU);
496 	manage_classless_routes = df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6,
497 	    DF_CLASSLESS_ROUTES);
498 
499 	/*
500 	 * Check to see if the default route, classless routes, or MTU options
501 	 * appear in the ignore list.
502 	 */
503 	for (int i = 0; i < dsmp->dsm_pillen; i++) {
504 		switch (dsmp->dsm_pil[i]) {
505 		case CD_MTU:
506 			ignore_mtu = B_TRUE;
507 			break;
508 		case CD_ROUTER:
509 			ignore_router_list = B_TRUE;
510 			break;
511 		case CD_CLASSLESS_ROUTES:
512 			ignore_routes = B_TRUE;
513 			break;
514 		}
515 	}
516 
517 	/*
518 	 * If the server provides a valid MTU option, and the operator has not
519 	 * disabled MTU management, configure the MTU on the interface now.
520 	 */
521 	if (manage_mtu) {
522 		DHCP_OPT *mtu;
523 		uint16_t mtuval = 0;
524 
525 		if (!ignore_mtu && (mtu = ack->opts[CD_MTU]) != NULL &&
526 		    mtu->len == sizeof (uint16_t)) {
527 			(void) memcpy(&mtuval, mtu->value, sizeof (mtuval));
528 			mtuval = ntohs(mtuval);
529 
530 			set_lif_mtu(lif, mtuval);
531 		} else {
532 			/*
533 			 * If no MTU value is provided, clear any value that
534 			 * might have been requested previously.
535 			 */
536 			clear_lif_mtu(lif);
537 		}
538 	}
539 
540 	VERIFY3U(avl_numnodes(&dsmp->dsm_routes), ==, 0);
541 
542 	/*
543 	 * Process the RFC3442 classless routes option, if present and enabled:
544 	 */
545 	if (!ignore_routes &&
546 	    (routes = ack->opts[CD_CLASSLESS_ROUTES]) != NULL &&
547 	    dhcp_classless_routes_add(dsmp, routes, lif->lif_addr) != 0) {
548 		dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot read "
549 		    "classless route list, ignoring classless routes");
550 
551 		/*
552 		 * We add routes incrementally while parsing the response.  To
553 		 * avoid using a partially valid response from the server,
554 		 * remove any classless routes we may have just added:
555 		 */
556 		discard_routes(dsmp, DHR_SRC_CLASSLESS_ROUTES);
557 	}
558 
559 	/*
560 	 * Process the traditional default gateway option, if present and
561 	 * enabled:
562 	 */
563 	if (!ignore_router_list &&
564 	    (router_list = ack->opts[CD_ROUTER]) != NULL &&
565 	    (router_list->len % sizeof (ipaddr_t)) == 0) {
566 		for (int i = 0; i < router_list->len / sizeof (ipaddr_t); i++) {
567 			struct in_addr gateway;
568 			(void) memcpy(&gateway,
569 			    router_list->value + (i * sizeof (ipaddr_t)),
570 			    sizeof (ipaddr_t));
571 			if (dhcp_route_add(dsmp, NULL, 0, &gateway,
572 			    B_FALSE, DHR_SRC_DEFAULT_GATEWAY) != 0) {
573 				dhcpmsg(MSG_ERR, "dhcp_bound_complete: "
574 				    "adding default router to list failed");
575 			}
576 		}
577 	}
578 
579 	/*
580 	 * RFC3442 requires us to ignore the router option if a valid classless
581 	 * routes option value is available and we are able to process it.  In
582 	 * theory the server should only give us one or the other, but we
583 	 * cannot assume that is the case.
584 	 */
585 	dhcp_route_source_t use_source = DHR_SRC_DEFAULT_GATEWAY;
586 	for (dhcp_route_t *dhr = avl_first(&dsmp->dsm_routes);
587 	    dhr != NULL; dhr = AVL_NEXT(&dsmp->dsm_routes, dhr)) {
588 		if (!(dhr->dhr_source & DHR_SRC_CLASSLESS_ROUTES)) {
589 			continue;
590 		}
591 
592 		use_source = DHR_SRC_CLASSLESS_ROUTES;
593 		dhcpmsg(MSG_INFO, "classless routes are available; "
594 		    "ignoring router option on %s",
595 		    dsmp->dsm_name);
596 		break;
597 	}
598 
599 	/*
600 	 * If management of classless routes is disabled through configuration
601 	 * but the option was requested and not explicitly ignored, we will not
602 	 * add any routes from any source.  This allows the operator to collect
603 	 * the option and perform some site-specific handling.
604 	 */
605 	if (!manage_classless_routes &&
606 	    use_source == DHR_SRC_CLASSLESS_ROUTES) {
607 		goto skip_routes;
608 	}
609 
610 	/*
611 	 * Add each provided router; we'll clean them up when the state machine
612 	 * goes away or when our lease expires.
613 	 *
614 	 * Note that we do not handle routes on IPv4 logicals; see README for
615 	 * details.
616 	 */
617 	if (strchr(lif->lif_name, ':') == NULL &&
618 	    !lif->lif_pif->pif_under_ipmp) {
619 		for (dhcp_route_t *dhr = avl_first(&dsmp->dsm_routes);
620 		    dhr != NULL; dhr = AVL_NEXT(&dsmp->dsm_routes, dhr)) {
621 			char rt[INET_ADDRSTRLEN];
622 			const char *verb = "added";
623 
624 			if ((dhr->dhr_source & use_source) != use_source) {
625 				/*
626 				 * This route is not from the source we have
627 				 * selected.
628 				 */
629 				continue;
630 			}
631 
632 			(void) strncpy(rt, inet_ntoa(dhr->dhr_network),
633 			    sizeof (rt));
634 
635 			if (!add_route(lif->lif_pif->pif_index, dhr)) {
636 				if (errno == EEXIST) {
637 					/*
638 					 * This route already exists in the
639 					 * kernel.  Adopt this route as if we
640 					 * were able to add it, just in case
641 					 * the DHCP agent crashed and was
642 					 * restarted.
643 					 */
644 					verb = "found";
645 				} else {
646 					dhcpmsg(MSG_ERR,
647 					    "dhcp_bound_complete: cannot "
648 					    "add route %s/%u -> %s (%s) on %s",
649 					    rt, dhr->dhr_prefix,
650 					    inet_ntoa(dhr->dhr_nexthop),
651 					    dhr->dhr_interface ? "interface" :
652 					    "gateway", dsmp->dsm_name);
653 					continue;
654 				}
655 			}
656 
657 			dhr->dhr_installed = B_TRUE;
658 			dhcpmsg(MSG_INFO, "%s route %s/%u -> %s (%s) on %s",
659 			    verb, rt, dhr->dhr_prefix,
660 			    inet_ntoa(dhr->dhr_nexthop),
661 			    dhr->dhr_interface ? "interface" : "gateway",
662 			    dsmp->dsm_name);
663 		}
664 	}
665 
666 skip_routes:
667 	oldstate = dsmp->dsm_state;
668 	if (!set_smach_state(dsmp, BOUND)) {
669 		dhcpmsg(MSG_ERR,
670 		    "dhcp_bound_complete: cannot set bound state on %s",
671 		    dsmp->dsm_name);
672 		return;
673 	}
674 
675 	dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name);
676 
677 	/*
678 	 * We're now committed to this binding, so if it came from BOOTP, set
679 	 * the flag.
680 	 */
681 
682 	if (ack->opts[CD_DHCP_TYPE] == NULL)
683 		dsmp->dsm_dflags |= DHCP_IF_BOOTP;
684 
685 	/*
686 	 * If the previous state was ADOPTING, event loop has not been started
687 	 * at this time; so don't run the EVENT_BOUND script.
688 	 */
689 	if (oldstate != ADOPTING) {
690 		(void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL,
691 		    NULL);
692 	}
693 
694 	dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
695 	write_lease_to_hostconf(dsmp);
696 }
697 
698 /*
699  * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131.
700  *	      We use up to plus or minus 2% jitter in the time.  This is a
701  *	      small value, but the timers involved are typically long.  A
702  *	      common T1 value is one day, and the fuzz is up to 28.8 minutes;
703  *	      plenty of time to make sure that individual clients don't renew
704  *	      all at the same time.
705  *
706  *   input: uint32_t: the number of seconds until lease expiration
707  *	    double: the approximate percentage of that time to return
708  *  output: double: a number approximating (sec * pct)
709  */
710 
711 static double
712 fuzzify(uint32_t sec, double pct)
713 {
714 	return (sec * (pct + (drand48() - 0.5) / 25.0));
715 }
716 
717 /*
718  * get_pkt_times(): pulls the lease times out of a v4 DHCP packet and stores
719  *		    them as host byte-order relative times in the passed in
720  *		    parameters.
721  *
722  *   input: PKT_LIST *: the packet to pull the packet times from
723  *	    lease_t *: where to store the relative lease time in hbo
724  *	    lease_t *: where to store the relative t1 time in hbo
725  *	    lease_t *: where to store the relative t2 time in hbo
726  *  output: void
727  */
728 
729 static void
730 get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2)
731 {
732 	*lease	= DHCP_PERM;
733 	*t1	= DHCP_PERM;
734 	*t2	= DHCP_PERM;
735 
736 	if (ack->opts[CD_DHCP_TYPE] == NULL) {
737 		dhcpmsg(MSG_VERBOSE,
738 		    "get_pkt_times: BOOTP response; infinite lease");
739 		return;
740 	}
741 	if (ack->opts[CD_LEASE_TIME] == NULL) {
742 		dhcpmsg(MSG_VERBOSE,
743 		    "get_pkt_times: no lease option provided");
744 		return;
745 	}
746 	if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
747 		dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option");
748 	}
749 
750 	(void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
751 	*lease = ntohl(*lease);
752 
753 	if (*lease == DHCP_PERM) {
754 		dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted");
755 		return;
756 	}
757 
758 	if (ack->opts[CD_T1_TIME] != NULL &&
759 	    ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) {
760 		(void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1));
761 		*t1 = ntohl(*t1);
762 	}
763 
764 	if (ack->opts[CD_T2_TIME] != NULL &&
765 	    ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) {
766 		(void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2));
767 		*t2 = ntohl(*t2);
768 	}
769 
770 	if ((*t1 == DHCP_PERM) || (*t1 >= *lease))
771 		*t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT);
772 
773 	if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1))
774 		*t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT);
775 
776 	dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u",
777 	    *lease, *t1, *t2);
778 }
779 
780 /*
781  * configure_v4_timers(): configures the lease timers on a v4 state machine
782  *
783  *   input: dhcp_smach_t *: the state machine to configure
784  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
785  */
786 
787 static boolean_t
788 configure_v4_timers(dhcp_smach_t *dsmp)
789 {
790 	PKT_LIST	*ack = dsmp->dsm_ack;
791 	lease_t		lease, t1, t2;
792 	dhcp_lease_t	*dlp;
793 	dhcp_lif_t	*lif;
794 
795 	/* v4 has just one lease per state machine, and one LIF */
796 	dlp = dsmp->dsm_leases;
797 	lif = dlp->dl_lifs;
798 
799 	/*
800 	 * If it's DHCP, but there's no valid lease time, then complain,
801 	 * decline the lease and return error.
802 	 */
803 	if (ack->opts[CD_DHCP_TYPE] != NULL &&
804 	    (ack->opts[CD_LEASE_TIME] == NULL ||
805 	    ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
806 		lif_mark_decline(lif, "Missing or corrupted lease time");
807 		send_declines(dsmp);
808 		dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in "
809 		    "ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" :
810 		    "corrupt", dsmp->dsm_name);
811 		return (B_FALSE);
812 	}
813 
814 	/* Stop the T1 and T2 timers */
815 	cancel_lease_timers(dlp);
816 
817 	/* Stop the LEASE timer */
818 	cancel_lif_timers(lif);
819 
820 	/*
821 	 * type has already been verified as ACK.  if type is not set,
822 	 * then we got a BOOTP packet.  we now fetch the t1, t2, and
823 	 * lease options out of the packet into variables.  they are
824 	 * returned as relative host-byte-ordered times.
825 	 */
826 
827 	get_pkt_times(ack, &lease, &t1, &t2);
828 
829 	/*
830 	 * if the current lease is mysteriously close to the new
831 	 * lease, warn the user.  unless there's less than a minute
832 	 * left, round to the closest minute.
833 	 */
834 
835 	if (lif->lif_expire.dt_start != 0 &&
836 	    abs((dsmp->dsm_newstart_monosec + lease) -
837 	    (dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) <
838 	    DHCP_LEASE_EPS) {
839 		const char *noext = "configure_v4_timers: lease renewed but "
840 		    "time not extended";
841 		int msg_level;
842 		uint_t minleft;
843 
844 		if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH)
845 			msg_level = MSG_ERROR;
846 		else
847 			msg_level = MSG_VERBOSE;
848 
849 		minleft = (lif->lif_expire.dt_start + 30) / 60;
850 
851 		if (lif->lif_expire.dt_start < 60) {
852 			dhcpmsg(msg_level, "%s; expires in %d seconds",
853 			    noext, lif->lif_expire.dt_start);
854 		} else if (minleft == 1) {
855 			dhcpmsg(msg_level, "%s; expires in 1 minute", noext);
856 		} else if (minleft > 120) {
857 			dhcpmsg(msg_level, "%s; expires in %d hours",
858 			    noext, (minleft + 30) / 60);
859 		} else {
860 			dhcpmsg(msg_level, "%s; expires in %d minutes",
861 			    noext, minleft);
862 		}
863 	}
864 
865 	init_timer(&dlp->dl_t1, t1);
866 	init_timer(&dlp->dl_t2, t2);
867 	init_timer(&lif->lif_expire, lease);
868 
869 	if (lease == DHCP_PERM) {
870 		dhcpmsg(MSG_INFO,
871 		    "configure_v4_timers: %s acquired permanent lease",
872 		    dsmp->dsm_name);
873 		return (B_TRUE);
874 	}
875 
876 	dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s",
877 	    dsmp->dsm_name,
878 	    monosec_to_string(dsmp->dsm_newstart_monosec + lease));
879 
880 	dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s",
881 	    dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
882 	    dlp->dl_t1.dt_start));
883 
884 	dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s",
885 	    dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
886 	    dlp->dl_t2.dt_start));
887 
888 	/*
889 	 * according to RFC2131, there is no minimum lease time, but don't
890 	 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN.
891 	 */
892 
893 	if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
894 		goto failure;
895 
896 	if (lease < DHCP_REBIND_MIN) {
897 		dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for "
898 		    "less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN);
899 		return (B_TRUE);
900 	}
901 
902 	if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew))
903 		goto failure;
904 
905 	if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))
906 		goto failure;
907 
908 	return (B_TRUE);
909 
910 failure:
911 	cancel_lease_timers(dlp);
912 	cancel_lif_timers(lif);
913 	dhcpmsg(MSG_WARNING,
914 	    "configure_v4_timers: cannot schedule lease timers");
915 	return (B_FALSE);
916 }
917 
918 /*
919  * configure_v6_leases(): configures the IPv6 leases on a state machine from
920  *			  the current DHCPv6 ACK.  We need to scan the ACK,
921  *			  create a lease for each IA_NA, and a new LIF for each
922  *			  IAADDR.
923  *
924  *   input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack)
925  *  output: enum v6_bind_result: restart, resend, or done
926  */
927 
928 static enum v6_bind_result
929 configure_v6_leases(dhcp_smach_t *dsmp)
930 {
931 	const dhcpv6_option_t *d6o, *d6so, *d6sso;
932 	const char *optbase, *estr, *msg;
933 	uint_t olen, solen, ssolen, msglen;
934 	dhcpv6_ia_na_t d6in;
935 	dhcpv6_iaaddr_t d6ia;
936 	dhcp_lease_t *dlp;
937 	uint32_t shortest;
938 	dhcp_lif_t *lif;
939 	uint_t nlifs;
940 	boolean_t got_iana = B_FALSE;
941 	uint_t scode;
942 
943 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next)
944 		dlp->dl_stale = B_TRUE;
945 
946 	d6o = NULL;
947 	while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA,
948 	    &olen)) != NULL) {
949 		if (olen < sizeof (d6in)) {
950 			dhcpmsg(MSG_WARNING,
951 			    "configure_v6_leases: garbled IA_NA");
952 			continue;
953 		}
954 
955 		/*
956 		 * Check the IAID.  It should be for our controlling LIF.  If a
957 		 * single state machine needs to use multiple IAIDs, then this
958 		 * will need to change.
959 		 */
960 		(void) memcpy(&d6in, d6o, sizeof (d6in));
961 		d6in.d6in_iaid = ntohl(d6in.d6in_iaid);
962 		if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) {
963 			dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
964 			    "IA_NA for IAID %x (not %x)", d6in.d6in_iaid,
965 			    dsmp->dsm_lif->lif_iaid);
966 			continue;
967 		}
968 
969 		/*
970 		 * See notes below; there's only one IA_NA and a single IAID
971 		 * for now.
972 		 */
973 		if ((dlp = dsmp->dsm_leases) != NULL)
974 			dlp->dl_stale = B_FALSE;
975 
976 		/*
977 		 * Note that some bug-ridden servers will try to give us
978 		 * multiple IA_NA options for a single IAID.  We ignore
979 		 * duplicates.
980 		 */
981 		if (got_iana) {
982 			dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected "
983 			    "extra IA_NA ignored");
984 			continue;
985 		}
986 
987 		d6in.d6in_t1 = ntohl(d6in.d6in_t1);
988 		d6in.d6in_t2 = ntohl(d6in.d6in_t2);
989 
990 		/* RFC 3315 required check for invalid T1/T2 combinations */
991 		if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) {
992 			dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
993 			    "IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1,
994 			    d6in.d6in_t2);
995 			continue;
996 		}
997 
998 		/*
999 		 * There may be a status code here.  Process if present.
1000 		 */
1001 		optbase = (const char *)d6o + sizeof (d6in);
1002 		olen -= sizeof (d6in);
1003 		d6so = dhcpv6_find_option(optbase, olen, NULL,
1004 		    DHCPV6_OPT_STATUS_CODE, &solen);
1005 		scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen);
1006 		if (scode != DHCPV6_STAT_SUCCESS) {
1007 			dhcpmsg(MSG_WARNING,
1008 			    "configure_v6_leases: IA_NA: %s: %.*s",
1009 			    estr, msglen, msg);
1010 		}
1011 		print_server_msg(dsmp, msg, msglen);
1012 
1013 		/*
1014 		 * Other errors are possible here.  According to RFC 3315
1015 		 * section 18.1.8, we ignore the entire IA if it gives the "no
1016 		 * addresses" status code.  We may try another server if we
1017 		 * like -- we instead opt to allow the addresses to expire and
1018 		 * then try a new server.
1019 		 *
1020 		 * If the status code is "no binding," then we must go back and
1021 		 * redo the Request.  Surprisingly, it doesn't matter if it's
1022 		 * any other code.
1023 		 */
1024 		if (scode == DHCPV6_STAT_NOADDRS) {
1025 			dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring "
1026 			    "no-addrs status in IA_NA");
1027 			continue;
1028 		}
1029 
1030 		if (scode == DHCPV6_STAT_NOBINDING) {
1031 			send_v6_request(dsmp);
1032 			return (v6Resent);
1033 		}
1034 
1035 		/*
1036 		 * Find or create the lease structure.  This part is simple,
1037 		 * because we support only IA_NA and a single IAID.  This means
1038 		 * there's only one lease structure.  The design supports
1039 		 * multiple lease structures so that IA_TA and IA_PD can be
1040 		 * added later.
1041 		 */
1042 		if ((dlp = dsmp->dsm_leases) == NULL &&
1043 		    (dlp = insert_lease(dsmp)) == NULL) {
1044 			dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to "
1045 			    "allocate memory for lease");
1046 			return (v6Restart);
1047 		}
1048 
1049 		/*
1050 		 * Iterate over the IAADDR options contained within this IA_NA.
1051 		 */
1052 		shortest = DHCPV6_INFTIME;
1053 		d6so = NULL;
1054 		while ((d6so = dhcpv6_find_option(optbase, olen, d6so,
1055 		    DHCPV6_OPT_IAADDR, &solen)) != NULL) {
1056 			if (solen < sizeof (d6ia)) {
1057 				dhcpmsg(MSG_WARNING,
1058 				    "configure_v6_leases: garbled IAADDR");
1059 				continue;
1060 			}
1061 			(void) memcpy(&d6ia, d6so, sizeof (d6ia));
1062 
1063 			d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife);
1064 			d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife);
1065 
1066 			/* RFC 3315 required validity check */
1067 			if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) {
1068 				dhcpmsg(MSG_WARNING,
1069 				    "configure_v6_leases: ignored IAADDR with "
1070 				    "preferred lifetime %u > valid %u",
1071 				    d6ia.d6ia_preflife, d6ia.d6ia_vallife);
1072 				continue;
1073 			}
1074 
1075 			/*
1076 			 * RFC 3315 allows a status code to be buried inside
1077 			 * the IAADDR option.  Look for it, and process if
1078 			 * present.  Process in a manner similar to that for
1079 			 * the IA itself; TAHI checks for this.  Real servers
1080 			 * likely won't do this.
1081 			 */
1082 			d6sso = dhcpv6_find_option((const char *)d6so +
1083 			    sizeof (d6ia), solen - sizeof (d6ia), NULL,
1084 			    DHCPV6_OPT_STATUS_CODE, &ssolen);
1085 			scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg,
1086 			    &msglen);
1087 			print_server_msg(dsmp, msg, msglen);
1088 			if (scode == DHCPV6_STAT_NOADDRS) {
1089 				dhcpmsg(MSG_DEBUG, "configure_v6_leases: "
1090 				    "ignoring no-addrs status in IAADDR");
1091 				continue;
1092 			}
1093 			if (scode == DHCPV6_STAT_NOBINDING) {
1094 				send_v6_request(dsmp);
1095 				return (v6Resent);
1096 			}
1097 			if (scode != DHCPV6_STAT_SUCCESS) {
1098 				dhcpmsg(MSG_WARNING,
1099 				    "configure_v6_leases: IAADDR: %s", estr);
1100 			}
1101 
1102 			/*
1103 			 * Locate the existing LIF within the lease associated
1104 			 * with this address, if any.
1105 			 */
1106 			lif = dlp->dl_lifs;
1107 			for (nlifs = dlp->dl_nlifs; nlifs > 0;
1108 			    nlifs--, lif = lif->lif_next) {
1109 				if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr,
1110 				    &lif->lif_v6addr))
1111 					break;
1112 			}
1113 
1114 			/*
1115 			 * If the server has set the lifetime to zero, then
1116 			 * delete the LIF.  Otherwise, set the new LIF expiry
1117 			 * time, adding the LIF if necessary.
1118 			 */
1119 			if (d6ia.d6ia_vallife == 0) {
1120 				/* If it was found, then it's expired */
1121 				if (nlifs != 0) {
1122 					dhcpmsg(MSG_DEBUG,
1123 					    "configure_v6_leases: lif %s has "
1124 					    "expired", lif->lif_name);
1125 					lif->lif_expired = B_TRUE;
1126 				}
1127 				continue;
1128 			}
1129 
1130 			/* If it wasn't found, then create it now. */
1131 			if (nlifs == 0) {
1132 				lif = plumb_lif(dsmp->dsm_lif->lif_pif,
1133 				    &d6ia.d6ia_addr);
1134 				if (lif == NULL)
1135 					continue;
1136 				if (++dlp->dl_nlifs == 1) {
1137 					dlp->dl_lifs = lif;
1138 				} else {
1139 					remque(lif);
1140 					insque(lif, dlp->dl_lifs);
1141 				}
1142 				lif->lif_lease = dlp;
1143 				lif->lif_dad_wait = _B_TRUE;
1144 				dsmp->dsm_lif_wait++;
1145 			} else {
1146 				/* If it was found, cancel timer */
1147 				cancel_lif_timers(lif);
1148 				if (d6ia.d6ia_preflife != 0 &&
1149 				    !clear_lif_deprecated(lif)) {
1150 					unplumb_lif(lif);
1151 					continue;
1152 				}
1153 			}
1154 
1155 			/* Set the new expiry timers */
1156 			init_timer(&lif->lif_preferred, d6ia.d6ia_preflife);
1157 			init_timer(&lif->lif_expire, d6ia.d6ia_vallife);
1158 
1159 			/*
1160 			 * If the preferred lifetime is over now, then the LIF
1161 			 * is deprecated.  If it's the same as the expiry time,
1162 			 * then we don't need a separate timer for it.
1163 			 */
1164 			if (d6ia.d6ia_preflife == 0) {
1165 				set_lif_deprecated(lif);
1166 			} else if (d6ia.d6ia_preflife != DHCPV6_INFTIME &&
1167 			    d6ia.d6ia_preflife != d6ia.d6ia_vallife &&
1168 			    !schedule_lif_timer(lif, &lif->lif_preferred,
1169 			    dhcp_deprecate)) {
1170 				unplumb_lif(lif);
1171 				continue;
1172 			}
1173 
1174 			if (d6ia.d6ia_vallife != DHCPV6_INFTIME &&
1175 			    !schedule_lif_timer(lif, &lif->lif_expire,
1176 			    dhcp_expire)) {
1177 				unplumb_lif(lif);
1178 				continue;
1179 			}
1180 
1181 			if (d6ia.d6ia_preflife < shortest)
1182 				shortest = d6ia.d6ia_preflife;
1183 		}
1184 
1185 		if (dlp->dl_nlifs == 0) {
1186 			dhcpmsg(MSG_WARNING,
1187 			    "configure_v6_leases: no IAADDRs found in IA_NA");
1188 			remove_lease(dlp);
1189 			continue;
1190 		}
1191 
1192 		if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) {
1193 			/* Default values from RFC 3315: 0.5 and 0.8 */
1194 			if ((d6in.d6in_t1 = shortest / 2) == 0)
1195 				d6in.d6in_t1 = 1;
1196 			d6in.d6in_t2 = shortest - shortest / 5;
1197 		}
1198 
1199 		cancel_lease_timers(dlp);
1200 		init_timer(&dlp->dl_t1, d6in.d6in_t1);
1201 		init_timer(&dlp->dl_t2, d6in.d6in_t2);
1202 
1203 		if ((d6in.d6in_t1 != DHCPV6_INFTIME &&
1204 		    !schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) ||
1205 		    (d6in.d6in_t2 != DHCPV6_INFTIME &&
1206 		    !schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) {
1207 			dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to "
1208 			    "set renew/rebind timers");
1209 		} else {
1210 			got_iana = B_TRUE;
1211 		}
1212 	}
1213 
1214 	if (!got_iana) {
1215 		dhcpmsg(MSG_WARNING,
1216 		    "configure_v6_leases: no usable IA_NA option found");
1217 	}
1218 
1219 	return (v6Done);
1220 }
1221 
1222 /*
1223  * configure_v4_lease(): configures the IPv4 lease on a state machine from
1224  *			 the current DHCP ACK.  There's only one lease and LIF
1225  *			 per state machine in IPv4.
1226  *
1227  *   input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack)
1228  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
1229  */
1230 
1231 static boolean_t
1232 configure_v4_lease(dhcp_smach_t *dsmp)
1233 {
1234 	struct lifreq		lifr;
1235 	struct sockaddr_in	*sin;
1236 	PKT_LIST		*ack = dsmp->dsm_ack;
1237 	dhcp_lease_t		*dlp;
1238 	dhcp_lif_t		*lif;
1239 	uint32_t		addrhbo;
1240 	struct in_addr		inaddr;
1241 
1242 	/*
1243 	 * if we're using DHCP, then we'll have a valid CD_SERVER_ID
1244 	 * (we checked in dhcp_acknak()); set it now so that
1245 	 * dsmp->dsm_server is valid in case we need to send_decline().
1246 	 * note that we use comparisons against opts[CD_DHCP_TYPE]
1247 	 * since we haven't set DHCP_IF_BOOTP yet (we don't do that
1248 	 * until we're sure we want the offered address.)
1249 	 */
1250 
1251 	if (ack->opts[CD_DHCP_TYPE] != NULL) {
1252 		(void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value,
1253 		    sizeof (inaddr));
1254 		IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server);
1255 	}
1256 
1257 	/*
1258 	 * There needs to be exactly one lease for IPv4, and that lease
1259 	 * controls the main LIF for the state machine.  If it doesn't exist
1260 	 * yet, then create it now.
1261 	 */
1262 	if ((dlp = dsmp->dsm_leases) == NULL &&
1263 	    (dlp = insert_lease(dsmp)) == NULL) {
1264 		dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate "
1265 		    "memory for lease");
1266 		return (B_FALSE);
1267 	}
1268 	if (dlp->dl_nlifs == 0) {
1269 		dlp->dl_lifs = dsmp->dsm_lif;
1270 		dlp->dl_nlifs = 1;
1271 
1272 		/* The lease holds a reference on the LIF */
1273 		hold_lif(dlp->dl_lifs);
1274 		dlp->dl_lifs->lif_lease = dlp;
1275 	}
1276 
1277 	lif = dlp->dl_lifs;
1278 
1279 	IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr);
1280 	addrhbo = ntohl(ack->pkt->yiaddr.s_addr);
1281 	if ((addrhbo & IN_CLASSA_NET) == 0 ||
1282 	    (addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
1283 	    IN_CLASSD(addrhbo)) {
1284 		dhcpmsg(MSG_ERROR,
1285 		    "configure_v4_lease: got invalid IP address %s for %s",
1286 		    inet_ntoa(ack->pkt->yiaddr), lif->lif_name);
1287 		return (B_FALSE);
1288 	}
1289 
1290 	(void) memset(&lifr, 0, sizeof (struct lifreq));
1291 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1292 
1293 	/*
1294 	 * bring the interface online.  note that there is no optimal
1295 	 * order here: it is considered bad taste (and in > solaris 7,
1296 	 * likely illegal) to bring an interface up before it has an
1297 	 * ip address.  however, due to an apparent bug in sun fddi
1298 	 * 5.0, fddi will not obtain a network routing entry unless
1299 	 * the interface is brought up before it has an ip address.
1300 	 * we take the lesser of the two evils; if fddi customers have
1301 	 * problems, they can get a newer fddi distribution which
1302 	 * fixes the problem.
1303 	 */
1304 
1305 	sin = (struct sockaddr_in *)&lifr.lifr_addr;
1306 	sin->sin_family = AF_INET;
1307 
1308 	(void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask));
1309 	if (ack->opts[CD_SUBNETMASK] != NULL &&
1310 	    ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) {
1311 
1312 		(void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value,
1313 		    sizeof (inaddr));
1314 
1315 	} else {
1316 
1317 		if (ack->opts[CD_SUBNETMASK] != NULL &&
1318 		    ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) {
1319 			dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
1320 			    "subnet mask length is %d instead of %d, ignoring",
1321 			    ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t));
1322 		} else {
1323 			dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
1324 			    "netmask specified for %s, making best guess",
1325 			    lif->lif_name);
1326 		}
1327 
1328 		/*
1329 		 * no legitimate IP subnet mask specified..  use best
1330 		 * guess.  recall that lif_addr is in network order, so
1331 		 * imagine it's 0x11223344: then when it is read into
1332 		 * a register on x86, it becomes 0x44332211, so we
1333 		 * must ntohl() it to convert it to 0x11223344 in
1334 		 * order to use the macros in <netinet/in.h>.
1335 		 */
1336 
1337 		if (IN_CLASSA(addrhbo))
1338 			inaddr.s_addr = htonl(IN_CLASSA_NET);
1339 		else if (IN_CLASSB(addrhbo))
1340 			inaddr.s_addr = htonl(IN_CLASSB_NET);
1341 		else if (IN_CLASSC(addrhbo))
1342 			inaddr.s_addr = htonl(IN_CLASSC_NET);
1343 		else {
1344 			/*
1345 			 * Cant be Class D as that is multicast
1346 			 * Must be Class E
1347 			 */
1348 			inaddr.s_addr =  htonl(IN_CLASSE_NET);
1349 		}
1350 	}
1351 	lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr;
1352 
1353 	sin->sin_addr = inaddr;
1354 	dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s",
1355 	    inet_ntoa(sin->sin_addr), lif->lif_name);
1356 
1357 	if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
1358 		dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask "
1359 		    "on %s", lif->lif_name);
1360 		return (B_FALSE);
1361 	}
1362 
1363 	IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr);
1364 	dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s",
1365 	    inet_ntoa(sin->sin_addr), lif->lif_name);
1366 
1367 	if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
1368 		dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address "
1369 		    "on %s", lif->lif_name);
1370 		return (B_FALSE);
1371 	}
1372 
1373 	if (!lif->lif_dad_wait) {
1374 		lif->lif_dad_wait = _B_TRUE;
1375 		dsmp->dsm_lif_wait++;
1376 	}
1377 
1378 	if (ack->opts[CD_BROADCASTADDR] != NULL &&
1379 	    ack->opts[CD_BROADCASTADDR]->len == sizeof (inaddr)) {
1380 
1381 		(void) memcpy(&inaddr, ack->opts[CD_BROADCASTADDR]->value,
1382 		    sizeof (inaddr));
1383 
1384 	} else {
1385 
1386 		if (ack->opts[CD_BROADCASTADDR] != NULL &&
1387 		    ack->opts[CD_BROADCASTADDR]->len != sizeof (inaddr)) {
1388 			dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
1389 			    "broadcast address length is %d instead of %d, "
1390 			    "ignoring", ack->opts[CD_BROADCASTADDR]->len,
1391 			    sizeof (inaddr));
1392 		} else {
1393 			dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
1394 			    "broadcast specified for %s, making best guess",
1395 			    lif->lif_name);
1396 		}
1397 
1398 		/*
1399 		 * no legitimate IP broadcast specified.  compute it
1400 		 * from the IP address and netmask.
1401 		 */
1402 
1403 		IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &inaddr);
1404 		inaddr.s_addr |= ~lif->lif_v6mask._S6_un._S6_u32[3];
1405 	}
1406 
1407 	/*
1408 	 * the kernel will set the broadcast address for us as part of
1409 	 * bringing the interface up.  since experience has shown that dhcp
1410 	 * servers sometimes provide a bogus broadcast address, we let the
1411 	 * kernel set it so that it's guaranteed to be correct.
1412 	 *
1413 	 * also, note any inconsistencies and save the broadcast address the
1414 	 * kernel set so that we can watch for changes to it.
1415 	 */
1416 
1417 	if (ioctl(v4_sock_fd, SIOCGLIFBRDADDR, &lifr) == -1) {
1418 		dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get broadcast "
1419 		    "address for %s", lif->lif_name);
1420 		return (B_FALSE);
1421 	}
1422 
1423 	if (inaddr.s_addr != sin->sin_addr.s_addr) {
1424 		dhcpmsg(MSG_WARNING, "configure_v4_lease: incorrect broadcast "
1425 		    "address %s specified for %s; ignoring", inet_ntoa(inaddr),
1426 		    lif->lif_name);
1427 	}
1428 
1429 	lif->lif_broadcast = sin->sin_addr.s_addr;
1430 	dhcpmsg(MSG_INFO,
1431 	    "configure_v4_lease: using broadcast address %s on %s",
1432 	    inet_ntoa(inaddr), lif->lif_name);
1433 	return (B_TRUE);
1434 }
1435 
1436 /*
1437  * save_server_id(): save off the new DHCPv6 Server ID
1438  *
1439  *   input: dhcp_smach_t *: the state machine to use
1440  *	    PKT_LIST *: the packet with the Reply message
1441  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
1442  */
1443 
1444 boolean_t
1445 save_server_id(dhcp_smach_t *dsmp, PKT_LIST *msg)
1446 {
1447 	const dhcpv6_option_t *d6o;
1448 	uint_t olen;
1449 
1450 	d6o = dhcpv6_pkt_option(msg, NULL, DHCPV6_OPT_SERVERID, &olen);
1451 	if (d6o == NULL)
1452 		return (B_FALSE);
1453 	olen -= sizeof (*d6o);
1454 	free(dsmp->dsm_serverid);
1455 	if ((dsmp->dsm_serverid = malloc(olen)) == NULL) {
1456 		return (B_FALSE);
1457 	} else {
1458 		dsmp->dsm_serveridlen = olen;
1459 		(void) memcpy(dsmp->dsm_serverid, d6o + 1, olen);
1460 		return (B_TRUE);
1461 	}
1462 }
1463