10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52157Sdh155122 * Common Development and Distribution License (the "License"). 62157Sdh155122 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*3431Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 250Sstevel@tonic-gate * SELECTING state of the client state machine. 260Sstevel@tonic-gate */ 270Sstevel@tonic-gate 280Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <sys/types.h> 310Sstevel@tonic-gate #include <stdio.h> 320Sstevel@tonic-gate #include <strings.h> 330Sstevel@tonic-gate #include <time.h> 340Sstevel@tonic-gate #include <limits.h> 350Sstevel@tonic-gate #include <netinet/in.h> 360Sstevel@tonic-gate #include <net/route.h> 370Sstevel@tonic-gate #include <net/if.h> 380Sstevel@tonic-gate #include <netinet/dhcp.h> 390Sstevel@tonic-gate #include <netinet/udp.h> 400Sstevel@tonic-gate #include <netinet/ip_var.h> 410Sstevel@tonic-gate #include <netinet/udp_var.h> 420Sstevel@tonic-gate #include <dhcpmsg.h> 430Sstevel@tonic-gate 440Sstevel@tonic-gate #include "states.h" 450Sstevel@tonic-gate #include "agent.h" 460Sstevel@tonic-gate #include "util.h" 470Sstevel@tonic-gate #include "interface.h" 480Sstevel@tonic-gate #include "packet.h" 490Sstevel@tonic-gate #include "defaults.h" 500Sstevel@tonic-gate 510Sstevel@tonic-gate static stop_func_t stop_selecting; 520Sstevel@tonic-gate 530Sstevel@tonic-gate /* 54*3431Scarlsonj * dhcp_start(): starts DHCP on a state machine 550Sstevel@tonic-gate * 560Sstevel@tonic-gate * input: iu_tq_t *: unused 57*3431Scarlsonj * void *: the state machine on which to start DHCP 580Sstevel@tonic-gate * output: void 590Sstevel@tonic-gate */ 600Sstevel@tonic-gate 610Sstevel@tonic-gate /* ARGSUSED */ 620Sstevel@tonic-gate void 630Sstevel@tonic-gate dhcp_start(iu_tq_t *tqp, void *arg) 640Sstevel@tonic-gate { 65*3431Scarlsonj dhcp_smach_t *dsmp = arg; 660Sstevel@tonic-gate 67*3431Scarlsonj release_smach(dsmp); 680Sstevel@tonic-gate 69*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "starting DHCP on %s", dsmp->dsm_name); 70*3431Scarlsonj dhcp_selecting(dsmp); 710Sstevel@tonic-gate } 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* 74*3431Scarlsonj * dhcp_selecting(): sends a DISCOVER and sets up reception of OFFERs for 75*3431Scarlsonj * IPv4, or sends a Solicit and sets up reception of 76*3431Scarlsonj * Advertisements for DHCPv6. 770Sstevel@tonic-gate * 78*3431Scarlsonj * input: dhcp_smach_t *: the state machine on which to send the DISCOVER 790Sstevel@tonic-gate * output: void 800Sstevel@tonic-gate */ 810Sstevel@tonic-gate 820Sstevel@tonic-gate void 83*3431Scarlsonj dhcp_selecting(dhcp_smach_t *dsmp) 840Sstevel@tonic-gate { 850Sstevel@tonic-gate dhcp_pkt_t *dpkt; 860Sstevel@tonic-gate const char *reqhost; 870Sstevel@tonic-gate char hostfile[PATH_MAX + 1]; 880Sstevel@tonic-gate 890Sstevel@tonic-gate /* 90*3431Scarlsonj * We first set up to collect OFFER/Advertise packets as they arrive. 91*3431Scarlsonj * We then send out DISCOVER/Solicit probes. Then we wait a 92*3431Scarlsonj * user-tunable number of seconds before seeing if OFFERs/ 93*3431Scarlsonj * Advertisements have come in response to our DISCOVER/Solicit. If 94*3431Scarlsonj * none have come in, we continue to wait, sending out our DISCOVER/ 95*3431Scarlsonj * Solicit probes with exponential backoff. If no OFFER/Advertisement 96*3431Scarlsonj * is ever received, we will wait forever (note that since we're 97*3431Scarlsonj * event-driven though, we're still able to service other state 98*3431Scarlsonj * machines). 990Sstevel@tonic-gate * 100*3431Scarlsonj * Note that we do an reset_smach() here because we may be landing in 101*3431Scarlsonj * dhcp_selecting() as a result of restarting DHCP, so the state 102*3431Scarlsonj * machine may not be fresh. 1030Sstevel@tonic-gate */ 1040Sstevel@tonic-gate 105*3431Scarlsonj reset_smach(dsmp); 106*3431Scarlsonj if (!set_smach_state(dsmp, SELECTING)) { 107*3431Scarlsonj dhcpmsg(MSG_ERROR, 108*3431Scarlsonj "dhcp_selecting: cannot switch to SELECTING state; " 109*3431Scarlsonj "reverting to INIT on %s", dsmp->dsm_name); 110*3431Scarlsonj goto failed; 1110Sstevel@tonic-gate 112*3431Scarlsonj } 1130Sstevel@tonic-gate 114*3431Scarlsonj dsmp->dsm_offer_timer = iu_schedule_timer(tq, 115*3431Scarlsonj dsmp->dsm_offer_wait, dhcp_requesting, dsmp); 116*3431Scarlsonj if (dsmp->dsm_offer_timer == -1) { 117*3431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot schedule to read " 118*3431Scarlsonj "%s packets", dsmp->dsm_isv6 ? "Advertise" : "OFFER"); 119*3431Scarlsonj goto failed; 120*3431Scarlsonj } 1210Sstevel@tonic-gate 122*3431Scarlsonj hold_smach(dsmp); 1230Sstevel@tonic-gate 124*3431Scarlsonj /* 125*3431Scarlsonj * Assemble and send the DHCPDISCOVER or Solicit message. 126*3431Scarlsonj * 127*3431Scarlsonj * If this fails, we'll wait for the select timer to go off 128*3431Scarlsonj * before trying again. 129*3431Scarlsonj */ 130*3431Scarlsonj if (dsmp->dsm_isv6) { 131*3431Scarlsonj dhcpv6_ia_na_t d6in; 1320Sstevel@tonic-gate 133*3431Scarlsonj if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_SOLICIT)) == NULL) { 134*3431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up " 135*3431Scarlsonj "Solicit packet"); 136*3431Scarlsonj return; 1370Sstevel@tonic-gate } 1380Sstevel@tonic-gate 139*3431Scarlsonj /* Add an IA_NA option for our controlling LIF */ 140*3431Scarlsonj d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid); 141*3431Scarlsonj d6in.d6in_t1 = htonl(0); 142*3431Scarlsonj d6in.d6in_t2 = htonl(0); 143*3431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, 144*3431Scarlsonj (dhcpv6_option_t *)&d6in + 1, 145*3431Scarlsonj sizeof (d6in) - sizeof (dhcpv6_option_t)); 146*3431Scarlsonj 147*3431Scarlsonj /* Option Request option for desired information */ 148*3431Scarlsonj (void) add_pkt_prl(dpkt, dsmp); 149*3431Scarlsonj 150*3431Scarlsonj /* Enable Rapid-Commit */ 151*3431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_RAPID_COMMIT, NULL, 0); 152*3431Scarlsonj 153*3431Scarlsonj /* xxx add Reconfigure Accept */ 1540Sstevel@tonic-gate 155*3431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers, 156*3431Scarlsonj stop_selecting, DHCPV6_SOL_TIMEOUT, DHCPV6_SOL_MAX_RT); 157*3431Scarlsonj } else { 158*3431Scarlsonj if ((dpkt = init_pkt(dsmp, DISCOVER)) == NULL) { 159*3431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up " 160*3431Scarlsonj "DISCOVER packet"); 161*3431Scarlsonj return; 162*3431Scarlsonj } 1630Sstevel@tonic-gate 164*3431Scarlsonj /* 165*3431Scarlsonj * The max DHCP message size option is set to the interface 166*3431Scarlsonj * MTU, minus the size of the UDP and IP headers. 167*3431Scarlsonj */ 168*3431Scarlsonj (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, 169*3431Scarlsonj htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr))); 170*3431Scarlsonj (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM)); 1710Sstevel@tonic-gate 172*3431Scarlsonj (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); 173*3431Scarlsonj (void) add_pkt_prl(dpkt, dsmp); 1740Sstevel@tonic-gate 175*3431Scarlsonj if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, 176*3431Scarlsonj DF_REQUEST_HOSTNAME)) { 177*3431Scarlsonj dhcpmsg(MSG_DEBUG, 178*3431Scarlsonj "dhcp_selecting: DF_REQUEST_HOSTNAME"); 179*3431Scarlsonj (void) snprintf(hostfile, sizeof (hostfile), 180*3431Scarlsonj "/etc/hostname.%s", dsmp->dsm_name); 1810Sstevel@tonic-gate 182*3431Scarlsonj if ((reqhost = iffile_to_hostname(hostfile)) != NULL) { 183*3431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", 184*3431Scarlsonj reqhost); 185*3431Scarlsonj dsmp->dsm_reqhost = strdup(reqhost); 186*3431Scarlsonj if (dsmp->dsm_reqhost != NULL) 187*3431Scarlsonj (void) add_pkt_opt(dpkt, CD_HOSTNAME, 188*3431Scarlsonj dsmp->dsm_reqhost, 189*3431Scarlsonj strlen(dsmp->dsm_reqhost)); 190*3431Scarlsonj else 191*3431Scarlsonj dhcpmsg(MSG_WARNING, 192*3431Scarlsonj "dhcp_selecting: cannot allocate " 193*3431Scarlsonj "memory for host name option"); 194*3431Scarlsonj } 1950Sstevel@tonic-gate } 196*3431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 197*3431Scarlsonj 198*3431Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), 199*3431Scarlsonj stop_selecting); 2000Sstevel@tonic-gate } 201*3431Scarlsonj return; 2020Sstevel@tonic-gate 203*3431Scarlsonj failed: 204*3431Scarlsonj (void) set_smach_state(dsmp, INIT); 205*3431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_FAILED; 206*3431Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); 2070Sstevel@tonic-gate } 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate /* 210*3431Scarlsonj * dhcp_collect_dlpi(): collects incoming OFFERs, ACKs, and NAKs via DLPI. 2110Sstevel@tonic-gate * 2120Sstevel@tonic-gate * input: iu_eh_t *: unused 213*3431Scarlsonj * int: the file descriptor the mesage arrived on 2140Sstevel@tonic-gate * short: unused 2150Sstevel@tonic-gate * iu_event_id_t: the id of this event callback with the handler 216*3431Scarlsonj * void *: the physical interface that received the message 2170Sstevel@tonic-gate * output: void 2180Sstevel@tonic-gate */ 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate /* ARGSUSED */ 221*3431Scarlsonj void 222*3431Scarlsonj dhcp_collect_dlpi(iu_eh_t *eh, int fd, short events, iu_event_id_t id, 2230Sstevel@tonic-gate void *arg) 2240Sstevel@tonic-gate { 225*3431Scarlsonj dhcp_pif_t *pif = arg; 226*3431Scarlsonj PKT_LIST *plp; 227*3431Scarlsonj uchar_t recv_type; 228*3431Scarlsonj const char *pname; 229*3431Scarlsonj dhcp_smach_t *dsmp; 230*3431Scarlsonj uint_t xid; 2310Sstevel@tonic-gate 232*3431Scarlsonj if ((plp = recv_pkt(fd, pif->pif_max, B_FALSE, B_TRUE)) == NULL) 2330Sstevel@tonic-gate return; 234*3431Scarlsonj 235*3431Scarlsonj recv_type = pkt_recv_type(plp); 236*3431Scarlsonj pname = pkt_type_to_string(recv_type, B_FALSE); 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate /* 2390Sstevel@tonic-gate * DHCP_PUNTYPED messages are BOOTP server responses. 2400Sstevel@tonic-gate */ 241*3431Scarlsonj if (!pkt_v4_match(recv_type, 242*3431Scarlsonj DHCP_PACK | DHCP_PNAK | DHCP_POFFER | DHCP_PUNTYPED)) { 243*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: ignored %s packet " 244*3431Scarlsonj "received via DLPI on %s", pname, pif->pif_name); 245*3431Scarlsonj free_pkt_entry(plp); 246*3431Scarlsonj return; 247*3431Scarlsonj } 2480Sstevel@tonic-gate 249*3431Scarlsonj /* 250*3431Scarlsonj * Loop through the state machines that match on XID to find one that's 251*3431Scarlsonj * interested in this offer. If there are none, then discard. 252*3431Scarlsonj */ 253*3431Scarlsonj xid = pkt_get_xid(plp->pkt, B_FALSE); 254*3431Scarlsonj for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL; 255*3431Scarlsonj dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) { 256*3431Scarlsonj 257*3431Scarlsonj /* 258*3431Scarlsonj * Find state machine on correct interface. 259*3431Scarlsonj */ 260*3431Scarlsonj if (dsmp->dsm_lif->lif_pif == pif) 261*3431Scarlsonj break; 262*3431Scarlsonj } 263*3431Scarlsonj 264*3431Scarlsonj if (dsmp == NULL) { 265*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: no matching state " 266*3431Scarlsonj "machine for %s packet XID %#x received via DLPI on %s", 267*3431Scarlsonj pname, xid, pif->pif_name); 268*3431Scarlsonj free_pkt_entry(plp); 269*3431Scarlsonj return; 270*3431Scarlsonj } 271*3431Scarlsonj 272*3431Scarlsonj /* 273*3431Scarlsonj * Ignore state machines that aren't looking for DLPI messages. 274*3431Scarlsonj */ 275*3431Scarlsonj if (!dsmp->dsm_using_dlpi) { 276*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: ignore state " 277*3431Scarlsonj "machine for %s packet XID %#x received via DLPI on %s", 278*3431Scarlsonj pname, xid, pif->pif_name); 279*3431Scarlsonj free_pkt_entry(plp); 280*3431Scarlsonj return; 281*3431Scarlsonj } 282*3431Scarlsonj 283*3431Scarlsonj if (pkt_v4_match(recv_type, DHCP_PACK | DHCP_PNAK)) { 284*3431Scarlsonj if (!dhcp_bound(dsmp, plp)) { 285*3431Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_collect_dlpi: dhcp_bound " 286*3431Scarlsonj "failed for %s", dsmp->dsm_name); 287*3431Scarlsonj dhcp_restart(dsmp); 288*3431Scarlsonj return; 289*3431Scarlsonj } 290*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: %s on %s", 291*3431Scarlsonj pname, dsmp->dsm_name); 292*3431Scarlsonj } else { 293*3431Scarlsonj pkt_smach_enqueue(dsmp, plp); 294*3431Scarlsonj } 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate /* 298*3431Scarlsonj * stop_selecting(): decides when to stop retransmitting DISCOVERs -- only when 299*3431Scarlsonj * abandoning the state machine. For DHCPv6, this timer may 300*3431Scarlsonj * go off before the offer wait timer. If so, then this is a 301*3431Scarlsonj * good time to check for valid Advertisements, so cancel the 302*3431Scarlsonj * timer and go check. 3030Sstevel@tonic-gate * 304*3431Scarlsonj * input: dhcp_smach_t *: the state machine DISCOVERs are being sent on 3050Sstevel@tonic-gate * unsigned int: the number of DISCOVERs sent so far 3060Sstevel@tonic-gate * output: boolean_t: B_TRUE if retransmissions should stop 3070Sstevel@tonic-gate */ 3080Sstevel@tonic-gate 309*3431Scarlsonj /* ARGSUSED1 */ 3100Sstevel@tonic-gate static boolean_t 311*3431Scarlsonj stop_selecting(dhcp_smach_t *dsmp, unsigned int n_discovers) 3120Sstevel@tonic-gate { 313*3431Scarlsonj /* 314*3431Scarlsonj * If we're using v4 and the underlying LIF we're trying to configure 315*3431Scarlsonj * has been touched by the user, then bail out. 316*3431Scarlsonj */ 317*3431Scarlsonj if (!dsmp->dsm_isv6 && !verify_lif(dsmp->dsm_lif)) { 318*3431Scarlsonj finished_smach(dsmp, DHCP_IPC_E_UNKIF); 319*3431Scarlsonj return (B_TRUE); 320*3431Scarlsonj } 321*3431Scarlsonj 322*3431Scarlsonj if (dsmp->dsm_recv_pkt_list != NULL) { 323*3431Scarlsonj dhcp_requesting(NULL, dsmp); 324*3431Scarlsonj if (dsmp->dsm_state != SELECTING) 325*3431Scarlsonj return (B_TRUE); 326*3431Scarlsonj } 3270Sstevel@tonic-gate return (B_FALSE); 3280Sstevel@tonic-gate } 329