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 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 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 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 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 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 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 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 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 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