xref: /titanic_41/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5985c053eSmeem  * Common Development and Distribution License (the "License").
6985c053eSmeem  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*0a3e1f6cSVasumathi Sundaram  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  *
247c478bd9Sstevel@tonic-gate  * BOUND state of the DHCP client state machine.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/socket.h>
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <netinet/in.h>
317c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
327c478bd9Sstevel@tonic-gate #include <unistd.h>
337c478bd9Sstevel@tonic-gate #include <time.h>
347c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
357c478bd9Sstevel@tonic-gate #include <stdlib.h>
36d04ccbb3Scarlsonj #include <search.h>
377c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
387c478bd9Sstevel@tonic-gate #include <dhcp_hostconf.h>
39d04ccbb3Scarlsonj #include <dhcpagent_util.h>
407c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #include "states.h"
437c478bd9Sstevel@tonic-gate #include "packet.h"
447c478bd9Sstevel@tonic-gate #include "util.h"
457c478bd9Sstevel@tonic-gate #include "agent.h"
467c478bd9Sstevel@tonic-gate #include "interface.h"
477c478bd9Sstevel@tonic-gate #include "script_handler.h"
487c478bd9Sstevel@tonic-gate 
49d04ccbb3Scarlsonj /*
50d04ccbb3Scarlsonj  * Possible outcomes for IPv6 binding attempt.
51d04ccbb3Scarlsonj  */
52d04ccbb3Scarlsonj enum v6_bind_result {
53d04ccbb3Scarlsonj 	v6Restart,		/* report failure and restart state machine */
54d04ccbb3Scarlsonj 	v6Resent,		/* new Request message has been sent */
55d04ccbb3Scarlsonj 	v6Done			/* successful binding */
56d04ccbb3Scarlsonj };
577c478bd9Sstevel@tonic-gate 
58d04ccbb3Scarlsonj static enum v6_bind_result configure_v6_leases(dhcp_smach_t *);
59d04ccbb3Scarlsonj static boolean_t configure_v4_lease(dhcp_smach_t *);
60d04ccbb3Scarlsonj static boolean_t configure_v4_timers(dhcp_smach_t *);
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate /*
637c478bd9Sstevel@tonic-gate  * bound_event_cb(): callback for script_start on the event EVENT_BOUND
647c478bd9Sstevel@tonic-gate  *
65d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine configured
66d04ccbb3Scarlsonj  *	    void *: unused
677c478bd9Sstevel@tonic-gate  *  output: int: always 1
687c478bd9Sstevel@tonic-gate  */
697c478bd9Sstevel@tonic-gate 
70d04ccbb3Scarlsonj /* ARGSUSED1 */
717c478bd9Sstevel@tonic-gate static int
bound_event_cb(dhcp_smach_t * dsmp,void * arg)72d04ccbb3Scarlsonj bound_event_cb(dhcp_smach_t *dsmp, void *arg)
737c478bd9Sstevel@tonic-gate {
74d04ccbb3Scarlsonj 	if (dsmp->dsm_ia.ia_fd != -1)
75d04ccbb3Scarlsonj 		ipc_action_finish(dsmp, DHCP_IPC_SUCCESS);
76d04ccbb3Scarlsonj 	else
77d04ccbb3Scarlsonj 		async_finish(dsmp);
787c478bd9Sstevel@tonic-gate 	return (1);
797c478bd9Sstevel@tonic-gate }
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*
82d04ccbb3Scarlsonj  * dhcp_bound(): configures an state machine and interfaces using information
83d04ccbb3Scarlsonj  *		 contained in the ACK/Reply packet and sets up lease timers.
84d04ccbb3Scarlsonj  *		 Before starting, the requested address is verified by
85d04ccbb3Scarlsonj  *		 Duplicate Address Detection to make sure it's not in use.
867c478bd9Sstevel@tonic-gate  *
87d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to move to bound
88d04ccbb3Scarlsonj  *	    PKT_LIST *: the ACK/Reply packet, or NULL to use dsmp->dsm_ack
89d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
907c478bd9Sstevel@tonic-gate  */
917c478bd9Sstevel@tonic-gate 
92d04ccbb3Scarlsonj boolean_t
dhcp_bound(dhcp_smach_t * dsmp,PKT_LIST * ack)93d04ccbb3Scarlsonj dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack)
947c478bd9Sstevel@tonic-gate {
95d04ccbb3Scarlsonj 	DHCPSTATE	oldstate;
96d04ccbb3Scarlsonj 	lease_t		new_lease;
97d04ccbb3Scarlsonj 	dhcp_lif_t	*lif;
98d04ccbb3Scarlsonj 	dhcp_lease_t	*dlp;
99d04ccbb3Scarlsonj 	enum v6_bind_result v6b;
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	if (ack != NULL) {
1027c478bd9Sstevel@tonic-gate 		/* If ack we're replacing is not the original, then free it */
103d04ccbb3Scarlsonj 		if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
104d04ccbb3Scarlsonj 			free_pkt_entry(dsmp->dsm_ack);
105d04ccbb3Scarlsonj 		dsmp->dsm_ack = ack;
1067c478bd9Sstevel@tonic-gate 		/* Save the first ack as the original */
107d04ccbb3Scarlsonj 		if (dsmp->dsm_orig_ack == NULL)
108d04ccbb3Scarlsonj 			dsmp->dsm_orig_ack = ack;
1097c478bd9Sstevel@tonic-gate 	}
1107c478bd9Sstevel@tonic-gate 
111d04ccbb3Scarlsonj 	oldstate = dsmp->dsm_state;
112d04ccbb3Scarlsonj 	switch (oldstate) {
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate 	case ADOPTING:
115d04ccbb3Scarlsonj 		/* Note that adoption occurs only for IPv4 DHCP. */
116d04ccbb3Scarlsonj 
117d04ccbb3Scarlsonj 		/* Ignore BOOTP */
118d04ccbb3Scarlsonj 		if (ack->opts[CD_DHCP_TYPE] == NULL)
119d04ccbb3Scarlsonj 			return (B_FALSE);
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 		/*
122d04ccbb3Scarlsonj 		 * if we're adopting a lease, the lease timers
1237c478bd9Sstevel@tonic-gate 		 * only provide an upper bound since we don't know
1247c478bd9Sstevel@tonic-gate 		 * from what time they are relative to.  assume we
1257c478bd9Sstevel@tonic-gate 		 * have a lease time of at most DHCP_ADOPT_LEASE_MAX.
1267c478bd9Sstevel@tonic-gate 		 */
127d04ccbb3Scarlsonj 		(void) memcpy(&new_lease, ack->opts[CD_LEASE_TIME]->value,
128d04ccbb3Scarlsonj 		    sizeof (lease_t));
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 		new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX));
1317c478bd9Sstevel@tonic-gate 
132d04ccbb3Scarlsonj 		(void) memcpy(ack->opts[CD_LEASE_TIME]->value, &new_lease,
133d04ccbb3Scarlsonj 		    sizeof (lease_t));
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 		/*
1367c478bd9Sstevel@tonic-gate 		 * we have no idea when the REQUEST that generated
1377c478bd9Sstevel@tonic-gate 		 * this ACK was sent, but for diagnostic purposes
1387c478bd9Sstevel@tonic-gate 		 * we'll assume its close to the current time.
1397c478bd9Sstevel@tonic-gate 		 */
140d04ccbb3Scarlsonj 		dsmp->dsm_newstart_monosec = monosec();
1417c478bd9Sstevel@tonic-gate 
142d04ccbb3Scarlsonj 		if (dsmp->dsm_isv6) {
143d04ccbb3Scarlsonj 			if ((v6b = configure_v6_leases(dsmp)) != v6Done)
144d04ccbb3Scarlsonj 				return (v6b == v6Resent);
145d04ccbb3Scarlsonj 		} else {
146d04ccbb3Scarlsonj 			if (!configure_v4_lease(dsmp))
147d04ccbb3Scarlsonj 				return (B_FALSE);
148dc041e83Scarlsonj 
149d04ccbb3Scarlsonj 			if (!configure_v4_timers(dsmp))
150d04ccbb3Scarlsonj 				return (B_FALSE);
151d04ccbb3Scarlsonj 		}
15269bb4bb4Scarlsonj 
153d04ccbb3Scarlsonj 		dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
154*0a3e1f6cSVasumathi Sundaram 		write_lease_to_hostconf(dsmp);
15569bb4bb4Scarlsonj 		break;
1567c478bd9Sstevel@tonic-gate 
157d04ccbb3Scarlsonj 	case SELECTING:
1587c478bd9Sstevel@tonic-gate 	case REQUESTING:
1597c478bd9Sstevel@tonic-gate 	case INIT_REBOOT:
1607c478bd9Sstevel@tonic-gate 
161d04ccbb3Scarlsonj 		if (dsmp->dsm_isv6) {
162d04ccbb3Scarlsonj 			if ((v6b = configure_v6_leases(dsmp)) != v6Done)
163d04ccbb3Scarlsonj 				return (v6b == v6Resent);
164d04ccbb3Scarlsonj 		} else {
165d04ccbb3Scarlsonj 			if (!configure_v4_lease(dsmp))
166d04ccbb3Scarlsonj 				return (B_FALSE);
1677c478bd9Sstevel@tonic-gate 
168d04ccbb3Scarlsonj 			if (!configure_v4_timers(dsmp))
169d04ccbb3Scarlsonj 				return (B_FALSE);
170d04ccbb3Scarlsonj 
171d04ccbb3Scarlsonj 			if (!clear_lif_deprecated(dsmp->dsm_lif))
172d04ccbb3Scarlsonj 				return (B_FALSE);
173d04ccbb3Scarlsonj 		}
174d04ccbb3Scarlsonj 
175d04ccbb3Scarlsonj 		/* Stop sending requests now */
176d04ccbb3Scarlsonj 		stop_pkt_retransmission(dsmp);
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 		/*
179d04ccbb3Scarlsonj 		 * If we didn't end up with any usable leases, then we have a
180d04ccbb3Scarlsonj 		 * problem.
1817c478bd9Sstevel@tonic-gate 		 */
182d04ccbb3Scarlsonj 		if (dsmp->dsm_leases == NULL) {
183d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
184d04ccbb3Scarlsonj 			    "dhcp_bound: no address lease established");
185d04ccbb3Scarlsonj 			return (B_FALSE);
186d04ccbb3Scarlsonj 		}
187d04ccbb3Scarlsonj 
188d04ccbb3Scarlsonj 		/*
189d04ccbb3Scarlsonj 		 * If this is a Rapid-Commit (selecting state) or if we're
190d04ccbb3Scarlsonj 		 * dealing with a reboot (init-reboot), then we will have a new
191d04ccbb3Scarlsonj 		 * server ID to save.
192d04ccbb3Scarlsonj 		 */
193d04ccbb3Scarlsonj 		if (ack != NULL &&
194d04ccbb3Scarlsonj 		    (oldstate == SELECTING || oldstate == INIT_REBOOT) &&
195d04ccbb3Scarlsonj 		    dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
196d04ccbb3Scarlsonj 			dhcpmsg(MSG_ERROR,
197d04ccbb3Scarlsonj 			    "dhcp_bound: unable to save server ID on %s",
198d04ccbb3Scarlsonj 			    dsmp->dsm_name);
199d04ccbb3Scarlsonj 			return (B_FALSE);
200d04ccbb3Scarlsonj 		}
201d04ccbb3Scarlsonj 
202d04ccbb3Scarlsonj 		/*
203d04ccbb3Scarlsonj 		 * We will continue configuring the interfaces via
204d04ccbb3Scarlsonj 		 * dhcp_bound_complete, once kernel DAD completes.  If no new
205d04ccbb3Scarlsonj 		 * leases were created (which can happen on an init-reboot used
206d04ccbb3Scarlsonj 		 * for link-up confirmation), then go straight to bound state.
207d04ccbb3Scarlsonj 		 */
208d04ccbb3Scarlsonj 		if (!set_smach_state(dsmp, PRE_BOUND))
209d04ccbb3Scarlsonj 			return (B_FALSE);
210d04ccbb3Scarlsonj 		if (dsmp->dsm_lif_wait == 0)
211d04ccbb3Scarlsonj 			dhcp_bound_complete(dsmp);
2127c478bd9Sstevel@tonic-gate 		break;
2137c478bd9Sstevel@tonic-gate 
21469bb4bb4Scarlsonj 	case PRE_BOUND:
215d04ccbb3Scarlsonj 	case BOUND:
216906cb642Scarlsonj 	case INFORMATION:
21769bb4bb4Scarlsonj 		/* This is just a duplicate ack; silently ignore it */
218d04ccbb3Scarlsonj 		return (B_TRUE);
21969bb4bb4Scarlsonj 
2207c478bd9Sstevel@tonic-gate 	case RENEWING:
2217c478bd9Sstevel@tonic-gate 	case REBINDING:
222d04ccbb3Scarlsonj 
223d04ccbb3Scarlsonj 		if (dsmp->dsm_isv6) {
224d04ccbb3Scarlsonj 			if ((v6b = configure_v6_leases(dsmp)) != v6Done)
225d04ccbb3Scarlsonj 				return (v6b == v6Resent);
226d04ccbb3Scarlsonj 		} else {
227d04ccbb3Scarlsonj 			if (!configure_v4_timers(dsmp))
228d04ccbb3Scarlsonj 				return (B_FALSE);
229d04ccbb3Scarlsonj 			if (!clear_lif_deprecated(dsmp->dsm_lif))
230d04ccbb3Scarlsonj 				return (B_FALSE);
231d04ccbb3Scarlsonj 		}
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 		/*
234d04ccbb3Scarlsonj 		 * If some or all of the leases were torn down by the server,
235d04ccbb3Scarlsonj 		 * then handle that as an expiry.  When the script is done
236d04ccbb3Scarlsonj 		 * running for the LOSS6 event, we'll end up back here.
2377c478bd9Sstevel@tonic-gate 		 */
238d04ccbb3Scarlsonj 		if ((lif = find_expired_lif(dsmp)) != NULL) {
239d04ccbb3Scarlsonj 			hold_lif(lif);
240d04ccbb3Scarlsonj 			dhcp_expire(NULL, lif);
241d04ccbb3Scarlsonj 			while ((lif = find_expired_lif(dsmp)) != NULL) {
242d04ccbb3Scarlsonj 				dlp = lif->lif_lease;
243d04ccbb3Scarlsonj 				unplumb_lif(lif);
244d04ccbb3Scarlsonj 				if (dlp->dl_nlifs == 0)
245d04ccbb3Scarlsonj 					remove_lease(dlp);
246985c053eSmeem 			}
247d04ccbb3Scarlsonj 			if (dsmp->dsm_leases == NULL)
248d04ccbb3Scarlsonj 				return (B_FALSE);
249985c053eSmeem 		}
2507c478bd9Sstevel@tonic-gate 
251d04ccbb3Scarlsonj 		if (oldstate == REBINDING && dsmp->dsm_isv6 &&
252d04ccbb3Scarlsonj 		    !save_server_id(dsmp, ack)) {
253d04ccbb3Scarlsonj 			return (B_FALSE);
254d04ccbb3Scarlsonj 		}
2557c478bd9Sstevel@tonic-gate 
256d04ccbb3Scarlsonj 		/*
257d04ccbb3Scarlsonj 		 * Handle Renew/Rebind that fails to address one of our leases.
258d04ccbb3Scarlsonj 		 * (Should just never happen, but RFC 3315 section 18.1.8
259d04ccbb3Scarlsonj 		 * requires it, and TAHI tests for it.)
260d04ccbb3Scarlsonj 		 */
261d04ccbb3Scarlsonj 		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
262d04ccbb3Scarlsonj 			if (dlp->dl_stale && dlp->dl_nlifs > 0)
263d04ccbb3Scarlsonj 				break;
264d04ccbb3Scarlsonj 		}
265d04ccbb3Scarlsonj 		if (dlp != NULL) {
266d04ccbb3Scarlsonj 			dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; "
267d04ccbb3Scarlsonj 			    "allow retransmit");
268d04ccbb3Scarlsonj 			return (B_TRUE);
269d04ccbb3Scarlsonj 		}
270d04ccbb3Scarlsonj 
271d04ccbb3Scarlsonj 		if (!set_smach_state(dsmp, BOUND))
272d04ccbb3Scarlsonj 			return (B_FALSE);
273d04ccbb3Scarlsonj 
274d04ccbb3Scarlsonj 		(void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 :
275d04ccbb3Scarlsonj 		    EVENT_EXTEND, bound_event_cb, NULL, NULL);
276d04ccbb3Scarlsonj 
277d04ccbb3Scarlsonj 		dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
278*0a3e1f6cSVasumathi Sundaram 		write_lease_to_hostconf(dsmp);
279d04ccbb3Scarlsonj 
280d04ccbb3Scarlsonj 		/* Stop sending requests now */
281d04ccbb3Scarlsonj 		stop_pkt_retransmission(dsmp);
2827c478bd9Sstevel@tonic-gate 		break;
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	case INFORM_SENT:
2857c478bd9Sstevel@tonic-gate 
286d04ccbb3Scarlsonj 		if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
287d04ccbb3Scarlsonj 			return (B_FALSE);
288d04ccbb3Scarlsonj 		}
289d04ccbb3Scarlsonj 
290d04ccbb3Scarlsonj 		(void) bound_event_cb(dsmp, NULL);
291d04ccbb3Scarlsonj 		if (!set_smach_state(dsmp, INFORMATION))
292d04ccbb3Scarlsonj 			return (B_FALSE);
293d04ccbb3Scarlsonj 
294d04ccbb3Scarlsonj 		/* Stop sending requests now */
295d04ccbb3Scarlsonj 		stop_pkt_retransmission(dsmp);
2967c478bd9Sstevel@tonic-gate 		break;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	default:
2997c478bd9Sstevel@tonic-gate 		/* something is really bizarre... */
300d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG,
301d04ccbb3Scarlsonj 		    "dhcp_bound: called in unexpected state: %s",
302d04ccbb3Scarlsonj 		    dhcp_state_to_string(dsmp->dsm_state));
303d04ccbb3Scarlsonj 		return (B_FALSE);
3047c478bd9Sstevel@tonic-gate 	}
3057c478bd9Sstevel@tonic-gate 
306d04ccbb3Scarlsonj 	return (B_TRUE);
3077c478bd9Sstevel@tonic-gate }
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate /*
31069bb4bb4Scarlsonj  * dhcp_bound_complete(): complete interface configuration after DAD
31169bb4bb4Scarlsonj  *
312d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine now ready
31369bb4bb4Scarlsonj  *  output: none
31469bb4bb4Scarlsonj  */
31569bb4bb4Scarlsonj 
31669bb4bb4Scarlsonj void
dhcp_bound_complete(dhcp_smach_t * dsmp)317d04ccbb3Scarlsonj dhcp_bound_complete(dhcp_smach_t *dsmp)
31869bb4bb4Scarlsonj {
319d04ccbb3Scarlsonj 	PKT_LIST	*ack;
320d04ccbb3Scarlsonj 	DHCP_OPT	*router_list;
321d04ccbb3Scarlsonj 	int		i;
322d04ccbb3Scarlsonj 	DHCPSTATE	oldstate;
323cfb9c9abScarlsonj 	dhcp_lif_t	*lif;
32469bb4bb4Scarlsonj 
325dc041e83Scarlsonj 	/*
326d04ccbb3Scarlsonj 	 * Do bound state entry processing only if running IPv4.  There's no
327d04ccbb3Scarlsonj 	 * need for this with DHCPv6 because link-locals are used for I/O and
328d04ccbb3Scarlsonj 	 * because DHCPv6 isn't entangled with routing.
329d04ccbb3Scarlsonj 	 */
330d04ccbb3Scarlsonj 	if (dsmp->dsm_isv6) {
331d04ccbb3Scarlsonj 		(void) set_smach_state(dsmp, BOUND);
332e704a8f2Smeem 		dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s",
333d04ccbb3Scarlsonj 		    dsmp->dsm_name);
334d04ccbb3Scarlsonj 		(void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL,
335d04ccbb3Scarlsonj 		    NULL);
336d04ccbb3Scarlsonj 		dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
337*0a3e1f6cSVasumathi Sundaram 		write_lease_to_hostconf(dsmp);
338d04ccbb3Scarlsonj 		return;
339d04ccbb3Scarlsonj 	}
340d04ccbb3Scarlsonj 
341d04ccbb3Scarlsonj 	/*
342cfb9c9abScarlsonj 	 * Add each provided router; we'll clean them up when the
343d04ccbb3Scarlsonj 	 * state machine goes away or when our lease expires.
344cfb9c9abScarlsonj 	 *
345cfb9c9abScarlsonj 	 * Note that we do not handle default routers on IPv4 logicals;
346cfb9c9abScarlsonj 	 * see README for details.
347d04ccbb3Scarlsonj 	 */
348d04ccbb3Scarlsonj 
349d04ccbb3Scarlsonj 	ack = dsmp->dsm_ack;
350d04ccbb3Scarlsonj 	router_list = ack->opts[CD_ROUTER];
351a1196271SJames Carlson 	for (i = 0; i < dsmp->dsm_pillen; i++) {
352a1196271SJames Carlson 		if (dsmp->dsm_pil[i] == CD_ROUTER)
353a1196271SJames Carlson 			router_list = NULL;
354a1196271SJames Carlson 	}
355cfb9c9abScarlsonj 	lif = dsmp->dsm_lif;
356cfb9c9abScarlsonj 	if (router_list != NULL &&
357cfb9c9abScarlsonj 	    (router_list->len % sizeof (ipaddr_t)) == 0 &&
358e11c3f44Smeem 	    strchr(lif->lif_name, ':') == NULL &&
359e11c3f44Smeem 	    !lif->lif_pif->pif_under_ipmp) {
360d04ccbb3Scarlsonj 
361d04ccbb3Scarlsonj 		dsmp->dsm_nrouters = router_list->len / sizeof (ipaddr_t);
362d04ccbb3Scarlsonj 		dsmp->dsm_routers  = malloc(router_list->len);
363d04ccbb3Scarlsonj 		if (dsmp->dsm_routers == NULL) {
364e704a8f2Smeem 			dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot allocate "
365d04ccbb3Scarlsonj 			    "default router list, ignoring default routers");
366d04ccbb3Scarlsonj 			dsmp->dsm_nrouters = 0;
367d04ccbb3Scarlsonj 		}
368d04ccbb3Scarlsonj 
369d04ccbb3Scarlsonj 		for (i = 0; i < dsmp->dsm_nrouters; i++) {
370d04ccbb3Scarlsonj 
371d04ccbb3Scarlsonj 			(void) memcpy(&dsmp->dsm_routers[i].s_addr,
372d04ccbb3Scarlsonj 			    router_list->value + (i * sizeof (ipaddr_t)),
373d04ccbb3Scarlsonj 			    sizeof (ipaddr_t));
374d04ccbb3Scarlsonj 
375cfb9c9abScarlsonj 			if (!add_default_route(lif->lif_pif->pif_index,
376d04ccbb3Scarlsonj 			    &dsmp->dsm_routers[i])) {
377e704a8f2Smeem 				dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot "
378e704a8f2Smeem 				    "add default router %s on %s", inet_ntoa(
379d04ccbb3Scarlsonj 				    dsmp->dsm_routers[i]), dsmp->dsm_name);
380d04ccbb3Scarlsonj 				dsmp->dsm_routers[i].s_addr = htonl(INADDR_ANY);
381d04ccbb3Scarlsonj 				continue;
382d04ccbb3Scarlsonj 			}
383d04ccbb3Scarlsonj 
384d04ccbb3Scarlsonj 			dhcpmsg(MSG_INFO, "added default router %s on %s",
385d04ccbb3Scarlsonj 			    inet_ntoa(dsmp->dsm_routers[i]), dsmp->dsm_name);
386d04ccbb3Scarlsonj 		}
387d04ccbb3Scarlsonj 	}
388d04ccbb3Scarlsonj 
389d04ccbb3Scarlsonj 	oldstate = dsmp->dsm_state;
390d04ccbb3Scarlsonj 	if (!set_smach_state(dsmp, BOUND)) {
391d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR,
392e704a8f2Smeem 		    "dhcp_bound_complete: cannot set bound state on %s",
393d04ccbb3Scarlsonj 		    dsmp->dsm_name);
394d04ccbb3Scarlsonj 		return;
395d04ccbb3Scarlsonj 	}
396d04ccbb3Scarlsonj 
397e704a8f2Smeem 	dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name);
398d04ccbb3Scarlsonj 
399d04ccbb3Scarlsonj 	/*
400d04ccbb3Scarlsonj 	 * We're now committed to this binding, so if it came from BOOTP, set
401d04ccbb3Scarlsonj 	 * the flag.
402d04ccbb3Scarlsonj 	 */
403d04ccbb3Scarlsonj 
404d04ccbb3Scarlsonj 	if (ack->opts[CD_DHCP_TYPE] == NULL)
405d04ccbb3Scarlsonj 		dsmp->dsm_dflags |= DHCP_IF_BOOTP;
406d04ccbb3Scarlsonj 
407d04ccbb3Scarlsonj 	/*
408d04ccbb3Scarlsonj 	 * If the previous state was ADOPTING, event loop has not been started
409dc041e83Scarlsonj 	 * at this time; so don't run the EVENT_BOUND script.
410dc041e83Scarlsonj 	 */
411d04ccbb3Scarlsonj 	if (oldstate != ADOPTING) {
412d04ccbb3Scarlsonj 		(void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL,
413dc041e83Scarlsonj 		    NULL);
414dc041e83Scarlsonj 	}
41569bb4bb4Scarlsonj 
416d04ccbb3Scarlsonj 	dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
417*0a3e1f6cSVasumathi Sundaram 	write_lease_to_hostconf(dsmp);
41869bb4bb4Scarlsonj }
41969bb4bb4Scarlsonj 
42069bb4bb4Scarlsonj /*
421d04ccbb3Scarlsonj  * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131.
422d04ccbb3Scarlsonj  *	      We use up to plus or minus 2% jitter in the time.  This is a
423d04ccbb3Scarlsonj  *	      small value, but the timers involved are typically long.  A
424d04ccbb3Scarlsonj  *	      common T1 value is one day, and the fuzz is up to 28.8 minutes;
425d04ccbb3Scarlsonj  *	      plenty of time to make sure that individual clients don't renew
426d04ccbb3Scarlsonj  *	      all at the same time.
4277c478bd9Sstevel@tonic-gate  *
428d04ccbb3Scarlsonj  *   input: uint32_t: the number of seconds until lease expiration
429d04ccbb3Scarlsonj  *	    double: the approximate percentage of that time to return
430d04ccbb3Scarlsonj  *  output: double: a number approximating (sec * pct)
4317c478bd9Sstevel@tonic-gate  */
4327c478bd9Sstevel@tonic-gate 
433d04ccbb3Scarlsonj static double
fuzzify(uint32_t sec,double pct)434d04ccbb3Scarlsonj fuzzify(uint32_t sec, double pct)
4357c478bd9Sstevel@tonic-gate {
436d04ccbb3Scarlsonj 	return (sec * (pct + (drand48() - 0.5) / 25.0));
4377c478bd9Sstevel@tonic-gate }
4387c478bd9Sstevel@tonic-gate 
439d04ccbb3Scarlsonj /*
440d04ccbb3Scarlsonj  * get_pkt_times(): pulls the lease times out of a v4 DHCP packet and stores
441d04ccbb3Scarlsonj  *		    them as host byte-order relative times in the passed in
442d04ccbb3Scarlsonj  *		    parameters.
443d04ccbb3Scarlsonj  *
444d04ccbb3Scarlsonj  *   input: PKT_LIST *: the packet to pull the packet times from
445d04ccbb3Scarlsonj  *	    lease_t *: where to store the relative lease time in hbo
446d04ccbb3Scarlsonj  *	    lease_t *: where to store the relative t1 time in hbo
447d04ccbb3Scarlsonj  *	    lease_t *: where to store the relative t2 time in hbo
448d04ccbb3Scarlsonj  *  output: void
449d04ccbb3Scarlsonj  */
450d04ccbb3Scarlsonj 
451d04ccbb3Scarlsonj static void
get_pkt_times(PKT_LIST * ack,lease_t * lease,lease_t * t1,lease_t * t2)452d04ccbb3Scarlsonj get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2)
453d04ccbb3Scarlsonj {
454d04ccbb3Scarlsonj 	*lease	= DHCP_PERM;
455d04ccbb3Scarlsonj 	*t1	= DHCP_PERM;
456d04ccbb3Scarlsonj 	*t2	= DHCP_PERM;
457d04ccbb3Scarlsonj 
458d04ccbb3Scarlsonj 	if (ack->opts[CD_DHCP_TYPE] == NULL) {
459d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE,
460d04ccbb3Scarlsonj 		    "get_pkt_times: BOOTP response; infinite lease");
461d04ccbb3Scarlsonj 		return;
462d04ccbb3Scarlsonj 	}
463d04ccbb3Scarlsonj 	if (ack->opts[CD_LEASE_TIME] == NULL) {
464d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE,
465d04ccbb3Scarlsonj 		    "get_pkt_times: no lease option provided");
466d04ccbb3Scarlsonj 		return;
467d04ccbb3Scarlsonj 	}
468d04ccbb3Scarlsonj 	if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
469d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option");
470d04ccbb3Scarlsonj 	}
471d04ccbb3Scarlsonj 
472d04ccbb3Scarlsonj 	(void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
473d04ccbb3Scarlsonj 	*lease = ntohl(*lease);
474d04ccbb3Scarlsonj 
475d04ccbb3Scarlsonj 	if (*lease == DHCP_PERM) {
476d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted");
477d04ccbb3Scarlsonj 		return;
478d04ccbb3Scarlsonj 	}
479d04ccbb3Scarlsonj 
480d04ccbb3Scarlsonj 	if (ack->opts[CD_T1_TIME] != NULL &&
481d04ccbb3Scarlsonj 	    ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) {
482d04ccbb3Scarlsonj 		(void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1));
483d04ccbb3Scarlsonj 		*t1 = ntohl(*t1);
484d04ccbb3Scarlsonj 	}
485d04ccbb3Scarlsonj 
486d04ccbb3Scarlsonj 	if (ack->opts[CD_T2_TIME] != NULL &&
487d04ccbb3Scarlsonj 	    ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) {
488d04ccbb3Scarlsonj 		(void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2));
489d04ccbb3Scarlsonj 		*t2 = ntohl(*t2);
490d04ccbb3Scarlsonj 	}
491d04ccbb3Scarlsonj 
492d04ccbb3Scarlsonj 	if ((*t1 == DHCP_PERM) || (*t1 >= *lease))
493d04ccbb3Scarlsonj 		*t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT);
494d04ccbb3Scarlsonj 
495d04ccbb3Scarlsonj 	if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1))
496d04ccbb3Scarlsonj 		*t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT);
497d04ccbb3Scarlsonj 
498d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u",
499d04ccbb3Scarlsonj 	    *lease, *t1, *t2);
500d04ccbb3Scarlsonj }
501d04ccbb3Scarlsonj 
502d04ccbb3Scarlsonj /*
503d04ccbb3Scarlsonj  * configure_v4_timers(): configures the lease timers on a v4 state machine
504d04ccbb3Scarlsonj  *
505d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to configure
506d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
507d04ccbb3Scarlsonj  */
508d04ccbb3Scarlsonj 
509d04ccbb3Scarlsonj static boolean_t
configure_v4_timers(dhcp_smach_t * dsmp)510d04ccbb3Scarlsonj configure_v4_timers(dhcp_smach_t *dsmp)
511d04ccbb3Scarlsonj {
512d04ccbb3Scarlsonj 	PKT_LIST	*ack = dsmp->dsm_ack;
513d04ccbb3Scarlsonj 	lease_t		lease, t1, t2;
514d04ccbb3Scarlsonj 	dhcp_lease_t	*dlp;
515d04ccbb3Scarlsonj 	dhcp_lif_t	*lif;
516d04ccbb3Scarlsonj 
517d04ccbb3Scarlsonj 	/* v4 has just one lease per state machine, and one LIF */
518d04ccbb3Scarlsonj 	dlp = dsmp->dsm_leases;
519d04ccbb3Scarlsonj 	lif = dlp->dl_lifs;
520d04ccbb3Scarlsonj 
521d04ccbb3Scarlsonj 	/*
522d04ccbb3Scarlsonj 	 * If it's DHCP, but there's no valid lease time, then complain,
523d04ccbb3Scarlsonj 	 * decline the lease and return error.
524d04ccbb3Scarlsonj 	 */
525d04ccbb3Scarlsonj 	if (ack->opts[CD_DHCP_TYPE] != NULL &&
526d04ccbb3Scarlsonj 	    (ack->opts[CD_LEASE_TIME] == NULL ||
527d04ccbb3Scarlsonj 	    ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
528d04ccbb3Scarlsonj 		lif_mark_decline(lif, "Missing or corrupted lease time");
529d04ccbb3Scarlsonj 		send_declines(dsmp);
530d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in "
531d04ccbb3Scarlsonj 		    "ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" :
532d04ccbb3Scarlsonj 		    "corrupt", dsmp->dsm_name);
533d04ccbb3Scarlsonj 		return (B_FALSE);
534d04ccbb3Scarlsonj 	}
535d04ccbb3Scarlsonj 
536d04ccbb3Scarlsonj 	/* Stop the T1 and T2 timers */
537d04ccbb3Scarlsonj 	cancel_lease_timers(dlp);
538d04ccbb3Scarlsonj 
539d04ccbb3Scarlsonj 	/* Stop the LEASE timer */
540d04ccbb3Scarlsonj 	cancel_lif_timers(lif);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	/*
5437c478bd9Sstevel@tonic-gate 	 * type has already been verified as ACK.  if type is not set,
5447c478bd9Sstevel@tonic-gate 	 * then we got a BOOTP packet.  we now fetch the t1, t2, and
5457c478bd9Sstevel@tonic-gate 	 * lease options out of the packet into variables.  they are
5467c478bd9Sstevel@tonic-gate 	 * returned as relative host-byte-ordered times.
5477c478bd9Sstevel@tonic-gate 	 */
5487c478bd9Sstevel@tonic-gate 
549d04ccbb3Scarlsonj 	get_pkt_times(ack, &lease, &t1, &t2);
5507c478bd9Sstevel@tonic-gate 
551d04ccbb3Scarlsonj 	/*
552d04ccbb3Scarlsonj 	 * if the current lease is mysteriously close to the new
553d04ccbb3Scarlsonj 	 * lease, warn the user.  unless there's less than a minute
554d04ccbb3Scarlsonj 	 * left, round to the closest minute.
555d04ccbb3Scarlsonj 	 */
5567c478bd9Sstevel@tonic-gate 
557d04ccbb3Scarlsonj 	if (lif->lif_expire.dt_start != 0 &&
558d04ccbb3Scarlsonj 	    abs((dsmp->dsm_newstart_monosec + lease) -
559d04ccbb3Scarlsonj 	    (dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) <
560d04ccbb3Scarlsonj 	    DHCP_LEASE_EPS) {
561d04ccbb3Scarlsonj 		const char *noext = "configure_v4_timers: lease renewed but "
562d04ccbb3Scarlsonj 		    "time not extended";
563d04ccbb3Scarlsonj 		int msg_level;
564d04ccbb3Scarlsonj 		uint_t minleft;
565d04ccbb3Scarlsonj 
566d04ccbb3Scarlsonj 		if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH)
567d04ccbb3Scarlsonj 			msg_level = MSG_ERROR;
568d04ccbb3Scarlsonj 		else
569d04ccbb3Scarlsonj 			msg_level = MSG_VERBOSE;
570d04ccbb3Scarlsonj 
571d04ccbb3Scarlsonj 		minleft = (lif->lif_expire.dt_start + 30) / 60;
572d04ccbb3Scarlsonj 
573d04ccbb3Scarlsonj 		if (lif->lif_expire.dt_start < 60) {
574d04ccbb3Scarlsonj 			dhcpmsg(msg_level, "%s; expires in %d seconds",
575d04ccbb3Scarlsonj 			    noext, lif->lif_expire.dt_start);
576d04ccbb3Scarlsonj 		} else if (minleft == 1) {
577d04ccbb3Scarlsonj 			dhcpmsg(msg_level, "%s; expires in 1 minute", noext);
578d04ccbb3Scarlsonj 		} else if (minleft > 120) {
579d04ccbb3Scarlsonj 			dhcpmsg(msg_level, "%s; expires in %d hours",
580d04ccbb3Scarlsonj 			    noext, (minleft + 30) / 60);
581d04ccbb3Scarlsonj 		} else {
582d04ccbb3Scarlsonj 			dhcpmsg(msg_level, "%s; expires in %d minutes",
583d04ccbb3Scarlsonj 			    noext, minleft);
584d04ccbb3Scarlsonj 		}
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate 
587d04ccbb3Scarlsonj 	init_timer(&dlp->dl_t1, t1);
588d04ccbb3Scarlsonj 	init_timer(&dlp->dl_t2, t2);
589d04ccbb3Scarlsonj 	init_timer(&lif->lif_expire, lease);
5907c478bd9Sstevel@tonic-gate 
591d04ccbb3Scarlsonj 	if (lease == DHCP_PERM) {
592d04ccbb3Scarlsonj 		dhcpmsg(MSG_INFO,
593d04ccbb3Scarlsonj 		    "configure_v4_timers: %s acquired permanent lease",
594d04ccbb3Scarlsonj 		    dsmp->dsm_name);
595d04ccbb3Scarlsonj 		return (B_TRUE);
596d04ccbb3Scarlsonj 	}
5977c478bd9Sstevel@tonic-gate 
598d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s",
599d04ccbb3Scarlsonj 	    dsmp->dsm_name,
600d04ccbb3Scarlsonj 	    monosec_to_string(dsmp->dsm_newstart_monosec + lease));
601d04ccbb3Scarlsonj 
602d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s",
603d04ccbb3Scarlsonj 	    dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
604d04ccbb3Scarlsonj 	    dlp->dl_t1.dt_start));
605d04ccbb3Scarlsonj 
606d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s",
607d04ccbb3Scarlsonj 	    dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
608d04ccbb3Scarlsonj 	    dlp->dl_t2.dt_start));
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	/*
6117c478bd9Sstevel@tonic-gate 	 * according to RFC2131, there is no minimum lease time, but don't
6127c478bd9Sstevel@tonic-gate 	 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN.
6137c478bd9Sstevel@tonic-gate 	 */
6147c478bd9Sstevel@tonic-gate 
615d04ccbb3Scarlsonj 	if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
6167c478bd9Sstevel@tonic-gate 		goto failure;
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	if (lease < DHCP_REBIND_MIN) {
619d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for "
620d04ccbb3Scarlsonj 		    "less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN);
621d04ccbb3Scarlsonj 		return (B_TRUE);
6227c478bd9Sstevel@tonic-gate 	}
6237c478bd9Sstevel@tonic-gate 
624d04ccbb3Scarlsonj 	if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew))
6257c478bd9Sstevel@tonic-gate 		goto failure;
6267c478bd9Sstevel@tonic-gate 
627d04ccbb3Scarlsonj 	if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))
6287c478bd9Sstevel@tonic-gate 		goto failure;
6297c478bd9Sstevel@tonic-gate 
630d04ccbb3Scarlsonj 	return (B_TRUE);
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate failure:
633d04ccbb3Scarlsonj 	cancel_lease_timers(dlp);
634d04ccbb3Scarlsonj 	cancel_lif_timers(lif);
635d04ccbb3Scarlsonj 	dhcpmsg(MSG_WARNING,
636d04ccbb3Scarlsonj 	    "configure_v4_timers: cannot schedule lease timers");
637d04ccbb3Scarlsonj 	return (B_FALSE);
6387c478bd9Sstevel@tonic-gate }
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate /*
641d04ccbb3Scarlsonj  * configure_v6_leases(): configures the IPv6 leases on a state machine from
642d04ccbb3Scarlsonj  *			  the current DHCPv6 ACK.  We need to scan the ACK,
643d04ccbb3Scarlsonj  *			  create a lease for each IA_NA, and a new LIF for each
644d04ccbb3Scarlsonj  *			  IAADDR.
6457c478bd9Sstevel@tonic-gate  *
646d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack)
647d04ccbb3Scarlsonj  *  output: enum v6_bind_result: restart, resend, or done
6487c478bd9Sstevel@tonic-gate  */
6497c478bd9Sstevel@tonic-gate 
650d04ccbb3Scarlsonj static enum v6_bind_result
configure_v6_leases(dhcp_smach_t * dsmp)651d04ccbb3Scarlsonj configure_v6_leases(dhcp_smach_t *dsmp)
6527c478bd9Sstevel@tonic-gate {
653d04ccbb3Scarlsonj 	const dhcpv6_option_t *d6o, *d6so, *d6sso;
654d04ccbb3Scarlsonj 	const char *optbase, *estr, *msg;
655d04ccbb3Scarlsonj 	uint_t olen, solen, ssolen, msglen;
656d04ccbb3Scarlsonj 	dhcpv6_ia_na_t d6in;
657d04ccbb3Scarlsonj 	dhcpv6_iaaddr_t d6ia;
658d04ccbb3Scarlsonj 	dhcp_lease_t *dlp;
659d04ccbb3Scarlsonj 	uint32_t shortest;
660d04ccbb3Scarlsonj 	dhcp_lif_t *lif;
661d04ccbb3Scarlsonj 	uint_t nlifs;
662d04ccbb3Scarlsonj 	boolean_t got_iana = B_FALSE;
663d04ccbb3Scarlsonj 	uint_t scode;
664d04ccbb3Scarlsonj 
665d04ccbb3Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next)
666d04ccbb3Scarlsonj 		dlp->dl_stale = B_TRUE;
667d04ccbb3Scarlsonj 
668d04ccbb3Scarlsonj 	d6o = NULL;
669d04ccbb3Scarlsonj 	while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA,
670d04ccbb3Scarlsonj 	    &olen)) != NULL) {
671d04ccbb3Scarlsonj 		if (olen < sizeof (d6in)) {
672d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
673d04ccbb3Scarlsonj 			    "configure_v6_leases: garbled IA_NA");
674d04ccbb3Scarlsonj 			continue;
675d04ccbb3Scarlsonj 		}
676d04ccbb3Scarlsonj 
677d04ccbb3Scarlsonj 		/*
678d04ccbb3Scarlsonj 		 * Check the IAID.  It should be for our controlling LIF.  If a
679d04ccbb3Scarlsonj 		 * single state machine needs to use multiple IAIDs, then this
680d04ccbb3Scarlsonj 		 * will need to change.
681d04ccbb3Scarlsonj 		 */
682d04ccbb3Scarlsonj 		(void) memcpy(&d6in, d6o, sizeof (d6in));
683d04ccbb3Scarlsonj 		d6in.d6in_iaid = ntohl(d6in.d6in_iaid);
684d04ccbb3Scarlsonj 		if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) {
685d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
686d04ccbb3Scarlsonj 			    "IA_NA for IAID %x (not %x)", d6in.d6in_iaid,
687d04ccbb3Scarlsonj 			    dsmp->dsm_lif->lif_iaid);
688d04ccbb3Scarlsonj 			continue;
689d04ccbb3Scarlsonj 		}
690d04ccbb3Scarlsonj 
691d04ccbb3Scarlsonj 		/*
692d04ccbb3Scarlsonj 		 * See notes below; there's only one IA_NA and a single IAID
693d04ccbb3Scarlsonj 		 * for now.
694d04ccbb3Scarlsonj 		 */
695d04ccbb3Scarlsonj 		if ((dlp = dsmp->dsm_leases) != NULL)
696d04ccbb3Scarlsonj 			dlp->dl_stale = B_FALSE;
697d04ccbb3Scarlsonj 
698d04ccbb3Scarlsonj 		/*
699d04ccbb3Scarlsonj 		 * Note that some bug-ridden servers will try to give us
700d04ccbb3Scarlsonj 		 * multiple IA_NA options for a single IAID.  We ignore
701d04ccbb3Scarlsonj 		 * duplicates.
702d04ccbb3Scarlsonj 		 */
703d04ccbb3Scarlsonj 		if (got_iana) {
704d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected "
705d04ccbb3Scarlsonj 			    "extra IA_NA ignored");
706d04ccbb3Scarlsonj 			continue;
707d04ccbb3Scarlsonj 		}
708d04ccbb3Scarlsonj 
709d04ccbb3Scarlsonj 		d6in.d6in_t1 = ntohl(d6in.d6in_t1);
710d04ccbb3Scarlsonj 		d6in.d6in_t2 = ntohl(d6in.d6in_t2);
711d04ccbb3Scarlsonj 
712d04ccbb3Scarlsonj 		/* RFC 3315 required check for invalid T1/T2 combinations */
713d04ccbb3Scarlsonj 		if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) {
714d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
715d04ccbb3Scarlsonj 			    "IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1,
716d04ccbb3Scarlsonj 			    d6in.d6in_t2);
717d04ccbb3Scarlsonj 			continue;
718d04ccbb3Scarlsonj 		}
719d04ccbb3Scarlsonj 
720d04ccbb3Scarlsonj 		/*
721d04ccbb3Scarlsonj 		 * There may be a status code here.  Process if present.
722d04ccbb3Scarlsonj 		 */
723d04ccbb3Scarlsonj 		optbase = (const char *)d6o + sizeof (d6in);
724d04ccbb3Scarlsonj 		olen -= sizeof (d6in);
725d04ccbb3Scarlsonj 		d6so = dhcpv6_find_option(optbase, olen, NULL,
726d04ccbb3Scarlsonj 		    DHCPV6_OPT_STATUS_CODE, &solen);
727d04ccbb3Scarlsonj 		scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen);
728d04ccbb3Scarlsonj 		if (scode != DHCPV6_STAT_SUCCESS) {
729d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
730d04ccbb3Scarlsonj 			    "configure_v6_leases: IA_NA: %s: %.*s",
731d04ccbb3Scarlsonj 			    estr, msglen, msg);
732d04ccbb3Scarlsonj 		}
733d04ccbb3Scarlsonj 		print_server_msg(dsmp, msg, msglen);
734d04ccbb3Scarlsonj 
735d04ccbb3Scarlsonj 		/*
736d04ccbb3Scarlsonj 		 * Other errors are possible here.  According to RFC 3315
737d04ccbb3Scarlsonj 		 * section 18.1.8, we ignore the entire IA if it gives the "no
738d04ccbb3Scarlsonj 		 * addresses" status code.  We may try another server if we
739d04ccbb3Scarlsonj 		 * like -- we instead opt to allow the addresses to expire and
740d04ccbb3Scarlsonj 		 * then try a new server.
741d04ccbb3Scarlsonj 		 *
742d04ccbb3Scarlsonj 		 * If the status code is "no binding," then we must go back and
743d04ccbb3Scarlsonj 		 * redo the Request.  Surprisingly, it doesn't matter if it's
744d04ccbb3Scarlsonj 		 * any other code.
745d04ccbb3Scarlsonj 		 */
746d04ccbb3Scarlsonj 		if (scode == DHCPV6_STAT_NOADDRS) {
747d04ccbb3Scarlsonj 			dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring "
748d04ccbb3Scarlsonj 			    "no-addrs status in IA_NA");
749d04ccbb3Scarlsonj 			continue;
750d04ccbb3Scarlsonj 		}
751d04ccbb3Scarlsonj 
752d04ccbb3Scarlsonj 		if (scode == DHCPV6_STAT_NOBINDING) {
753d04ccbb3Scarlsonj 			send_v6_request(dsmp);
754d04ccbb3Scarlsonj 			return (v6Resent);
755d04ccbb3Scarlsonj 		}
756d04ccbb3Scarlsonj 
757d04ccbb3Scarlsonj 		/*
758d04ccbb3Scarlsonj 		 * Find or create the lease structure.  This part is simple,
759d04ccbb3Scarlsonj 		 * because we support only IA_NA and a single IAID.  This means
760d04ccbb3Scarlsonj 		 * there's only one lease structure.  The design supports
761d04ccbb3Scarlsonj 		 * multiple lease structures so that IA_TA and IA_PD can be
762d04ccbb3Scarlsonj 		 * added later.
763d04ccbb3Scarlsonj 		 */
764d04ccbb3Scarlsonj 		if ((dlp = dsmp->dsm_leases) == NULL &&
765d04ccbb3Scarlsonj 		    (dlp = insert_lease(dsmp)) == NULL) {
766d04ccbb3Scarlsonj 			dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to "
767d04ccbb3Scarlsonj 			    "allocate memory for lease");
768d04ccbb3Scarlsonj 			return (v6Restart);
769d04ccbb3Scarlsonj 		}
770d04ccbb3Scarlsonj 
771d04ccbb3Scarlsonj 		/*
772d04ccbb3Scarlsonj 		 * Iterate over the IAADDR options contained within this IA_NA.
773d04ccbb3Scarlsonj 		 */
774d04ccbb3Scarlsonj 		shortest = DHCPV6_INFTIME;
775d04ccbb3Scarlsonj 		d6so = NULL;
776d04ccbb3Scarlsonj 		while ((d6so = dhcpv6_find_option(optbase, olen, d6so,
777d04ccbb3Scarlsonj 		    DHCPV6_OPT_IAADDR, &solen)) != NULL) {
778d04ccbb3Scarlsonj 			if (solen < sizeof (d6ia)) {
779d04ccbb3Scarlsonj 				dhcpmsg(MSG_WARNING,
780d04ccbb3Scarlsonj 				    "configure_v6_leases: garbled IAADDR");
781d04ccbb3Scarlsonj 				continue;
782d04ccbb3Scarlsonj 			}
783d04ccbb3Scarlsonj 			(void) memcpy(&d6ia, d6so, sizeof (d6ia));
784d04ccbb3Scarlsonj 
785d04ccbb3Scarlsonj 			d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife);
786d04ccbb3Scarlsonj 			d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife);
787d04ccbb3Scarlsonj 
788d04ccbb3Scarlsonj 			/* RFC 3315 required validity check */
789d04ccbb3Scarlsonj 			if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) {
790d04ccbb3Scarlsonj 				dhcpmsg(MSG_WARNING,
791d04ccbb3Scarlsonj 				    "configure_v6_leases: ignored IAADDR with "
792d04ccbb3Scarlsonj 				    "preferred lifetime %u > valid %u",
793d04ccbb3Scarlsonj 				    d6ia.d6ia_preflife, d6ia.d6ia_vallife);
794d04ccbb3Scarlsonj 				continue;
795d04ccbb3Scarlsonj 			}
796d04ccbb3Scarlsonj 
797d04ccbb3Scarlsonj 			/*
798d04ccbb3Scarlsonj 			 * RFC 3315 allows a status code to be buried inside
799d04ccbb3Scarlsonj 			 * the IAADDR option.  Look for it, and process if
800d04ccbb3Scarlsonj 			 * present.  Process in a manner similar to that for
801d04ccbb3Scarlsonj 			 * the IA itself; TAHI checks for this.  Real servers
802d04ccbb3Scarlsonj 			 * likely won't do this.
803d04ccbb3Scarlsonj 			 */
804d04ccbb3Scarlsonj 			d6sso = dhcpv6_find_option((const char *)d6so +
805d04ccbb3Scarlsonj 			    sizeof (d6ia), solen - sizeof (d6ia), NULL,
806d04ccbb3Scarlsonj 			    DHCPV6_OPT_STATUS_CODE, &ssolen);
807d04ccbb3Scarlsonj 			scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg,
808d04ccbb3Scarlsonj 			    &msglen);
809d04ccbb3Scarlsonj 			print_server_msg(dsmp, msg, msglen);
810d04ccbb3Scarlsonj 			if (scode == DHCPV6_STAT_NOADDRS) {
811d04ccbb3Scarlsonj 				dhcpmsg(MSG_DEBUG, "configure_v6_leases: "
812d04ccbb3Scarlsonj 				    "ignoring no-addrs status in IAADDR");
813d04ccbb3Scarlsonj 				continue;
814d04ccbb3Scarlsonj 			}
815d04ccbb3Scarlsonj 			if (scode == DHCPV6_STAT_NOBINDING) {
816d04ccbb3Scarlsonj 				send_v6_request(dsmp);
817d04ccbb3Scarlsonj 				return (v6Resent);
818d04ccbb3Scarlsonj 			}
819d04ccbb3Scarlsonj 			if (scode != DHCPV6_STAT_SUCCESS) {
820d04ccbb3Scarlsonj 				dhcpmsg(MSG_WARNING,
821d04ccbb3Scarlsonj 				    "configure_v6_leases: IAADDR: %s", estr);
822d04ccbb3Scarlsonj 			}
823d04ccbb3Scarlsonj 
824d04ccbb3Scarlsonj 			/*
825d04ccbb3Scarlsonj 			 * Locate the existing LIF within the lease associated
826d04ccbb3Scarlsonj 			 * with this address, if any.
827d04ccbb3Scarlsonj 			 */
828d04ccbb3Scarlsonj 			lif = dlp->dl_lifs;
829d04ccbb3Scarlsonj 			for (nlifs = dlp->dl_nlifs; nlifs > 0;
830d04ccbb3Scarlsonj 			    nlifs--, lif = lif->lif_next) {
831d04ccbb3Scarlsonj 				if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr,
832d04ccbb3Scarlsonj 				    &lif->lif_v6addr))
833d04ccbb3Scarlsonj 					break;
834d04ccbb3Scarlsonj 			}
835d04ccbb3Scarlsonj 
836d04ccbb3Scarlsonj 			/*
837d04ccbb3Scarlsonj 			 * If the server has set the lifetime to zero, then
838d04ccbb3Scarlsonj 			 * delete the LIF.  Otherwise, set the new LIF expiry
839d04ccbb3Scarlsonj 			 * time, adding the LIF if necessary.
840d04ccbb3Scarlsonj 			 */
841d04ccbb3Scarlsonj 			if (d6ia.d6ia_vallife == 0) {
842d04ccbb3Scarlsonj 				/* If it was found, then it's expired */
843d04ccbb3Scarlsonj 				if (nlifs != 0) {
844d04ccbb3Scarlsonj 					dhcpmsg(MSG_DEBUG,
845d04ccbb3Scarlsonj 					    "configure_v6_leases: lif %s has "
846d04ccbb3Scarlsonj 					    "expired", lif->lif_name);
847d04ccbb3Scarlsonj 					lif->lif_expired = B_TRUE;
848d04ccbb3Scarlsonj 				}
849d04ccbb3Scarlsonj 				continue;
850d04ccbb3Scarlsonj 			}
851d04ccbb3Scarlsonj 
852d04ccbb3Scarlsonj 			/* If it wasn't found, then create it now. */
853d04ccbb3Scarlsonj 			if (nlifs == 0) {
854d04ccbb3Scarlsonj 				lif = plumb_lif(dsmp->dsm_lif->lif_pif,
855d04ccbb3Scarlsonj 				    &d6ia.d6ia_addr);
856d04ccbb3Scarlsonj 				if (lif == NULL)
857d04ccbb3Scarlsonj 					continue;
858d04ccbb3Scarlsonj 				if (++dlp->dl_nlifs == 1) {
859d04ccbb3Scarlsonj 					dlp->dl_lifs = lif;
860d04ccbb3Scarlsonj 				} else {
861d04ccbb3Scarlsonj 					remque(lif);
862d04ccbb3Scarlsonj 					insque(lif, dlp->dl_lifs);
863d04ccbb3Scarlsonj 				}
864d04ccbb3Scarlsonj 				lif->lif_lease = dlp;
865d04ccbb3Scarlsonj 				lif->lif_dad_wait = _B_TRUE;
866d04ccbb3Scarlsonj 				dsmp->dsm_lif_wait++;
867d04ccbb3Scarlsonj 			} else {
868d04ccbb3Scarlsonj 				/* If it was found, cancel timer */
869d04ccbb3Scarlsonj 				cancel_lif_timers(lif);
870d04ccbb3Scarlsonj 				if (d6ia.d6ia_preflife != 0 &&
871d04ccbb3Scarlsonj 				    !clear_lif_deprecated(lif)) {
872d04ccbb3Scarlsonj 					unplumb_lif(lif);
873d04ccbb3Scarlsonj 					continue;
874d04ccbb3Scarlsonj 				}
875d04ccbb3Scarlsonj 			}
876d04ccbb3Scarlsonj 
877d04ccbb3Scarlsonj 			/* Set the new expiry timers */
878d04ccbb3Scarlsonj 			init_timer(&lif->lif_preferred, d6ia.d6ia_preflife);
879d04ccbb3Scarlsonj 			init_timer(&lif->lif_expire, d6ia.d6ia_vallife);
880d04ccbb3Scarlsonj 
881d04ccbb3Scarlsonj 			/*
882d04ccbb3Scarlsonj 			 * If the preferred lifetime is over now, then the LIF
883d04ccbb3Scarlsonj 			 * is deprecated.  If it's the same as the expiry time,
884d04ccbb3Scarlsonj 			 * then we don't need a separate timer for it.
885d04ccbb3Scarlsonj 			 */
886d04ccbb3Scarlsonj 			if (d6ia.d6ia_preflife == 0) {
887d04ccbb3Scarlsonj 				set_lif_deprecated(lif);
888d04ccbb3Scarlsonj 			} else if (d6ia.d6ia_preflife != DHCPV6_INFTIME &&
889d04ccbb3Scarlsonj 			    d6ia.d6ia_preflife != d6ia.d6ia_vallife &&
890d04ccbb3Scarlsonj 			    !schedule_lif_timer(lif, &lif->lif_preferred,
891d04ccbb3Scarlsonj 			    dhcp_deprecate)) {
892d04ccbb3Scarlsonj 				unplumb_lif(lif);
893d04ccbb3Scarlsonj 				continue;
894d04ccbb3Scarlsonj 			}
895d04ccbb3Scarlsonj 
896d04ccbb3Scarlsonj 			if (d6ia.d6ia_vallife != DHCPV6_INFTIME &&
897d04ccbb3Scarlsonj 			    !schedule_lif_timer(lif, &lif->lif_expire,
898d04ccbb3Scarlsonj 			    dhcp_expire)) {
899d04ccbb3Scarlsonj 				unplumb_lif(lif);
900d04ccbb3Scarlsonj 				continue;
901d04ccbb3Scarlsonj 			}
902d04ccbb3Scarlsonj 
903d04ccbb3Scarlsonj 			if (d6ia.d6ia_preflife < shortest)
904d04ccbb3Scarlsonj 				shortest = d6ia.d6ia_preflife;
905d04ccbb3Scarlsonj 		}
906d04ccbb3Scarlsonj 
907d04ccbb3Scarlsonj 		if (dlp->dl_nlifs == 0) {
908d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING,
909d04ccbb3Scarlsonj 			    "configure_v6_leases: no IAADDRs found in IA_NA");
910d04ccbb3Scarlsonj 			remove_lease(dlp);
911d04ccbb3Scarlsonj 			continue;
912d04ccbb3Scarlsonj 		}
913d04ccbb3Scarlsonj 
914d04ccbb3Scarlsonj 		if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) {
915d04ccbb3Scarlsonj 			/* Default values from RFC 3315: 0.5 and 0.8 */
916d04ccbb3Scarlsonj 			if ((d6in.d6in_t1 = shortest / 2) == 0)
917d04ccbb3Scarlsonj 				d6in.d6in_t1 = 1;
918d04ccbb3Scarlsonj 			d6in.d6in_t2 = shortest - shortest / 5;
919d04ccbb3Scarlsonj 		}
920d04ccbb3Scarlsonj 
921d04ccbb3Scarlsonj 		cancel_lease_timers(dlp);
922d04ccbb3Scarlsonj 		init_timer(&dlp->dl_t1, d6in.d6in_t1);
923d04ccbb3Scarlsonj 		init_timer(&dlp->dl_t2, d6in.d6in_t2);
924d04ccbb3Scarlsonj 
925d04ccbb3Scarlsonj 		if ((d6in.d6in_t1 != DHCPV6_INFTIME &&
926d04ccbb3Scarlsonj 		    !schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) ||
927d04ccbb3Scarlsonj 		    (d6in.d6in_t2 != DHCPV6_INFTIME &&
928d04ccbb3Scarlsonj 		    !schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) {
929d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to "
930d04ccbb3Scarlsonj 			    "set renew/rebind timers");
931d04ccbb3Scarlsonj 		} else {
932d04ccbb3Scarlsonj 			got_iana = B_TRUE;
933d04ccbb3Scarlsonj 		}
934d04ccbb3Scarlsonj 	}
935d04ccbb3Scarlsonj 
936d04ccbb3Scarlsonj 	if (!got_iana) {
937d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING,
938d04ccbb3Scarlsonj 		    "configure_v6_leases: no usable IA_NA option found");
939d04ccbb3Scarlsonj 	}
940d04ccbb3Scarlsonj 
941d04ccbb3Scarlsonj 	return (v6Done);
942d04ccbb3Scarlsonj }
943d04ccbb3Scarlsonj 
944d04ccbb3Scarlsonj /*
945d04ccbb3Scarlsonj  * configure_v4_lease(): configures the IPv4 lease on a state machine from
946d04ccbb3Scarlsonj  *			 the current DHCP ACK.  There's only one lease and LIF
947d04ccbb3Scarlsonj  *			 per state machine in IPv4.
948d04ccbb3Scarlsonj  *
949d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack)
950d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
951d04ccbb3Scarlsonj  */
952d04ccbb3Scarlsonj 
953d04ccbb3Scarlsonj static boolean_t
configure_v4_lease(dhcp_smach_t * dsmp)954d04ccbb3Scarlsonj configure_v4_lease(dhcp_smach_t *dsmp)
955d04ccbb3Scarlsonj {
956d04ccbb3Scarlsonj 	struct lifreq		lifr;
9577c478bd9Sstevel@tonic-gate 	struct sockaddr_in	*sin;
958d04ccbb3Scarlsonj 	PKT_LIST		*ack = dsmp->dsm_ack;
959d04ccbb3Scarlsonj 	dhcp_lease_t		*dlp;
960d04ccbb3Scarlsonj 	dhcp_lif_t		*lif;
961d04ccbb3Scarlsonj 	uint32_t		addrhbo;
962d04ccbb3Scarlsonj 	struct in_addr		inaddr;
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	/*
9657c478bd9Sstevel@tonic-gate 	 * if we're using DHCP, then we'll have a valid CD_SERVER_ID
9667c478bd9Sstevel@tonic-gate 	 * (we checked in dhcp_acknak()); set it now so that
967d04ccbb3Scarlsonj 	 * dsmp->dsm_server is valid in case we need to send_decline().
9687c478bd9Sstevel@tonic-gate 	 * note that we use comparisons against opts[CD_DHCP_TYPE]
9697c478bd9Sstevel@tonic-gate 	 * since we haven't set DHCP_IF_BOOTP yet (we don't do that
9707c478bd9Sstevel@tonic-gate 	 * until we're sure we want the offered address.)
9717c478bd9Sstevel@tonic-gate 	 */
9727c478bd9Sstevel@tonic-gate 
973d04ccbb3Scarlsonj 	if (ack->opts[CD_DHCP_TYPE] != NULL) {
974d04ccbb3Scarlsonj 		(void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value,
975d04ccbb3Scarlsonj 		    sizeof (inaddr));
976d04ccbb3Scarlsonj 		IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server);
9777c478bd9Sstevel@tonic-gate 	}
9787c478bd9Sstevel@tonic-gate 
979d04ccbb3Scarlsonj 	/*
980d04ccbb3Scarlsonj 	 * There needs to be exactly one lease for IPv4, and that lease
981d04ccbb3Scarlsonj 	 * controls the main LIF for the state machine.  If it doesn't exist
982d04ccbb3Scarlsonj 	 * yet, then create it now.
983d04ccbb3Scarlsonj 	 */
984d04ccbb3Scarlsonj 	if ((dlp = dsmp->dsm_leases) == NULL &&
985d04ccbb3Scarlsonj 	    (dlp = insert_lease(dsmp)) == NULL) {
986d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate "
987d04ccbb3Scarlsonj 		    "memory for lease");
988d04ccbb3Scarlsonj 		return (B_FALSE);
989d04ccbb3Scarlsonj 	}
990d04ccbb3Scarlsonj 	if (dlp->dl_nlifs == 0) {
991d04ccbb3Scarlsonj 		dlp->dl_lifs = dsmp->dsm_lif;
992d04ccbb3Scarlsonj 		dlp->dl_nlifs = 1;
993d04ccbb3Scarlsonj 
994d04ccbb3Scarlsonj 		/* The lease holds a reference on the LIF */
995d04ccbb3Scarlsonj 		hold_lif(dlp->dl_lifs);
996d04ccbb3Scarlsonj 		dlp->dl_lifs->lif_lease = dlp;
997d04ccbb3Scarlsonj 	}
998d04ccbb3Scarlsonj 
999d04ccbb3Scarlsonj 	lif = dlp->dl_lifs;
1000d04ccbb3Scarlsonj 
1001d04ccbb3Scarlsonj 	IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr);
1002d04ccbb3Scarlsonj 	addrhbo = ntohl(ack->pkt->yiaddr.s_addr);
1003d04ccbb3Scarlsonj 	if ((addrhbo & IN_CLASSA_NET) == 0 ||
1004d04ccbb3Scarlsonj 	    (addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
1005d04ccbb3Scarlsonj 	    IN_CLASSD(addrhbo)) {
1006d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERROR,
1007d04ccbb3Scarlsonj 		    "configure_v4_lease: got invalid IP address %s for %s",
1008d04ccbb3Scarlsonj 		    inet_ntoa(ack->pkt->yiaddr), lif->lif_name);
1009d04ccbb3Scarlsonj 		return (B_FALSE);
1010d04ccbb3Scarlsonj 	}
1011d04ccbb3Scarlsonj 
1012d04ccbb3Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
1013d04ccbb3Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 	/*
10167c478bd9Sstevel@tonic-gate 	 * bring the interface online.  note that there is no optimal
10177c478bd9Sstevel@tonic-gate 	 * order here: it is considered bad taste (and in > solaris 7,
10187c478bd9Sstevel@tonic-gate 	 * likely illegal) to bring an interface up before it has an
10197c478bd9Sstevel@tonic-gate 	 * ip address.  however, due to an apparent bug in sun fddi
10207c478bd9Sstevel@tonic-gate 	 * 5.0, fddi will not obtain a network routing entry unless
10217c478bd9Sstevel@tonic-gate 	 * the interface is brought up before it has an ip address.
10227c478bd9Sstevel@tonic-gate 	 * we take the lesser of the two evils; if fddi customers have
10237c478bd9Sstevel@tonic-gate 	 * problems, they can get a newer fddi distribution which
10247c478bd9Sstevel@tonic-gate 	 * fixes the problem.
10257c478bd9Sstevel@tonic-gate 	 */
10267c478bd9Sstevel@tonic-gate 
1027d04ccbb3Scarlsonj 	sin = (struct sockaddr_in *)&lifr.lifr_addr;
10287c478bd9Sstevel@tonic-gate 	sin->sin_family = AF_INET;
10297c478bd9Sstevel@tonic-gate 
1030d04ccbb3Scarlsonj 	(void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask));
10317c478bd9Sstevel@tonic-gate 	if (ack->opts[CD_SUBNETMASK] != NULL &&
1032d04ccbb3Scarlsonj 	    ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) {
10337c478bd9Sstevel@tonic-gate 
1034d04ccbb3Scarlsonj 		(void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value,
1035d04ccbb3Scarlsonj 		    sizeof (inaddr));
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 	} else {
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 		if (ack->opts[CD_SUBNETMASK] != NULL &&
1040d04ccbb3Scarlsonj 		    ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) {
1041d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
1042d04ccbb3Scarlsonj 			    "subnet mask length is %d instead of %d, ignoring",
10437c478bd9Sstevel@tonic-gate 			    ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t));
1044d04ccbb3Scarlsonj 		} else {
1045d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
1046d04ccbb3Scarlsonj 			    "netmask specified for %s, making best guess",
1047d04ccbb3Scarlsonj 			    lif->lif_name);
1048d04ccbb3Scarlsonj 		}
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 		/*
10517c478bd9Sstevel@tonic-gate 		 * no legitimate IP subnet mask specified..  use best
1052d04ccbb3Scarlsonj 		 * guess.  recall that lif_addr is in network order, so
10537c478bd9Sstevel@tonic-gate 		 * imagine it's 0x11223344: then when it is read into
10547c478bd9Sstevel@tonic-gate 		 * a register on x86, it becomes 0x44332211, so we
10557c478bd9Sstevel@tonic-gate 		 * must ntohl() it to convert it to 0x11223344 in
10567c478bd9Sstevel@tonic-gate 		 * order to use the macros in <netinet/in.h>.
10577c478bd9Sstevel@tonic-gate 		 */
10587c478bd9Sstevel@tonic-gate 
1059d04ccbb3Scarlsonj 		if (IN_CLASSA(addrhbo))
1060d04ccbb3Scarlsonj 			inaddr.s_addr = htonl(IN_CLASSA_NET);
1061d04ccbb3Scarlsonj 		else if (IN_CLASSB(addrhbo))
1062d04ccbb3Scarlsonj 			inaddr.s_addr = htonl(IN_CLASSB_NET);
10632a9459bdSsangeeta 		else if (IN_CLASSC(addrhbo))
1064d04ccbb3Scarlsonj 			inaddr.s_addr = htonl(IN_CLASSC_NET);
10652a9459bdSsangeeta 		else {
10662a9459bdSsangeeta 			/*
10672a9459bdSsangeeta 			 * Cant be Class D as that is multicast
10682a9459bdSsangeeta 			 * Must be Class E
10692a9459bdSsangeeta 			 */
10702a9459bdSsangeeta 			inaddr.s_addr =  htonl(IN_CLASSE_NET);
10712a9459bdSsangeeta 		}
1072d04ccbb3Scarlsonj 	}
1073d04ccbb3Scarlsonj 	lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr;
10747c478bd9Sstevel@tonic-gate 
1075d04ccbb3Scarlsonj 	sin->sin_addr = inaddr;
1076d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s",
1077d04ccbb3Scarlsonj 	    inet_ntoa(sin->sin_addr), lif->lif_name);
1078d04ccbb3Scarlsonj 
1079d04ccbb3Scarlsonj 	if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
1080d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask "
1081d04ccbb3Scarlsonj 		    "on %s", lif->lif_name);
1082d04ccbb3Scarlsonj 		return (B_FALSE);
10837c478bd9Sstevel@tonic-gate 	}
10847c478bd9Sstevel@tonic-gate 
1085d04ccbb3Scarlsonj 	IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr);
1086d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s",
1087d04ccbb3Scarlsonj 	    inet_ntoa(sin->sin_addr), lif->lif_name);
10887c478bd9Sstevel@tonic-gate 
1089d04ccbb3Scarlsonj 	if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
1090d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address "
1091d04ccbb3Scarlsonj 		    "on %s", lif->lif_name);
1092d04ccbb3Scarlsonj 		return (B_FALSE);
10937c478bd9Sstevel@tonic-gate 	}
10947c478bd9Sstevel@tonic-gate 
109542dc071eSJames Carlson 	if (!lif->lif_dad_wait) {
109642dc071eSJames Carlson 		lif->lif_dad_wait = _B_TRUE;
1097d04ccbb3Scarlsonj 		dsmp->dsm_lif_wait++;
109842dc071eSJames Carlson 	}
1099d04ccbb3Scarlsonj 
11007c478bd9Sstevel@tonic-gate 	if (ack->opts[CD_BROADCASTADDR] != NULL &&
1101d04ccbb3Scarlsonj 	    ack->opts[CD_BROADCASTADDR]->len == sizeof (inaddr)) {
11027c478bd9Sstevel@tonic-gate 
1103d04ccbb3Scarlsonj 		(void) memcpy(&inaddr, ack->opts[CD_BROADCASTADDR]->value,
1104d04ccbb3Scarlsonj 		    sizeof (inaddr));
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	} else {
11077c478bd9Sstevel@tonic-gate 
11087c478bd9Sstevel@tonic-gate 		if (ack->opts[CD_BROADCASTADDR] != NULL &&
1109d04ccbb3Scarlsonj 		    ack->opts[CD_BROADCASTADDR]->len != sizeof (inaddr)) {
1110d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
11117c478bd9Sstevel@tonic-gate 			    "broadcast address length is %d instead of %d, "
11127c478bd9Sstevel@tonic-gate 			    "ignoring", ack->opts[CD_BROADCASTADDR]->len,
1113d04ccbb3Scarlsonj 			    sizeof (inaddr));
1114d04ccbb3Scarlsonj 		} else {
1115d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
1116d04ccbb3Scarlsonj 			    "broadcast specified for %s, making best guess",
1117d04ccbb3Scarlsonj 			    lif->lif_name);
1118d04ccbb3Scarlsonj 		}
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate 		/*
11217c478bd9Sstevel@tonic-gate 		 * no legitimate IP broadcast specified.  compute it
11227c478bd9Sstevel@tonic-gate 		 * from the IP address and netmask.
11237c478bd9Sstevel@tonic-gate 		 */
11247c478bd9Sstevel@tonic-gate 
1125d04ccbb3Scarlsonj 		IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &inaddr);
1126d04ccbb3Scarlsonj 		inaddr.s_addr |= ~lif->lif_v6mask._S6_un._S6_u32[3];
11277c478bd9Sstevel@tonic-gate 	}
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	/*
11307c478bd9Sstevel@tonic-gate 	 * the kernel will set the broadcast address for us as part of
11317c478bd9Sstevel@tonic-gate 	 * bringing the interface up.  since experience has shown that dhcp
11327c478bd9Sstevel@tonic-gate 	 * servers sometimes provide a bogus broadcast address, we let the
11337c478bd9Sstevel@tonic-gate 	 * kernel set it so that it's guaranteed to be correct.
11347c478bd9Sstevel@tonic-gate 	 *
11357c478bd9Sstevel@tonic-gate 	 * also, note any inconsistencies and save the broadcast address the
11367c478bd9Sstevel@tonic-gate 	 * kernel set so that we can watch for changes to it.
11377c478bd9Sstevel@tonic-gate 	 */
11387c478bd9Sstevel@tonic-gate 
1139d04ccbb3Scarlsonj 	if (ioctl(v4_sock_fd, SIOCGLIFBRDADDR, &lifr) == -1) {
1140d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get broadcast "
1141d04ccbb3Scarlsonj 		    "address for %s", lif->lif_name);
1142d04ccbb3Scarlsonj 		return (B_FALSE);
11437c478bd9Sstevel@tonic-gate 	}
11447c478bd9Sstevel@tonic-gate 
1145d04ccbb3Scarlsonj 	if (inaddr.s_addr != sin->sin_addr.s_addr) {
1146d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "configure_v4_lease: incorrect broadcast "
1147d04ccbb3Scarlsonj 		    "address %s specified for %s; ignoring", inet_ntoa(inaddr),
1148d04ccbb3Scarlsonj 		    lif->lif_name);
11497c478bd9Sstevel@tonic-gate 	}
11507c478bd9Sstevel@tonic-gate 
11514ee71a50Scarlsonj 	lif->lif_broadcast = sin->sin_addr.s_addr;
1152d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO,
1153d04ccbb3Scarlsonj 	    "configure_v4_lease: using broadcast address %s on %s",
1154d04ccbb3Scarlsonj 	    inet_ntoa(inaddr), lif->lif_name);
1155d04ccbb3Scarlsonj 	return (B_TRUE);
115669bb4bb4Scarlsonj }
115769bb4bb4Scarlsonj 
115869bb4bb4Scarlsonj /*
1159d04ccbb3Scarlsonj  * save_server_id(): save off the new DHCPv6 Server ID
116069bb4bb4Scarlsonj  *
1161d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to use
1162d04ccbb3Scarlsonj  *	    PKT_LIST *: the packet with the Reply message
1163d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
116469bb4bb4Scarlsonj  */
116569bb4bb4Scarlsonj 
1166d04ccbb3Scarlsonj boolean_t
save_server_id(dhcp_smach_t * dsmp,PKT_LIST * msg)1167d04ccbb3Scarlsonj save_server_id(dhcp_smach_t *dsmp, PKT_LIST *msg)
116869bb4bb4Scarlsonj {
1169d04ccbb3Scarlsonj 	const dhcpv6_option_t *d6o;
1170d04ccbb3Scarlsonj 	uint_t olen;
11717c478bd9Sstevel@tonic-gate 
1172d04ccbb3Scarlsonj 	d6o = dhcpv6_pkt_option(msg, NULL, DHCPV6_OPT_SERVERID, &olen);
1173d04ccbb3Scarlsonj 	if (d6o == NULL)
1174d04ccbb3Scarlsonj 		return (B_FALSE);
1175d04ccbb3Scarlsonj 	olen -= sizeof (*d6o);
1176d04ccbb3Scarlsonj 	free(dsmp->dsm_serverid);
1177d04ccbb3Scarlsonj 	if ((dsmp->dsm_serverid = malloc(olen)) == NULL) {
1178d04ccbb3Scarlsonj 		return (B_FALSE);
1179d04ccbb3Scarlsonj 	} else {
1180d04ccbb3Scarlsonj 		dsmp->dsm_serveridlen = olen;
1181d04ccbb3Scarlsonj 		(void) memcpy(dsmp->dsm_serverid, d6o + 1, olen);
1182d04ccbb3Scarlsonj 		return (B_TRUE);
11837c478bd9Sstevel@tonic-gate 	}
11847c478bd9Sstevel@tonic-gate }
1185