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*12989SVasumathi.Sundaram@oracle.COM * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate *
240Sstevel@tonic-gate * SELECTING state of the client state machine.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate #include <sys/types.h>
280Sstevel@tonic-gate #include <stdio.h>
294106Scarlsonj #include <stdlib.h>
300Sstevel@tonic-gate #include <strings.h>
310Sstevel@tonic-gate #include <time.h>
320Sstevel@tonic-gate #include <limits.h>
330Sstevel@tonic-gate #include <netinet/in.h>
340Sstevel@tonic-gate #include <net/route.h>
350Sstevel@tonic-gate #include <net/if.h>
360Sstevel@tonic-gate #include <netinet/dhcp.h>
370Sstevel@tonic-gate #include <netinet/udp.h>
380Sstevel@tonic-gate #include <netinet/ip_var.h>
390Sstevel@tonic-gate #include <netinet/udp_var.h>
400Sstevel@tonic-gate #include <dhcpmsg.h>
41*12989SVasumathi.Sundaram@oracle.COM #include <dhcp_hostconf.h>
420Sstevel@tonic-gate
430Sstevel@tonic-gate #include "states.h"
440Sstevel@tonic-gate #include "agent.h"
450Sstevel@tonic-gate #include "util.h"
460Sstevel@tonic-gate #include "interface.h"
470Sstevel@tonic-gate #include "packet.h"
480Sstevel@tonic-gate #include "defaults.h"
490Sstevel@tonic-gate
500Sstevel@tonic-gate static stop_func_t stop_selecting;
510Sstevel@tonic-gate
520Sstevel@tonic-gate /*
533431Scarlsonj * dhcp_start(): starts DHCP on a state machine
540Sstevel@tonic-gate *
550Sstevel@tonic-gate * input: iu_tq_t *: unused
563431Scarlsonj * void *: the state machine on which to start DHCP
570Sstevel@tonic-gate * output: void
580Sstevel@tonic-gate */
590Sstevel@tonic-gate
600Sstevel@tonic-gate /* ARGSUSED */
614106Scarlsonj static void
dhcp_start(iu_tq_t * tqp,void * arg)620Sstevel@tonic-gate dhcp_start(iu_tq_t *tqp, void *arg)
630Sstevel@tonic-gate {
643431Scarlsonj dhcp_smach_t *dsmp = arg;
650Sstevel@tonic-gate
664106Scarlsonj dsmp->dsm_start_timer = -1;
674106Scarlsonj (void) set_smach_state(dsmp, INIT);
684106Scarlsonj if (verify_smach(dsmp)) {
694106Scarlsonj dhcpmsg(MSG_VERBOSE, "starting DHCP on %s", dsmp->dsm_name);
704106Scarlsonj dhcp_selecting(dsmp);
714106Scarlsonj }
724106Scarlsonj }
730Sstevel@tonic-gate
744106Scarlsonj /*
754106Scarlsonj * set_start_timer(): sets a random timer to start a DHCP state machine
764106Scarlsonj *
774106Scarlsonj * input: dhcp_smach_t *: the state machine on which to start DHCP
784106Scarlsonj * output: boolean_t: B_TRUE if a timer is now running
794106Scarlsonj */
804106Scarlsonj
814106Scarlsonj boolean_t
set_start_timer(dhcp_smach_t * dsmp)824106Scarlsonj set_start_timer(dhcp_smach_t *dsmp)
834106Scarlsonj {
844106Scarlsonj if (dsmp->dsm_start_timer != -1)
854106Scarlsonj return (B_TRUE);
864106Scarlsonj
874106Scarlsonj dsmp->dsm_start_timer = iu_schedule_timer_ms(tq,
884106Scarlsonj lrand48() % DHCP_SELECT_WAIT, dhcp_start, dsmp);
894106Scarlsonj if (dsmp->dsm_start_timer == -1)
904106Scarlsonj return (B_FALSE);
914106Scarlsonj
924106Scarlsonj hold_smach(dsmp);
934106Scarlsonj return (B_TRUE);
940Sstevel@tonic-gate }
950Sstevel@tonic-gate
960Sstevel@tonic-gate /*
973431Scarlsonj * dhcp_selecting(): sends a DISCOVER and sets up reception of OFFERs for
983431Scarlsonj * IPv4, or sends a Solicit and sets up reception of
993431Scarlsonj * Advertisements for DHCPv6.
1000Sstevel@tonic-gate *
1013431Scarlsonj * input: dhcp_smach_t *: the state machine on which to send the DISCOVER
1020Sstevel@tonic-gate * output: void
1030Sstevel@tonic-gate */
1040Sstevel@tonic-gate
1050Sstevel@tonic-gate void
dhcp_selecting(dhcp_smach_t * dsmp)1063431Scarlsonj dhcp_selecting(dhcp_smach_t *dsmp)
1070Sstevel@tonic-gate {
1080Sstevel@tonic-gate dhcp_pkt_t *dpkt;
1090Sstevel@tonic-gate const char *reqhost;
1100Sstevel@tonic-gate char hostfile[PATH_MAX + 1];
1110Sstevel@tonic-gate
1120Sstevel@tonic-gate /*
1133431Scarlsonj * We first set up to collect OFFER/Advertise packets as they arrive.
1143431Scarlsonj * We then send out DISCOVER/Solicit probes. Then we wait a
1153431Scarlsonj * user-tunable number of seconds before seeing if OFFERs/
1163431Scarlsonj * Advertisements have come in response to our DISCOVER/Solicit. If
1173431Scarlsonj * none have come in, we continue to wait, sending out our DISCOVER/
1183431Scarlsonj * Solicit probes with exponential backoff. If no OFFER/Advertisement
1193431Scarlsonj * is ever received, we will wait forever (note that since we're
1203431Scarlsonj * event-driven though, we're still able to service other state
1213431Scarlsonj * machines).
1220Sstevel@tonic-gate *
1233431Scarlsonj * Note that we do an reset_smach() here because we may be landing in
1243431Scarlsonj * dhcp_selecting() as a result of restarting DHCP, so the state
1253431Scarlsonj * machine may not be fresh.
1260Sstevel@tonic-gate */
1270Sstevel@tonic-gate
1283431Scarlsonj reset_smach(dsmp);
1293431Scarlsonj if (!set_smach_state(dsmp, SELECTING)) {
1303431Scarlsonj dhcpmsg(MSG_ERROR,
1313431Scarlsonj "dhcp_selecting: cannot switch to SELECTING state; "
1323431Scarlsonj "reverting to INIT on %s", dsmp->dsm_name);
1333431Scarlsonj goto failed;
1340Sstevel@tonic-gate
1353431Scarlsonj }
1360Sstevel@tonic-gate
137*12989SVasumathi.Sundaram@oracle.COM /* Remove the stale hostconf file, if there is any */
138*12989SVasumathi.Sundaram@oracle.COM (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
139*12989SVasumathi.Sundaram@oracle.COM
1403431Scarlsonj dsmp->dsm_offer_timer = iu_schedule_timer(tq,
1413431Scarlsonj dsmp->dsm_offer_wait, dhcp_requesting, dsmp);
1423431Scarlsonj if (dsmp->dsm_offer_timer == -1) {
1433431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot schedule to read "
1443431Scarlsonj "%s packets", dsmp->dsm_isv6 ? "Advertise" : "OFFER");
1453431Scarlsonj goto failed;
1463431Scarlsonj }
1470Sstevel@tonic-gate
1483431Scarlsonj hold_smach(dsmp);
1490Sstevel@tonic-gate
1503431Scarlsonj /*
1513431Scarlsonj * Assemble and send the DHCPDISCOVER or Solicit message.
1523431Scarlsonj *
1533431Scarlsonj * If this fails, we'll wait for the select timer to go off
1543431Scarlsonj * before trying again.
1553431Scarlsonj */
1563431Scarlsonj if (dsmp->dsm_isv6) {
1573431Scarlsonj dhcpv6_ia_na_t d6in;
1580Sstevel@tonic-gate
1593431Scarlsonj if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_SOLICIT)) == NULL) {
1603431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up "
1613431Scarlsonj "Solicit packet");
1623431Scarlsonj return;
1630Sstevel@tonic-gate }
1640Sstevel@tonic-gate
1653431Scarlsonj /* Add an IA_NA option for our controlling LIF */
1663431Scarlsonj d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
1673431Scarlsonj d6in.d6in_t1 = htonl(0);
1683431Scarlsonj d6in.d6in_t2 = htonl(0);
1693431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
1703431Scarlsonj (dhcpv6_option_t *)&d6in + 1,
1713431Scarlsonj sizeof (d6in) - sizeof (dhcpv6_option_t));
1723431Scarlsonj
1733431Scarlsonj /* Option Request option for desired information */
1743431Scarlsonj (void) add_pkt_prl(dpkt, dsmp);
1753431Scarlsonj
1763431Scarlsonj /* Enable Rapid-Commit */
1773431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_RAPID_COMMIT, NULL, 0);
1783431Scarlsonj
1793431Scarlsonj /* xxx add Reconfigure Accept */
1800Sstevel@tonic-gate
1813431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers,
1823431Scarlsonj stop_selecting, DHCPV6_SOL_TIMEOUT, DHCPV6_SOL_MAX_RT);
1833431Scarlsonj } else {
1843431Scarlsonj if ((dpkt = init_pkt(dsmp, DISCOVER)) == NULL) {
1853431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up "
1863431Scarlsonj "DISCOVER packet");
1873431Scarlsonj return;
1883431Scarlsonj }
1890Sstevel@tonic-gate
1903431Scarlsonj /*
1913431Scarlsonj * The max DHCP message size option is set to the interface
1923431Scarlsonj * MTU, minus the size of the UDP and IP headers.
1933431Scarlsonj */
1943431Scarlsonj (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
1953431Scarlsonj htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
1963431Scarlsonj (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
1970Sstevel@tonic-gate
1983448Sdh155122 if (class_id_len != 0) {
1993448Sdh155122 (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
2003448Sdh155122 class_id_len);
2013448Sdh155122 }
2023431Scarlsonj (void) add_pkt_prl(dpkt, dsmp);
2030Sstevel@tonic-gate
2043431Scarlsonj if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6,
2053431Scarlsonj DF_REQUEST_HOSTNAME)) {
2063431Scarlsonj dhcpmsg(MSG_DEBUG,
2073431Scarlsonj "dhcp_selecting: DF_REQUEST_HOSTNAME");
2083431Scarlsonj (void) snprintf(hostfile, sizeof (hostfile),
2093431Scarlsonj "/etc/hostname.%s", dsmp->dsm_name);
2100Sstevel@tonic-gate
2113431Scarlsonj if ((reqhost = iffile_to_hostname(hostfile)) != NULL) {
2123431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s",
2133431Scarlsonj reqhost);
2143431Scarlsonj dsmp->dsm_reqhost = strdup(reqhost);
2153431Scarlsonj if (dsmp->dsm_reqhost != NULL)
2163431Scarlsonj (void) add_pkt_opt(dpkt, CD_HOSTNAME,
2173431Scarlsonj dsmp->dsm_reqhost,
2183431Scarlsonj strlen(dsmp->dsm_reqhost));
2193431Scarlsonj else
2203431Scarlsonj dhcpmsg(MSG_WARNING,
2213431Scarlsonj "dhcp_selecting: cannot allocate "
2223431Scarlsonj "memory for host name option");
2233431Scarlsonj }
2240Sstevel@tonic-gate }
2253431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0);
2263431Scarlsonj
2273431Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
2283431Scarlsonj stop_selecting);
2290Sstevel@tonic-gate }
2303431Scarlsonj return;
2310Sstevel@tonic-gate
2323431Scarlsonj failed:
2333431Scarlsonj (void) set_smach_state(dsmp, INIT);
2343431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_FAILED;
2353431Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate /*
2393431Scarlsonj * stop_selecting(): decides when to stop retransmitting DISCOVERs -- only when
2403431Scarlsonj * abandoning the state machine. For DHCPv6, this timer may
2413431Scarlsonj * go off before the offer wait timer. If so, then this is a
2423431Scarlsonj * good time to check for valid Advertisements, so cancel the
2433431Scarlsonj * timer and go check.
2440Sstevel@tonic-gate *
2453431Scarlsonj * input: dhcp_smach_t *: the state machine DISCOVERs are being sent on
2460Sstevel@tonic-gate * unsigned int: the number of DISCOVERs sent so far
2470Sstevel@tonic-gate * output: boolean_t: B_TRUE if retransmissions should stop
2480Sstevel@tonic-gate */
2490Sstevel@tonic-gate
2503431Scarlsonj /* ARGSUSED1 */
2510Sstevel@tonic-gate static boolean_t
stop_selecting(dhcp_smach_t * dsmp,unsigned int n_discovers)2523431Scarlsonj stop_selecting(dhcp_smach_t *dsmp, unsigned int n_discovers)
2530Sstevel@tonic-gate {
2543431Scarlsonj /*
2553431Scarlsonj * If we're using v4 and the underlying LIF we're trying to configure
2563431Scarlsonj * has been touched by the user, then bail out.
2573431Scarlsonj */
2583431Scarlsonj if (!dsmp->dsm_isv6 && !verify_lif(dsmp->dsm_lif)) {
2593431Scarlsonj finished_smach(dsmp, DHCP_IPC_E_UNKIF);
2603431Scarlsonj return (B_TRUE);
2613431Scarlsonj }
2623431Scarlsonj
2633431Scarlsonj if (dsmp->dsm_recv_pkt_list != NULL) {
2643431Scarlsonj dhcp_requesting(NULL, dsmp);
2653431Scarlsonj if (dsmp->dsm_state != SELECTING)
2663431Scarlsonj return (B_TRUE);
2673431Scarlsonj }
2680Sstevel@tonic-gate return (B_FALSE);
2690Sstevel@tonic-gate }
270