xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c (revision 12989:1e6b26d10a55)
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