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