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