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