xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/netstrategy/netstrategy.c (revision 7170:4ef930516e57)
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
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * 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  */
21761Smeem 
220Sstevel@tonic-gate /*
23*7170Sdme  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * This program does the following:
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  * a) Returns:
330Sstevel@tonic-gate  *	0	- if the program successfully determined the net strategy.
340Sstevel@tonic-gate  *	!0	- if an error occurred.
350Sstevel@tonic-gate  *
360Sstevel@tonic-gate  * b) If the program is successful, it prints three tokens to
370Sstevel@tonic-gate  *    stdout: <root fs type> <interface name> <net config strategy>.
380Sstevel@tonic-gate  *    where:
390Sstevel@tonic-gate  *	<root fs type>		-	"nfs" or "ufs"
400Sstevel@tonic-gate  *	<interface name>	-	"hme0" or "none"
415084Sjohnlev  *	<net config strategy>	-	"dhcp", "rarp", "bootprops"
425084Sjohnlev  *					or "none"
430Sstevel@tonic-gate  *
440Sstevel@tonic-gate  *    Eg:
450Sstevel@tonic-gate  *	# /sbin/netstrategy
460Sstevel@tonic-gate  *	ufs hme0 dhcp
470Sstevel@tonic-gate  *
480Sstevel@tonic-gate  *    <root fs type> identifies the system's root file system type.
490Sstevel@tonic-gate  *
500Sstevel@tonic-gate  *    <interface name> is the 16 char name of the root interface, and is only
510Sstevel@tonic-gate  *	set if rarp/dhcp was used to configure the interface.
520Sstevel@tonic-gate  *
535084Sjohnlev  *    <net config strategy> can be either "rarp", "dhcp", "bootprops", or
545084Sjohnlev  *	"none" depending on which strategy was used to configure the
555084Sjohnlev  *	interface. Is "none" if no interface was configured using a
565084Sjohnlev  *	net-based strategy.
570Sstevel@tonic-gate  *
580Sstevel@tonic-gate  * CAVEATS: what about autoclient systems? XXX
595084Sjohnlev  *
605084Sjohnlev  * The logic here must match that in usr/src/uts/common/fs/nfs/nfs_dlinet.c,
615084Sjohnlev  * in particular that code (which implements diskless boot) imposes an
625084Sjohnlev  * ordering on possible ways of configuring network interfaces.
630Sstevel@tonic-gate  */
640Sstevel@tonic-gate 
650Sstevel@tonic-gate #include <stdio.h>
660Sstevel@tonic-gate #include <stdlib.h>
670Sstevel@tonic-gate #include <unistd.h>
680Sstevel@tonic-gate #include <string.h>
690Sstevel@tonic-gate #include <sys/types.h>
700Sstevel@tonic-gate #include <errno.h>
710Sstevel@tonic-gate #include <alloca.h>
720Sstevel@tonic-gate #include <sys/systeminfo.h>
730Sstevel@tonic-gate #include <sys/socket.h>
740Sstevel@tonic-gate #include <sys/sockio.h>
750Sstevel@tonic-gate #include <net/if.h>
760Sstevel@tonic-gate #include <sys/statvfs.h>
775084Sjohnlev #include <libdevinfo.h>
785084Sjohnlev 
795084Sjohnlev static char *program;
805084Sjohnlev 
815084Sjohnlev static char *
825084Sjohnlev get_root_fstype()
835084Sjohnlev {
845084Sjohnlev 	static struct statvfs vfs;
855084Sjohnlev 
865084Sjohnlev 	/* root location */
875084Sjohnlev 	if (statvfs("/", &vfs) < 0) {
885084Sjohnlev 		return ("none");
895084Sjohnlev 	} else {
905084Sjohnlev 		if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0)
915084Sjohnlev 			vfs.f_basetype[sizeof ("nfs") - 1] = '\0';
925084Sjohnlev 		return (vfs.f_basetype);
935084Sjohnlev 	}
945084Sjohnlev }
955084Sjohnlev 
965084Sjohnlev /*
975084Sjohnlev  * The following boot properties can be used to configure a network
985084Sjohnlev  * interface in the case of a diskless boot.
995084Sjohnlev  *	host-ip, subnet-mask, server-path, server-name, server-ip.
1005084Sjohnlev  *
1015084Sjohnlev  * XXX non-diskless case requires "network-interface"?
1025084Sjohnlev  */
1035084Sjohnlev static boolean_t
1045084Sjohnlev boot_properties_present()
1055084Sjohnlev {
1065084Sjohnlev 	/* XXX should use sys/bootprops.h, but it's not delivered */
1075084Sjohnlev 	const char *required_properties[] = {
1085084Sjohnlev 		"host-ip",
1095084Sjohnlev 		"subnet-mask",
1105084Sjohnlev 		"server-path",
1115084Sjohnlev 		"server-name",
1125084Sjohnlev 		"server-ip",
1135084Sjohnlev 		NULL,
1145084Sjohnlev 	};
1155084Sjohnlev 	const char **prop = required_properties;
1165084Sjohnlev 	char *prop_value;
1175084Sjohnlev 	di_node_t dn;
1185084Sjohnlev 
1195084Sjohnlev 	if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
1205084Sjohnlev 		(void) fprintf(stderr, "%s: di_init: %s\n", program,
1215084Sjohnlev 		    strerror(errno));
1225084Sjohnlev 		di_fini(dn);
1235084Sjohnlev 		return (B_FALSE);
1245084Sjohnlev 	}
1255084Sjohnlev 
1265084Sjohnlev 	while (*prop != NULL) {
1275084Sjohnlev 		if (di_prop_lookup_strings(DDI_DEV_T_ANY,
1285084Sjohnlev 		    dn, *prop, &prop_value) != 1) {
1295084Sjohnlev 			di_fini(dn);
1305084Sjohnlev 			return (B_FALSE);
1315084Sjohnlev 		}
1325084Sjohnlev 		prop++;
1335084Sjohnlev 	}
1345084Sjohnlev 	di_fini(dn);
1355084Sjohnlev 
1365084Sjohnlev 	return (B_TRUE);
1375084Sjohnlev }
1385084Sjohnlev 
1395084Sjohnlev static char *
1405084Sjohnlev get_first_interface()
1415084Sjohnlev {
1425084Sjohnlev 	int fd;
1435084Sjohnlev 	struct lifnum ifnum;
1445084Sjohnlev 	struct lifconf ifconf;
1455084Sjohnlev 	struct lifreq *ifr;
1465084Sjohnlev 	static char interface[IFNAMSIZ];
1475084Sjohnlev 
1485084Sjohnlev 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1495084Sjohnlev 		(void) fprintf(stderr, "%s: socket: %s\n", program,
1505084Sjohnlev 		    strerror(errno));
1515084Sjohnlev 		return (NULL);
1525084Sjohnlev 	}
1535084Sjohnlev 
1545084Sjohnlev 	ifnum.lifn_family = AF_UNSPEC;
155*7170Sdme 	ifnum.lifn_flags = 0;
156*7170Sdme 	ifnum.lifn_count = 0;
1575084Sjohnlev 
1585084Sjohnlev 	if (ioctl(fd, SIOCGLIFNUM, &ifnum) < 0) {
1595084Sjohnlev 		(void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program,
1605084Sjohnlev 		    strerror(errno));
1615084Sjohnlev 		(void) close(fd);
1625084Sjohnlev 		return (NULL);
1635084Sjohnlev 	}
1645084Sjohnlev 
1655084Sjohnlev 	ifconf.lifc_family = AF_UNSPEC;
166*7170Sdme 	ifconf.lifc_flags = 0;
1675084Sjohnlev 	ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq);
1685084Sjohnlev 	ifconf.lifc_buf = alloca(ifconf.lifc_len);
1695084Sjohnlev 
1705084Sjohnlev 	if (ioctl(fd, SIOCGLIFCONF, &ifconf) < 0) {
1715084Sjohnlev 		(void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program,
1725084Sjohnlev 		    strerror(errno));
1735084Sjohnlev 		(void) close(fd);
1745084Sjohnlev 		return (NULL);
1755084Sjohnlev 	}
1765084Sjohnlev 
1775084Sjohnlev 	for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len /
1785084Sjohnlev 	    sizeof (ifconf.lifc_req[0])]; ifr++) {
1795084Sjohnlev 
1805084Sjohnlev 		if (strchr(ifr->lifr_name, ':') != NULL)
1815084Sjohnlev 			continue;	/* skip logical interfaces */
1825084Sjohnlev 
1835084Sjohnlev 		if (ioctl(fd, SIOCGLIFFLAGS, ifr) < 0) {
1845084Sjohnlev 			(void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n",
1855084Sjohnlev 			    program, strerror(errno));
1865084Sjohnlev 			continue;
1875084Sjohnlev 		}
1885084Sjohnlev 
1895084Sjohnlev 		if (ifr->lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT))
1905084Sjohnlev 			continue;
1915084Sjohnlev 
1925084Sjohnlev 		if (ifr->lifr_flags & IFF_UP) {
1935084Sjohnlev 			/*
1945084Sjohnlev 			 * For the "nfs rarp" and "nfs bootprops"
1955084Sjohnlev 			 * cases, we assume that the first non-virtual
1965084Sjohnlev 			 * IFF_UP interface is the one used.
1975084Sjohnlev 			 *
1985084Sjohnlev 			 * Since the order of the interfaces retrieved
1995084Sjohnlev 			 * via SIOCGLIFCONF is not deterministic, this
2005084Sjohnlev 			 * is largely silliness, but (a) "it's always
2015084Sjohnlev 			 * been this way", (b) machines booted this
2025084Sjohnlev 			 * way typically only have one interface, and
2035084Sjohnlev 			 * (c) no one consumes the interface name in
2045084Sjohnlev 			 * the RARP case anyway.
2055084Sjohnlev 			 */
2065084Sjohnlev 			(void) strncpy(interface, ifr->lifr_name, IFNAMSIZ);
2075084Sjohnlev 			(void) close(fd);
2085084Sjohnlev 			return (interface);
2095084Sjohnlev 		}
2105084Sjohnlev 	}
2115084Sjohnlev 
2125084Sjohnlev 	(void) close(fd);
2135084Sjohnlev 
2145084Sjohnlev 	return (NULL);
2155084Sjohnlev }
2165084Sjohnlev 
2175084Sjohnlev /*
2185084Sjohnlev  * Is DHCP running on the specified interface?
2195084Sjohnlev  */
2205084Sjohnlev static boolean_t
2215084Sjohnlev check_dhcp_running(char *interface)
2225084Sjohnlev {
2235084Sjohnlev 	int fd;
2245084Sjohnlev 	struct ifreq ifr;
2255084Sjohnlev 
2265084Sjohnlev 	if (interface == NULL)
2275084Sjohnlev 		return (B_FALSE);
2285084Sjohnlev 
2295084Sjohnlev 	(void) strncpy(ifr.ifr_name, interface, IFNAMSIZ);
2305084Sjohnlev 
2315084Sjohnlev 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
2325084Sjohnlev 		(void) fprintf(stderr, "%s: socket: %s\n", program,
2335084Sjohnlev 		    strerror(errno));
2345084Sjohnlev 		return (B_FALSE);
2355084Sjohnlev 	}
2365084Sjohnlev 
2375084Sjohnlev 	if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
2385084Sjohnlev 		(void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n",
2395084Sjohnlev 		    program, strerror(errno));
2405084Sjohnlev 		return (B_FALSE);
2415084Sjohnlev 	}
2425084Sjohnlev 
2435084Sjohnlev 	if (ifr.ifr_flags & IFF_DHCPRUNNING)
2445084Sjohnlev 		return (B_TRUE);
2455084Sjohnlev 
2465084Sjohnlev 	return (B_FALSE);
2475084Sjohnlev }
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate /* ARGSUSED */
2500Sstevel@tonic-gate int
2510Sstevel@tonic-gate main(int argc, char *argv[])
2520Sstevel@tonic-gate {
2535084Sjohnlev 	char *root, *interface, *strategy, dummy;
2545084Sjohnlev 	long len;
2555084Sjohnlev 
2565084Sjohnlev 	root = interface = strategy = NULL;
2575084Sjohnlev 	program = argv[0];
2585084Sjohnlev 
2595084Sjohnlev 	root = get_root_fstype();
2600Sstevel@tonic-gate 
2615084Sjohnlev 	/*
2625084Sjohnlev 	 * If diskless, perhaps boot properties were used to configure
2635084Sjohnlev 	 * the interface.
2645084Sjohnlev 	 */
2655084Sjohnlev 	if ((strcmp(root, "nfs") == 0) && boot_properties_present()) {
2665084Sjohnlev 		strategy = "bootprops";
2675084Sjohnlev 
2685084Sjohnlev 		interface = get_first_interface();
2695084Sjohnlev 		if (interface == NULL) {
2705084Sjohnlev 			(void) fprintf(stderr,
2715084Sjohnlev 			    "%s: cannot identify root interface.\n", program);
2725084Sjohnlev 			return (2);
2735084Sjohnlev 		}
2745084Sjohnlev 
2755084Sjohnlev 		(void) printf("%s %s %s\n", root, interface, strategy);
2765084Sjohnlev 		return (0);
2770Sstevel@tonic-gate 	}
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	/*
2800Sstevel@tonic-gate 	 * Handle the simple case where diskless dhcp tells us everything
2810Sstevel@tonic-gate 	 * we need to know.
2820Sstevel@tonic-gate 	 */
2830Sstevel@tonic-gate 	if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) {
2840Sstevel@tonic-gate 		/* interface is first thing in cache. */
2850Sstevel@tonic-gate 		strategy = "dhcp";
2860Sstevel@tonic-gate 		interface = alloca(len);
2870Sstevel@tonic-gate 		(void) sysinfo(SI_DHCP_CACHE, interface, len);
2880Sstevel@tonic-gate 		(void) printf("%s %s %s\n", root, interface, strategy);
2890Sstevel@tonic-gate 		return (0);
2900Sstevel@tonic-gate 	}
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	/*
2930Sstevel@tonic-gate 	 * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle
2940Sstevel@tonic-gate 	 * "ufs rarp" (consumers are coded to deal with this reality), so
2950Sstevel@tonic-gate 	 * there are three possible situations:
2960Sstevel@tonic-gate 	 *
2970Sstevel@tonic-gate 	 *	1. We're "ufs dhcp" if there are any interfaces which have
2980Sstevel@tonic-gate 	 *	   obtained their addresses through DHCP.  That is, if there
2990Sstevel@tonic-gate 	 *	   are any IFF_UP and non-IFF_VIRTUAL interfaces also have
3000Sstevel@tonic-gate 	 *	   IFF_DHCPRUNNING set.
3010Sstevel@tonic-gate 	 *
3020Sstevel@tonic-gate 	 *	2. We're "ufs none" if our filesystem is local and there
3030Sstevel@tonic-gate 	 *	   are no interfaces which have obtained their addresses
3040Sstevel@tonic-gate 	 *	   through DHCP.
3050Sstevel@tonic-gate 	 *
3060Sstevel@tonic-gate 	 *	3. We're "nfs rarp" if our filesystem is remote and there's
3070Sstevel@tonic-gate 	 *	   at least IFF_UP non-IFF_VIRTUAL interface (which there
3080Sstevel@tonic-gate 	 *	   *must* be, since we're running over NFS somehow), then
3090Sstevel@tonic-gate 	 *	   it must be RARP since SI_DHCP_CACHE call above failed.
3100Sstevel@tonic-gate 	 *	   It's too bad there isn't an IFF_RARPRUNNING flag.
3110Sstevel@tonic-gate 	 */
3120Sstevel@tonic-gate 
3135084Sjohnlev 	interface = get_first_interface();
3140Sstevel@tonic-gate 
3155084Sjohnlev 	if (check_dhcp_running(interface))
3165084Sjohnlev 		strategy = "dhcp";
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) {
3190Sstevel@tonic-gate 		if (interface == NULL) {
3200Sstevel@tonic-gate 			(void) fprintf(stderr,
3215084Sjohnlev 			    "%s: cannot identify root interface.\n", program);
3220Sstevel@tonic-gate 			return (2);
3230Sstevel@tonic-gate 		}
3240Sstevel@tonic-gate 		if (strategy == NULL)
3250Sstevel@tonic-gate 			strategy = "rarp";	/*  must be rarp/bootparams */
3260Sstevel@tonic-gate 	} else {
3270Sstevel@tonic-gate 		if (interface == NULL || strategy == NULL)
3280Sstevel@tonic-gate 			interface = strategy = "none";
3290Sstevel@tonic-gate 	}
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	(void) printf("%s %s %s\n", root, interface, strategy);
3320Sstevel@tonic-gate 	return (0);
3330Sstevel@tonic-gate }
334