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