xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/netstrategy/netstrategy.c (revision 7254:444cb228ad69)
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 /*
237170Sdme  * 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 
81*7254Sokie static int s4, s6;	/* inet and inet6 sockets */
82*7254Sokie 
83*7254Sokie static boolean_t
open_sockets(void)84*7254Sokie open_sockets(void)
85*7254Sokie {
86*7254Sokie 	if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
87*7254Sokie 		(void) fprintf(stderr, "%s: inet socket: %s\n", program,
88*7254Sokie 		    strerror(errno));
89*7254Sokie 		return (B_FALSE);
90*7254Sokie 	}
91*7254Sokie 	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
92*7254Sokie 		(void) fprintf(stderr, "%s: inet6 socket: %s\n", program,
93*7254Sokie 		    strerror(errno));
94*7254Sokie 		return (B_FALSE);
95*7254Sokie 	}
96*7254Sokie 	return (B_TRUE);
97*7254Sokie }
98*7254Sokie 
99*7254Sokie static void
close_sockets(void)100*7254Sokie close_sockets(void)
101*7254Sokie {
102*7254Sokie 	(void) close(s4);
103*7254Sokie 	(void) close(s6);
104*7254Sokie }
105*7254Sokie 
1065084Sjohnlev static char *
get_root_fstype()1075084Sjohnlev get_root_fstype()
1085084Sjohnlev {
1095084Sjohnlev 	static struct statvfs vfs;
1105084Sjohnlev 
1115084Sjohnlev 	/* root location */
1125084Sjohnlev 	if (statvfs("/", &vfs) < 0) {
1135084Sjohnlev 		return ("none");
1145084Sjohnlev 	} else {
1155084Sjohnlev 		if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0)
1165084Sjohnlev 			vfs.f_basetype[sizeof ("nfs") - 1] = '\0';
1175084Sjohnlev 		return (vfs.f_basetype);
1185084Sjohnlev 	}
1195084Sjohnlev }
1205084Sjohnlev 
1215084Sjohnlev /*
1225084Sjohnlev  * The following boot properties can be used to configure a network
1235084Sjohnlev  * interface in the case of a diskless boot.
1245084Sjohnlev  *	host-ip, subnet-mask, server-path, server-name, server-ip.
1255084Sjohnlev  *
1265084Sjohnlev  * XXX non-diskless case requires "network-interface"?
1275084Sjohnlev  */
1285084Sjohnlev static boolean_t
boot_properties_present()1295084Sjohnlev boot_properties_present()
1305084Sjohnlev {
1315084Sjohnlev 	/* XXX should use sys/bootprops.h, but it's not delivered */
1325084Sjohnlev 	const char *required_properties[] = {
1335084Sjohnlev 		"host-ip",
1345084Sjohnlev 		"subnet-mask",
1355084Sjohnlev 		"server-path",
1365084Sjohnlev 		"server-name",
1375084Sjohnlev 		"server-ip",
1385084Sjohnlev 		NULL,
1395084Sjohnlev 	};
1405084Sjohnlev 	const char **prop = required_properties;
1415084Sjohnlev 	char *prop_value;
1425084Sjohnlev 	di_node_t dn;
1435084Sjohnlev 
1445084Sjohnlev 	if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
1455084Sjohnlev 		(void) fprintf(stderr, "%s: di_init: %s\n", program,
1465084Sjohnlev 		    strerror(errno));
1475084Sjohnlev 		di_fini(dn);
1485084Sjohnlev 		return (B_FALSE);
1495084Sjohnlev 	}
1505084Sjohnlev 
1515084Sjohnlev 	while (*prop != NULL) {
1525084Sjohnlev 		if (di_prop_lookup_strings(DDI_DEV_T_ANY,
1535084Sjohnlev 		    dn, *prop, &prop_value) != 1) {
1545084Sjohnlev 			di_fini(dn);
1555084Sjohnlev 			return (B_FALSE);
1565084Sjohnlev 		}
1575084Sjohnlev 		prop++;
1585084Sjohnlev 	}
1595084Sjohnlev 	di_fini(dn);
1605084Sjohnlev 
1615084Sjohnlev 	return (B_TRUE);
1625084Sjohnlev }
1635084Sjohnlev 
1645084Sjohnlev static char *
get_first_interface(boolean_t * dhcpflag)165*7254Sokie get_first_interface(boolean_t *dhcpflag)
1665084Sjohnlev {
1675084Sjohnlev 	struct lifnum ifnum;
1685084Sjohnlev 	struct lifconf ifconf;
1695084Sjohnlev 	struct lifreq *ifr;
170*7254Sokie 	static char interface[LIFNAMSIZ];
171*7254Sokie 	boolean_t isv4, found_one = B_FALSE;
1725084Sjohnlev 
1735084Sjohnlev 	ifnum.lifn_family = AF_UNSPEC;
1747170Sdme 	ifnum.lifn_flags = 0;
1757170Sdme 	ifnum.lifn_count = 0;
1765084Sjohnlev 
177*7254Sokie 	if (ioctl(s4, SIOCGLIFNUM, &ifnum) < 0) {
1785084Sjohnlev 		(void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program,
1795084Sjohnlev 		    strerror(errno));
1805084Sjohnlev 		return (NULL);
1815084Sjohnlev 	}
1825084Sjohnlev 
1835084Sjohnlev 	ifconf.lifc_family = AF_UNSPEC;
1847170Sdme 	ifconf.lifc_flags = 0;
1855084Sjohnlev 	ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq);
1865084Sjohnlev 	ifconf.lifc_buf = alloca(ifconf.lifc_len);
1875084Sjohnlev 
188*7254Sokie 	if (ioctl(s4, SIOCGLIFCONF, &ifconf) < 0) {
1895084Sjohnlev 		(void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program,
1905084Sjohnlev 		    strerror(errno));
1915084Sjohnlev 		return (NULL);
1925084Sjohnlev 	}
1935084Sjohnlev 
1945084Sjohnlev 	for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len /
1955084Sjohnlev 	    sizeof (ifconf.lifc_req[0])]; ifr++) {
196*7254Sokie 		struct lifreq flifr;
197*7254Sokie 		struct sockaddr_in *sin;
1985084Sjohnlev 
1995084Sjohnlev 		if (strchr(ifr->lifr_name, ':') != NULL)
2005084Sjohnlev 			continue;	/* skip logical interfaces */
2015084Sjohnlev 
202*7254Sokie 		isv4 = ifr->lifr_addr.ss_family == AF_INET;
203*7254Sokie 
204*7254Sokie 		(void) strncpy(flifr.lifr_name, ifr->lifr_name, LIFNAMSIZ);
205*7254Sokie 
206*7254Sokie 		if (ioctl(isv4 ? s4 : s6, SIOCGLIFFLAGS, &flifr) < 0) {
207*7254Sokie 			(void) fprintf(stderr, "%s: SIOCGLIFFLAGS: %s\n",
2085084Sjohnlev 			    program, strerror(errno));
2095084Sjohnlev 			continue;
2105084Sjohnlev 		}
2115084Sjohnlev 
212*7254Sokie 		if (!(flifr.lifr_flags & IFF_UP) ||
213*7254Sokie 		    (flifr.lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT)))
2145084Sjohnlev 			continue;
2155084Sjohnlev 
216*7254Sokie 		/*
217*7254Sokie 		 * For the "nfs rarp" and "nfs bootprops"
218*7254Sokie 		 * cases, we assume that the first non-virtual
219*7254Sokie 		 * IFF_UP interface with a non-zero address is
220*7254Sokie 		 * the one used.
221*7254Sokie 		 *
222*7254Sokie 		 * For the non-zero address check, we only check
223*7254Sokie 		 * v4 interfaces, as it's not possible to set the
224*7254Sokie 		 * the first logical interface (the only ones we
225*7254Sokie 		 * look at here) to ::0; that interface must have
226*7254Sokie 		 * a link-local address.
227*7254Sokie 		 *
228*7254Sokie 		 * If we don't find an IFF_UP interface with a
229*7254Sokie 		 * non-zero address, we'll return the last IFF_UP
230*7254Sokie 		 * interface seen.
231*7254Sokie 		 *
232*7254Sokie 		 * Since the order of the interfaces retrieved
233*7254Sokie 		 * via SIOCGLIFCONF is not deterministic, this
234*7254Sokie 		 * is largely silliness, but (a) "it's always
235*7254Sokie 		 * been this way", and (b) no one consumes the
236*7254Sokie 		 * interface name in the RARP case anyway.
237*7254Sokie 		 */
238*7254Sokie 
239*7254Sokie 		found_one = B_TRUE;
240*7254Sokie 		(void) strncpy(interface, ifr->lifr_name, LIFNAMSIZ);
241*7254Sokie 		*dhcpflag = (flifr.lifr_flags & IFF_DHCPRUNNING) != 0;
242*7254Sokie 		sin = (struct sockaddr_in *)&ifr->lifr_addr;
243*7254Sokie 		if (isv4 && (sin->sin_addr.s_addr == INADDR_ANY)) {
244*7254Sokie 			/* keep looking for a non-zero address */
245*7254Sokie 			continue;
2465084Sjohnlev 		}
247*7254Sokie 		return (interface);
2485084Sjohnlev 	}
2495084Sjohnlev 
250*7254Sokie 	return (found_one ? interface : NULL);
2515084Sjohnlev }
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate /* ARGSUSED */
2540Sstevel@tonic-gate int
main(int argc,char * argv[])2550Sstevel@tonic-gate main(int argc, char *argv[])
2560Sstevel@tonic-gate {
2575084Sjohnlev 	char *root, *interface, *strategy, dummy;
2585084Sjohnlev 	long len;
259*7254Sokie 	boolean_t dhcp_running = B_FALSE;
2605084Sjohnlev 
2615084Sjohnlev 	root = interface = strategy = NULL;
2625084Sjohnlev 	program = argv[0];
2635084Sjohnlev 
2645084Sjohnlev 	root = get_root_fstype();
2650Sstevel@tonic-gate 
266*7254Sokie 	if (!open_sockets()) {
267*7254Sokie 		(void) fprintf(stderr,
268*7254Sokie 		    "%s: cannot get interface information\n", program);
269*7254Sokie 		return (2);
270*7254Sokie 	}
271*7254Sokie 
2725084Sjohnlev 	/*
2735084Sjohnlev 	 * If diskless, perhaps boot properties were used to configure
2745084Sjohnlev 	 * the interface.
2755084Sjohnlev 	 */
2765084Sjohnlev 	if ((strcmp(root, "nfs") == 0) && boot_properties_present()) {
2775084Sjohnlev 		strategy = "bootprops";
2785084Sjohnlev 
279*7254Sokie 		interface = get_first_interface(&dhcp_running);
2805084Sjohnlev 		if (interface == NULL) {
2815084Sjohnlev 			(void) fprintf(stderr,
2825084Sjohnlev 			    "%s: cannot identify root interface.\n", program);
283*7254Sokie 			close_sockets();
2845084Sjohnlev 			return (2);
2855084Sjohnlev 		}
2865084Sjohnlev 
2875084Sjohnlev 		(void) printf("%s %s %s\n", root, interface, strategy);
288*7254Sokie 		close_sockets();
2895084Sjohnlev 		return (0);
2900Sstevel@tonic-gate 	}
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	/*
2930Sstevel@tonic-gate 	 * Handle the simple case where diskless dhcp tells us everything
2940Sstevel@tonic-gate 	 * we need to know.
2950Sstevel@tonic-gate 	 */
2960Sstevel@tonic-gate 	if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) {
2970Sstevel@tonic-gate 		/* interface is first thing in cache. */
2980Sstevel@tonic-gate 		strategy = "dhcp";
2990Sstevel@tonic-gate 		interface = alloca(len);
3000Sstevel@tonic-gate 		(void) sysinfo(SI_DHCP_CACHE, interface, len);
3010Sstevel@tonic-gate 		(void) printf("%s %s %s\n", root, interface, strategy);
302*7254Sokie 		close_sockets();
3030Sstevel@tonic-gate 		return (0);
3040Sstevel@tonic-gate 	}
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	/*
3070Sstevel@tonic-gate 	 * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle
3080Sstevel@tonic-gate 	 * "ufs rarp" (consumers are coded to deal with this reality), so
3090Sstevel@tonic-gate 	 * there are three possible situations:
3100Sstevel@tonic-gate 	 *
3110Sstevel@tonic-gate 	 *	1. We're "ufs dhcp" if there are any interfaces which have
3120Sstevel@tonic-gate 	 *	   obtained their addresses through DHCP.  That is, if there
3130Sstevel@tonic-gate 	 *	   are any IFF_UP and non-IFF_VIRTUAL interfaces also have
3140Sstevel@tonic-gate 	 *	   IFF_DHCPRUNNING set.
3150Sstevel@tonic-gate 	 *
3160Sstevel@tonic-gate 	 *	2. We're "ufs none" if our filesystem is local and there
3170Sstevel@tonic-gate 	 *	   are no interfaces which have obtained their addresses
3180Sstevel@tonic-gate 	 *	   through DHCP.
3190Sstevel@tonic-gate 	 *
3200Sstevel@tonic-gate 	 *	3. We're "nfs rarp" if our filesystem is remote and there's
3210Sstevel@tonic-gate 	 *	   at least IFF_UP non-IFF_VIRTUAL interface (which there
3220Sstevel@tonic-gate 	 *	   *must* be, since we're running over NFS somehow), then
3230Sstevel@tonic-gate 	 *	   it must be RARP since SI_DHCP_CACHE call above failed.
3240Sstevel@tonic-gate 	 *	   It's too bad there isn't an IFF_RARPRUNNING flag.
3250Sstevel@tonic-gate 	 */
3260Sstevel@tonic-gate 
327*7254Sokie 	interface = get_first_interface(&dhcp_running);
3280Sstevel@tonic-gate 
329*7254Sokie 	if (dhcp_running)
3305084Sjohnlev 		strategy = "dhcp";
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) {
3330Sstevel@tonic-gate 		if (interface == NULL) {
3340Sstevel@tonic-gate 			(void) fprintf(stderr,
3355084Sjohnlev 			    "%s: cannot identify root interface.\n", program);
336*7254Sokie 			close_sockets();
3370Sstevel@tonic-gate 			return (2);
3380Sstevel@tonic-gate 		}
3390Sstevel@tonic-gate 		if (strategy == NULL)
3400Sstevel@tonic-gate 			strategy = "rarp";	/*  must be rarp/bootparams */
3410Sstevel@tonic-gate 	} else {
3420Sstevel@tonic-gate 		if (interface == NULL || strategy == NULL)
3430Sstevel@tonic-gate 			interface = strategy = "none";
3440Sstevel@tonic-gate 	}
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	(void) printf("%s %s %s\n", root, interface, strategy);
347*7254Sokie 	close_sockets();
3480Sstevel@tonic-gate 	return (0);
3490Sstevel@tonic-gate }
350