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