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 /* 223431Scarlsonj * 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 /* 543431Scarlsonj * dhcp_start(): starts DHCP on a state machine 550Sstevel@tonic-gate * 560Sstevel@tonic-gate * input: iu_tq_t *: unused 573431Scarlsonj * 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 { 653431Scarlsonj dhcp_smach_t *dsmp = arg; 660Sstevel@tonic-gate 673431Scarlsonj release_smach(dsmp); 680Sstevel@tonic-gate 693431Scarlsonj dhcpmsg(MSG_VERBOSE, "starting DHCP on %s", dsmp->dsm_name); 703431Scarlsonj dhcp_selecting(dsmp); 710Sstevel@tonic-gate } 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* 743431Scarlsonj * dhcp_selecting(): sends a DISCOVER and sets up reception of OFFERs for 753431Scarlsonj * IPv4, or sends a Solicit and sets up reception of 763431Scarlsonj * Advertisements for DHCPv6. 770Sstevel@tonic-gate * 783431Scarlsonj * 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 833431Scarlsonj 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 /* 903431Scarlsonj * We first set up to collect OFFER/Advertise packets as they arrive. 913431Scarlsonj * We then send out DISCOVER/Solicit probes. Then we wait a 923431Scarlsonj * user-tunable number of seconds before seeing if OFFERs/ 933431Scarlsonj * Advertisements have come in response to our DISCOVER/Solicit. If 943431Scarlsonj * none have come in, we continue to wait, sending out our DISCOVER/ 953431Scarlsonj * Solicit probes with exponential backoff. If no OFFER/Advertisement 963431Scarlsonj * is ever received, we will wait forever (note that since we're 973431Scarlsonj * event-driven though, we're still able to service other state 983431Scarlsonj * machines). 990Sstevel@tonic-gate * 1003431Scarlsonj * Note that we do an reset_smach() here because we may be landing in 1013431Scarlsonj * dhcp_selecting() as a result of restarting DHCP, so the state 1023431Scarlsonj * machine may not be fresh. 1030Sstevel@tonic-gate */ 1040Sstevel@tonic-gate 1053431Scarlsonj reset_smach(dsmp); 1063431Scarlsonj if (!set_smach_state(dsmp, SELECTING)) { 1073431Scarlsonj dhcpmsg(MSG_ERROR, 1083431Scarlsonj "dhcp_selecting: cannot switch to SELECTING state; " 1093431Scarlsonj "reverting to INIT on %s", dsmp->dsm_name); 1103431Scarlsonj goto failed; 1110Sstevel@tonic-gate 1123431Scarlsonj } 1130Sstevel@tonic-gate 1143431Scarlsonj dsmp->dsm_offer_timer = iu_schedule_timer(tq, 1153431Scarlsonj dsmp->dsm_offer_wait, dhcp_requesting, dsmp); 1163431Scarlsonj if (dsmp->dsm_offer_timer == -1) { 1173431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot schedule to read " 1183431Scarlsonj "%s packets", dsmp->dsm_isv6 ? "Advertise" : "OFFER"); 1193431Scarlsonj goto failed; 1203431Scarlsonj } 1210Sstevel@tonic-gate 1223431Scarlsonj hold_smach(dsmp); 1230Sstevel@tonic-gate 1243431Scarlsonj /* 1253431Scarlsonj * Assemble and send the DHCPDISCOVER or Solicit message. 1263431Scarlsonj * 1273431Scarlsonj * If this fails, we'll wait for the select timer to go off 1283431Scarlsonj * before trying again. 1293431Scarlsonj */ 1303431Scarlsonj if (dsmp->dsm_isv6) { 1313431Scarlsonj dhcpv6_ia_na_t d6in; 1320Sstevel@tonic-gate 1333431Scarlsonj if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_SOLICIT)) == NULL) { 1343431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up " 1353431Scarlsonj "Solicit packet"); 1363431Scarlsonj return; 1370Sstevel@tonic-gate } 1380Sstevel@tonic-gate 1393431Scarlsonj /* Add an IA_NA option for our controlling LIF */ 1403431Scarlsonj d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid); 1413431Scarlsonj d6in.d6in_t1 = htonl(0); 1423431Scarlsonj d6in.d6in_t2 = htonl(0); 1433431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, 1443431Scarlsonj (dhcpv6_option_t *)&d6in + 1, 1453431Scarlsonj sizeof (d6in) - sizeof (dhcpv6_option_t)); 1463431Scarlsonj 1473431Scarlsonj /* Option Request option for desired information */ 1483431Scarlsonj (void) add_pkt_prl(dpkt, dsmp); 1493431Scarlsonj 1503431Scarlsonj /* Enable Rapid-Commit */ 1513431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_RAPID_COMMIT, NULL, 0); 1523431Scarlsonj 1533431Scarlsonj /* xxx add Reconfigure Accept */ 1540Sstevel@tonic-gate 1553431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers, 1563431Scarlsonj stop_selecting, DHCPV6_SOL_TIMEOUT, DHCPV6_SOL_MAX_RT); 1573431Scarlsonj } else { 1583431Scarlsonj if ((dpkt = init_pkt(dsmp, DISCOVER)) == NULL) { 1593431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up " 1603431Scarlsonj "DISCOVER packet"); 1613431Scarlsonj return; 1623431Scarlsonj } 1630Sstevel@tonic-gate 1643431Scarlsonj /* 1653431Scarlsonj * The max DHCP message size option is set to the interface 1663431Scarlsonj * MTU, minus the size of the UDP and IP headers. 1673431Scarlsonj */ 1683431Scarlsonj (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, 1693431Scarlsonj htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr))); 1703431Scarlsonj (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM)); 1710Sstevel@tonic-gate 172*3448Sdh155122 if (class_id_len != 0) { 173*3448Sdh155122 (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, 174*3448Sdh155122 class_id_len); 175*3448Sdh155122 } 1763431Scarlsonj (void) add_pkt_prl(dpkt, dsmp); 1770Sstevel@tonic-gate 1783431Scarlsonj if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, 1793431Scarlsonj DF_REQUEST_HOSTNAME)) { 1803431Scarlsonj dhcpmsg(MSG_DEBUG, 1813431Scarlsonj "dhcp_selecting: DF_REQUEST_HOSTNAME"); 1823431Scarlsonj (void) snprintf(hostfile, sizeof (hostfile), 1833431Scarlsonj "/etc/hostname.%s", dsmp->dsm_name); 1840Sstevel@tonic-gate 1853431Scarlsonj if ((reqhost = iffile_to_hostname(hostfile)) != NULL) { 1863431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", 1873431Scarlsonj reqhost); 1883431Scarlsonj dsmp->dsm_reqhost = strdup(reqhost); 1893431Scarlsonj if (dsmp->dsm_reqhost != NULL) 1903431Scarlsonj (void) add_pkt_opt(dpkt, CD_HOSTNAME, 1913431Scarlsonj dsmp->dsm_reqhost, 1923431Scarlsonj strlen(dsmp->dsm_reqhost)); 1933431Scarlsonj else 1943431Scarlsonj dhcpmsg(MSG_WARNING, 1953431Scarlsonj "dhcp_selecting: cannot allocate " 1963431Scarlsonj "memory for host name option"); 1973431Scarlsonj } 1980Sstevel@tonic-gate } 1993431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 2003431Scarlsonj 2013431Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), 2023431Scarlsonj stop_selecting); 2030Sstevel@tonic-gate } 2043431Scarlsonj return; 2050Sstevel@tonic-gate 2063431Scarlsonj failed: 2073431Scarlsonj (void) set_smach_state(dsmp, INIT); 2083431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_FAILED; 2093431Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); 2100Sstevel@tonic-gate } 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate /* 2133431Scarlsonj * dhcp_collect_dlpi(): collects incoming OFFERs, ACKs, and NAKs via DLPI. 2140Sstevel@tonic-gate * 2150Sstevel@tonic-gate * input: iu_eh_t *: unused 2163431Scarlsonj * int: the file descriptor the mesage arrived on 2170Sstevel@tonic-gate * short: unused 2180Sstevel@tonic-gate * iu_event_id_t: the id of this event callback with the handler 2193431Scarlsonj * void *: the physical interface that received the message 2200Sstevel@tonic-gate * output: void 2210Sstevel@tonic-gate */ 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate /* ARGSUSED */ 2243431Scarlsonj void 2253431Scarlsonj dhcp_collect_dlpi(iu_eh_t *eh, int fd, short events, iu_event_id_t id, 2260Sstevel@tonic-gate void *arg) 2270Sstevel@tonic-gate { 2283431Scarlsonj dhcp_pif_t *pif = arg; 2293431Scarlsonj PKT_LIST *plp; 2303431Scarlsonj uchar_t recv_type; 2313431Scarlsonj const char *pname; 2323431Scarlsonj dhcp_smach_t *dsmp; 2333431Scarlsonj uint_t xid; 2340Sstevel@tonic-gate 2353431Scarlsonj if ((plp = recv_pkt(fd, pif->pif_max, B_FALSE, B_TRUE)) == NULL) 2360Sstevel@tonic-gate return; 2373431Scarlsonj 2383431Scarlsonj recv_type = pkt_recv_type(plp); 2393431Scarlsonj pname = pkt_type_to_string(recv_type, B_FALSE); 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate /* 2420Sstevel@tonic-gate * DHCP_PUNTYPED messages are BOOTP server responses. 2430Sstevel@tonic-gate */ 2443431Scarlsonj if (!pkt_v4_match(recv_type, 2453431Scarlsonj DHCP_PACK | DHCP_PNAK | DHCP_POFFER | DHCP_PUNTYPED)) { 2463431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: ignored %s packet " 2473431Scarlsonj "received via DLPI on %s", pname, pif->pif_name); 2483431Scarlsonj free_pkt_entry(plp); 2493431Scarlsonj return; 2503431Scarlsonj } 2510Sstevel@tonic-gate 2523431Scarlsonj /* 2533431Scarlsonj * Loop through the state machines that match on XID to find one that's 2543431Scarlsonj * interested in this offer. If there are none, then discard. 2553431Scarlsonj */ 2563431Scarlsonj xid = pkt_get_xid(plp->pkt, B_FALSE); 2573431Scarlsonj for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL; 2583431Scarlsonj dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) { 2593431Scarlsonj 2603431Scarlsonj /* 2613431Scarlsonj * Find state machine on correct interface. 2623431Scarlsonj */ 2633431Scarlsonj if (dsmp->dsm_lif->lif_pif == pif) 2643431Scarlsonj break; 2653431Scarlsonj } 2663431Scarlsonj 2673431Scarlsonj if (dsmp == NULL) { 2683431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: no matching state " 2693431Scarlsonj "machine for %s packet XID %#x received via DLPI on %s", 2703431Scarlsonj pname, xid, pif->pif_name); 2713431Scarlsonj free_pkt_entry(plp); 2723431Scarlsonj return; 2733431Scarlsonj } 2743431Scarlsonj 2753431Scarlsonj /* 2763431Scarlsonj * Ignore state machines that aren't looking for DLPI messages. 2773431Scarlsonj */ 2783431Scarlsonj if (!dsmp->dsm_using_dlpi) { 2793431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: ignore state " 2803431Scarlsonj "machine for %s packet XID %#x received via DLPI on %s", 2813431Scarlsonj pname, xid, pif->pif_name); 2823431Scarlsonj free_pkt_entry(plp); 2833431Scarlsonj return; 2843431Scarlsonj } 2853431Scarlsonj 286*3448Sdh155122 if (pkt_v4_match(recv_type, DHCP_PACK)) { 2873431Scarlsonj if (!dhcp_bound(dsmp, plp)) { 2883431Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_collect_dlpi: dhcp_bound " 2893431Scarlsonj "failed for %s", dsmp->dsm_name); 2903431Scarlsonj dhcp_restart(dsmp); 2913431Scarlsonj return; 2923431Scarlsonj } 2933431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: %s on %s", 2943431Scarlsonj pname, dsmp->dsm_name); 295*3448Sdh155122 } else if (pkt_v4_match(recv_type, DHCP_PNAK)) { 296*3448Sdh155122 free_pkt_entry(plp); 297*3448Sdh155122 dhcp_restart(dsmp); 2983431Scarlsonj } else { 2993431Scarlsonj pkt_smach_enqueue(dsmp, plp); 3003431Scarlsonj } 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate /* 3043431Scarlsonj * stop_selecting(): decides when to stop retransmitting DISCOVERs -- only when 3053431Scarlsonj * abandoning the state machine. For DHCPv6, this timer may 3063431Scarlsonj * go off before the offer wait timer. If so, then this is a 3073431Scarlsonj * good time to check for valid Advertisements, so cancel the 3083431Scarlsonj * timer and go check. 3090Sstevel@tonic-gate * 3103431Scarlsonj * input: dhcp_smach_t *: the state machine DISCOVERs are being sent on 3110Sstevel@tonic-gate * unsigned int: the number of DISCOVERs sent so far 3120Sstevel@tonic-gate * output: boolean_t: B_TRUE if retransmissions should stop 3130Sstevel@tonic-gate */ 3140Sstevel@tonic-gate 3153431Scarlsonj /* ARGSUSED1 */ 3160Sstevel@tonic-gate static boolean_t 3173431Scarlsonj stop_selecting(dhcp_smach_t *dsmp, unsigned int n_discovers) 3180Sstevel@tonic-gate { 3193431Scarlsonj /* 3203431Scarlsonj * If we're using v4 and the underlying LIF we're trying to configure 3213431Scarlsonj * has been touched by the user, then bail out. 3223431Scarlsonj */ 3233431Scarlsonj if (!dsmp->dsm_isv6 && !verify_lif(dsmp->dsm_lif)) { 3243431Scarlsonj finished_smach(dsmp, DHCP_IPC_E_UNKIF); 3253431Scarlsonj return (B_TRUE); 3263431Scarlsonj } 3273431Scarlsonj 3283431Scarlsonj if (dsmp->dsm_recv_pkt_list != NULL) { 3293431Scarlsonj dhcp_requesting(NULL, dsmp); 3303431Scarlsonj if (dsmp->dsm_state != SELECTING) 3313431Scarlsonj return (B_TRUE); 3323431Scarlsonj } 3330Sstevel@tonic-gate return (B_FALSE); 3340Sstevel@tonic-gate } 335