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 5c2934490Sdh155122 * Common Development and Distribution License (the "License"). 6c2934490Sdh155122 * 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 * SELECTING state of the client state machine. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #include <sys/types.h> 287c478bd9Sstevel@tonic-gate #include <stdio.h> 29cfb9c9abScarlsonj #include <stdlib.h> 307c478bd9Sstevel@tonic-gate #include <strings.h> 317c478bd9Sstevel@tonic-gate #include <time.h> 327c478bd9Sstevel@tonic-gate #include <limits.h> 337c478bd9Sstevel@tonic-gate #include <netinet/in.h> 347c478bd9Sstevel@tonic-gate #include <net/route.h> 357c478bd9Sstevel@tonic-gate #include <net/if.h> 367c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h> 377c478bd9Sstevel@tonic-gate #include <netinet/udp.h> 387c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h> 397c478bd9Sstevel@tonic-gate #include <netinet/udp_var.h> 407c478bd9Sstevel@tonic-gate #include <dhcpmsg.h> 41*0a3e1f6cSVasumathi Sundaram #include <dhcp_hostconf.h> 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate #include "states.h" 447c478bd9Sstevel@tonic-gate #include "agent.h" 457c478bd9Sstevel@tonic-gate #include "util.h" 467c478bd9Sstevel@tonic-gate #include "interface.h" 477c478bd9Sstevel@tonic-gate #include "packet.h" 487c478bd9Sstevel@tonic-gate #include "defaults.h" 497c478bd9Sstevel@tonic-gate 507c478bd9Sstevel@tonic-gate static stop_func_t stop_selecting; 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate /* 53d04ccbb3Scarlsonj * dhcp_start(): starts DHCP on a state machine 547c478bd9Sstevel@tonic-gate * 557c478bd9Sstevel@tonic-gate * input: iu_tq_t *: unused 56d04ccbb3Scarlsonj * void *: the state machine on which to start DHCP 577c478bd9Sstevel@tonic-gate * output: void 587c478bd9Sstevel@tonic-gate */ 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate /* ARGSUSED */ 61cfb9c9abScarlsonj static void 627c478bd9Sstevel@tonic-gate dhcp_start(iu_tq_t *tqp, void *arg) 637c478bd9Sstevel@tonic-gate { 64d04ccbb3Scarlsonj dhcp_smach_t *dsmp = arg; 657c478bd9Sstevel@tonic-gate 66cfb9c9abScarlsonj dsmp->dsm_start_timer = -1; 67cfb9c9abScarlsonj (void) set_smach_state(dsmp, INIT); 68cfb9c9abScarlsonj if (verify_smach(dsmp)) { 69d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "starting DHCP on %s", dsmp->dsm_name); 70d04ccbb3Scarlsonj dhcp_selecting(dsmp); 717c478bd9Sstevel@tonic-gate } 72cfb9c9abScarlsonj } 73cfb9c9abScarlsonj 74cfb9c9abScarlsonj /* 75cfb9c9abScarlsonj * set_start_timer(): sets a random timer to start a DHCP state machine 76cfb9c9abScarlsonj * 77cfb9c9abScarlsonj * input: dhcp_smach_t *: the state machine on which to start DHCP 78cfb9c9abScarlsonj * output: boolean_t: B_TRUE if a timer is now running 79cfb9c9abScarlsonj */ 80cfb9c9abScarlsonj 81cfb9c9abScarlsonj boolean_t 82cfb9c9abScarlsonj set_start_timer(dhcp_smach_t *dsmp) 83cfb9c9abScarlsonj { 84cfb9c9abScarlsonj if (dsmp->dsm_start_timer != -1) 85cfb9c9abScarlsonj return (B_TRUE); 86cfb9c9abScarlsonj 87cfb9c9abScarlsonj dsmp->dsm_start_timer = iu_schedule_timer_ms(tq, 88cfb9c9abScarlsonj lrand48() % DHCP_SELECT_WAIT, dhcp_start, dsmp); 89cfb9c9abScarlsonj if (dsmp->dsm_start_timer == -1) 90cfb9c9abScarlsonj return (B_FALSE); 91cfb9c9abScarlsonj 92cfb9c9abScarlsonj hold_smach(dsmp); 93cfb9c9abScarlsonj return (B_TRUE); 94cfb9c9abScarlsonj } 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate /* 97d04ccbb3Scarlsonj * dhcp_selecting(): sends a DISCOVER and sets up reception of OFFERs for 98d04ccbb3Scarlsonj * IPv4, or sends a Solicit and sets up reception of 99d04ccbb3Scarlsonj * Advertisements for DHCPv6. 1007c478bd9Sstevel@tonic-gate * 101d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine on which to send the DISCOVER 1027c478bd9Sstevel@tonic-gate * output: void 1037c478bd9Sstevel@tonic-gate */ 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate void 106d04ccbb3Scarlsonj dhcp_selecting(dhcp_smach_t *dsmp) 1077c478bd9Sstevel@tonic-gate { 1087c478bd9Sstevel@tonic-gate dhcp_pkt_t *dpkt; 1097c478bd9Sstevel@tonic-gate const char *reqhost; 1107c478bd9Sstevel@tonic-gate char hostfile[PATH_MAX + 1]; 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate /* 113d04ccbb3Scarlsonj * We first set up to collect OFFER/Advertise packets as they arrive. 114d04ccbb3Scarlsonj * We then send out DISCOVER/Solicit probes. Then we wait a 115d04ccbb3Scarlsonj * user-tunable number of seconds before seeing if OFFERs/ 116d04ccbb3Scarlsonj * Advertisements have come in response to our DISCOVER/Solicit. If 117d04ccbb3Scarlsonj * none have come in, we continue to wait, sending out our DISCOVER/ 118d04ccbb3Scarlsonj * Solicit probes with exponential backoff. If no OFFER/Advertisement 119d04ccbb3Scarlsonj * is ever received, we will wait forever (note that since we're 120d04ccbb3Scarlsonj * event-driven though, we're still able to service other state 121d04ccbb3Scarlsonj * machines). 1227c478bd9Sstevel@tonic-gate * 123d04ccbb3Scarlsonj * Note that we do an reset_smach() here because we may be landing in 124d04ccbb3Scarlsonj * dhcp_selecting() as a result of restarting DHCP, so the state 125d04ccbb3Scarlsonj * machine may not be fresh. 1267c478bd9Sstevel@tonic-gate */ 1277c478bd9Sstevel@tonic-gate 128d04ccbb3Scarlsonj reset_smach(dsmp); 129d04ccbb3Scarlsonj if (!set_smach_state(dsmp, SELECTING)) { 130d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, 131d04ccbb3Scarlsonj "dhcp_selecting: cannot switch to SELECTING state; " 132d04ccbb3Scarlsonj "reverting to INIT on %s", dsmp->dsm_name); 133d04ccbb3Scarlsonj goto failed; 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate } 1367c478bd9Sstevel@tonic-gate 137*0a3e1f6cSVasumathi Sundaram /* Remove the stale hostconf file, if there is any */ 138*0a3e1f6cSVasumathi Sundaram (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); 139*0a3e1f6cSVasumathi Sundaram 140d04ccbb3Scarlsonj dsmp->dsm_offer_timer = iu_schedule_timer(tq, 141d04ccbb3Scarlsonj dsmp->dsm_offer_wait, dhcp_requesting, dsmp); 142d04ccbb3Scarlsonj if (dsmp->dsm_offer_timer == -1) { 143d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot schedule to read " 144d04ccbb3Scarlsonj "%s packets", dsmp->dsm_isv6 ? "Advertise" : "OFFER"); 145d04ccbb3Scarlsonj goto failed; 146d04ccbb3Scarlsonj } 147d04ccbb3Scarlsonj 148d04ccbb3Scarlsonj hold_smach(dsmp); 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate /* 151d04ccbb3Scarlsonj * Assemble and send the DHCPDISCOVER or Solicit message. 152d04ccbb3Scarlsonj * 153d04ccbb3Scarlsonj * If this fails, we'll wait for the select timer to go off 154d04ccbb3Scarlsonj * before trying again. 1557c478bd9Sstevel@tonic-gate */ 156d04ccbb3Scarlsonj if (dsmp->dsm_isv6) { 157d04ccbb3Scarlsonj dhcpv6_ia_na_t d6in; 1587c478bd9Sstevel@tonic-gate 159d04ccbb3Scarlsonj if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_SOLICIT)) == NULL) { 160d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up " 161d04ccbb3Scarlsonj "Solicit packet"); 162d04ccbb3Scarlsonj return; 163d04ccbb3Scarlsonj } 1647c478bd9Sstevel@tonic-gate 165d04ccbb3Scarlsonj /* Add an IA_NA option for our controlling LIF */ 166d04ccbb3Scarlsonj d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid); 167d04ccbb3Scarlsonj d6in.d6in_t1 = htonl(0); 168d04ccbb3Scarlsonj d6in.d6in_t2 = htonl(0); 169d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, 170d04ccbb3Scarlsonj (dhcpv6_option_t *)&d6in + 1, 171d04ccbb3Scarlsonj sizeof (d6in) - sizeof (dhcpv6_option_t)); 1727c478bd9Sstevel@tonic-gate 173d04ccbb3Scarlsonj /* Option Request option for desired information */ 174d04ccbb3Scarlsonj (void) add_pkt_prl(dpkt, dsmp); 1757c478bd9Sstevel@tonic-gate 176d04ccbb3Scarlsonj /* Enable Rapid-Commit */ 177d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_RAPID_COMMIT, NULL, 0); 178d04ccbb3Scarlsonj 179d04ccbb3Scarlsonj /* xxx add Reconfigure Accept */ 180d04ccbb3Scarlsonj 181d04ccbb3Scarlsonj (void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers, 182d04ccbb3Scarlsonj stop_selecting, DHCPV6_SOL_TIMEOUT, DHCPV6_SOL_MAX_RT); 183d04ccbb3Scarlsonj } else { 184d04ccbb3Scarlsonj if ((dpkt = init_pkt(dsmp, DISCOVER)) == NULL) { 185d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up " 186d04ccbb3Scarlsonj "DISCOVER packet"); 187d04ccbb3Scarlsonj return; 188d04ccbb3Scarlsonj } 189d04ccbb3Scarlsonj 190d04ccbb3Scarlsonj /* 191d04ccbb3Scarlsonj * The max DHCP message size option is set to the interface 192d04ccbb3Scarlsonj * MTU, minus the size of the UDP and IP headers. 193d04ccbb3Scarlsonj */ 194d04ccbb3Scarlsonj (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, 195d04ccbb3Scarlsonj htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr))); 196d04ccbb3Scarlsonj (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM)); 197d04ccbb3Scarlsonj 198f4b3ec61Sdh155122 if (class_id_len != 0) { 199f4b3ec61Sdh155122 (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, 200f4b3ec61Sdh155122 class_id_len); 201f4b3ec61Sdh155122 } 202d04ccbb3Scarlsonj (void) add_pkt_prl(dpkt, dsmp); 203d04ccbb3Scarlsonj 204d04ccbb3Scarlsonj if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, 205d04ccbb3Scarlsonj DF_REQUEST_HOSTNAME)) { 206d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, 207d04ccbb3Scarlsonj "dhcp_selecting: DF_REQUEST_HOSTNAME"); 208d04ccbb3Scarlsonj (void) snprintf(hostfile, sizeof (hostfile), 209d04ccbb3Scarlsonj "/etc/hostname.%s", dsmp->dsm_name); 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate if ((reqhost = iffile_to_hostname(hostfile)) != NULL) { 212d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", 213d04ccbb3Scarlsonj reqhost); 214d04ccbb3Scarlsonj dsmp->dsm_reqhost = strdup(reqhost); 215d04ccbb3Scarlsonj if (dsmp->dsm_reqhost != NULL) 216d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_HOSTNAME, 217d04ccbb3Scarlsonj dsmp->dsm_reqhost, 218d04ccbb3Scarlsonj strlen(dsmp->dsm_reqhost)); 2197c478bd9Sstevel@tonic-gate else 220d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, 221d04ccbb3Scarlsonj "dhcp_selecting: cannot allocate " 222d04ccbb3Scarlsonj "memory for host name option"); 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate } 225d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 2267c478bd9Sstevel@tonic-gate 227d04ccbb3Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), 228d04ccbb3Scarlsonj stop_selecting); 229d04ccbb3Scarlsonj } 230d04ccbb3Scarlsonj return; 231d04ccbb3Scarlsonj 232d04ccbb3Scarlsonj failed: 233d04ccbb3Scarlsonj (void) set_smach_state(dsmp, INIT); 234d04ccbb3Scarlsonj dsmp->dsm_dflags |= DHCP_IF_FAILED; 235d04ccbb3Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); 2367c478bd9Sstevel@tonic-gate } 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate /* 239d04ccbb3Scarlsonj * stop_selecting(): decides when to stop retransmitting DISCOVERs -- only when 240d04ccbb3Scarlsonj * abandoning the state machine. For DHCPv6, this timer may 241d04ccbb3Scarlsonj * go off before the offer wait timer. If so, then this is a 242d04ccbb3Scarlsonj * good time to check for valid Advertisements, so cancel the 243d04ccbb3Scarlsonj * timer and go check. 2447c478bd9Sstevel@tonic-gate * 245d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine DISCOVERs are being sent on 2467c478bd9Sstevel@tonic-gate * unsigned int: the number of DISCOVERs sent so far 2477c478bd9Sstevel@tonic-gate * output: boolean_t: B_TRUE if retransmissions should stop 2487c478bd9Sstevel@tonic-gate */ 2497c478bd9Sstevel@tonic-gate 250d04ccbb3Scarlsonj /* ARGSUSED1 */ 2517c478bd9Sstevel@tonic-gate static boolean_t 252d04ccbb3Scarlsonj stop_selecting(dhcp_smach_t *dsmp, unsigned int n_discovers) 2537c478bd9Sstevel@tonic-gate { 254d04ccbb3Scarlsonj /* 255d04ccbb3Scarlsonj * If we're using v4 and the underlying LIF we're trying to configure 256d04ccbb3Scarlsonj * has been touched by the user, then bail out. 257d04ccbb3Scarlsonj */ 258d04ccbb3Scarlsonj if (!dsmp->dsm_isv6 && !verify_lif(dsmp->dsm_lif)) { 259d04ccbb3Scarlsonj finished_smach(dsmp, DHCP_IPC_E_UNKIF); 260d04ccbb3Scarlsonj return (B_TRUE); 261d04ccbb3Scarlsonj } 262d04ccbb3Scarlsonj 263d04ccbb3Scarlsonj if (dsmp->dsm_recv_pkt_list != NULL) { 264d04ccbb3Scarlsonj dhcp_requesting(NULL, dsmp); 265d04ccbb3Scarlsonj if (dsmp->dsm_state != SELECTING) 266d04ccbb3Scarlsonj return (B_TRUE); 267d04ccbb3Scarlsonj } 2687c478bd9Sstevel@tonic-gate return (B_FALSE); 2697c478bd9Sstevel@tonic-gate } 270