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