17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21595aa6e4Smeem 
227c478bd9Sstevel@tonic-gate /*
235d863251Sdme  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*bd93c05dSAlexander Eremin  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * This program does the following:
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  * a) Returns:
337c478bd9Sstevel@tonic-gate  *	0	- if the program successfully determined the net strategy.
347c478bd9Sstevel@tonic-gate  *	!0	- if an error occurred.
357c478bd9Sstevel@tonic-gate  *
367c478bd9Sstevel@tonic-gate  * b) If the program is successful, it prints three tokens to
377c478bd9Sstevel@tonic-gate  *    stdout: <root fs type> <interface name> <net config strategy>.
387c478bd9Sstevel@tonic-gate  *    where:
39*bd93c05dSAlexander Eremin  *	<root fs type>		-	"nfs", "ufs" or "zfs"
407c478bd9Sstevel@tonic-gate  *	<interface name>	-	"hme0" or "none"
41843e1988Sjohnlev  *	<net config strategy>	-	"dhcp", "rarp", "bootprops"
42843e1988Sjohnlev  *					or "none"
437c478bd9Sstevel@tonic-gate  *
447c478bd9Sstevel@tonic-gate  *    Eg:
457c478bd9Sstevel@tonic-gate  *	# /sbin/netstrategy
467c478bd9Sstevel@tonic-gate  *	ufs hme0 dhcp
477c478bd9Sstevel@tonic-gate  *
487c478bd9Sstevel@tonic-gate  *    <root fs type> identifies the system's root file system type.
497c478bd9Sstevel@tonic-gate  *
507c478bd9Sstevel@tonic-gate  *    <interface name> is the 16 char name of the root interface, and is only
517c478bd9Sstevel@tonic-gate  *	set if rarp/dhcp was used to configure the interface.
527c478bd9Sstevel@tonic-gate  *
53843e1988Sjohnlev  *    <net config strategy> can be either "rarp", "dhcp", "bootprops", or
54843e1988Sjohnlev  *	"none" depending on which strategy was used to configure the
55843e1988Sjohnlev  *	interface. Is "none" if no interface was configured using a
56843e1988Sjohnlev  *	net-based strategy.
577c478bd9Sstevel@tonic-gate  *
587c478bd9Sstevel@tonic-gate  * CAVEATS: what about autoclient systems? XXX
59843e1988Sjohnlev  *
60843e1988Sjohnlev  * The logic here must match that in usr/src/uts/common/fs/nfs/nfs_dlinet.c,
61843e1988Sjohnlev  * in particular that code (which implements diskless boot) imposes an
62843e1988Sjohnlev  * ordering on possible ways of configuring network interfaces.
637c478bd9Sstevel@tonic-gate  */
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate #include <stdio.h>
667c478bd9Sstevel@tonic-gate #include <stdlib.h>
677c478bd9Sstevel@tonic-gate #include <unistd.h>
687c478bd9Sstevel@tonic-gate #include <string.h>
697c478bd9Sstevel@tonic-gate #include <sys/types.h>
707c478bd9Sstevel@tonic-gate #include <errno.h>
717c478bd9Sstevel@tonic-gate #include <alloca.h>
727c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h>
737c478bd9Sstevel@tonic-gate #include <sys/socket.h>
747c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
757c478bd9Sstevel@tonic-gate #include <net/if.h>
767c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
77843e1988Sjohnlev #include <libdevinfo.h>
787c478bd9Sstevel@tonic-gate 
79843e1988Sjohnlev static char *program;
80843e1988Sjohnlev 
818acf8916Sokie static int s4, s6;	/* inet and inet6 sockets */
828acf8916Sokie 
838acf8916Sokie static boolean_t
open_sockets(void)848acf8916Sokie open_sockets(void)
858acf8916Sokie {
868acf8916Sokie 	if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
878acf8916Sokie 		(void) fprintf(stderr, "%s: inet socket: %s\n", program,
888acf8916Sokie 		    strerror(errno));
898acf8916Sokie 		return (B_FALSE);
908acf8916Sokie 	}
918acf8916Sokie 	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
928acf8916Sokie 		(void) fprintf(stderr, "%s: inet6 socket: %s\n", program,
938acf8916Sokie 		    strerror(errno));
948acf8916Sokie 		return (B_FALSE);
958acf8916Sokie 	}
968acf8916Sokie 	return (B_TRUE);
978acf8916Sokie }
988acf8916Sokie 
998acf8916Sokie static void
close_sockets(void)1008acf8916Sokie close_sockets(void)
1018acf8916Sokie {
1028acf8916Sokie 	(void) close(s4);
1038acf8916Sokie 	(void) close(s6);
1048acf8916Sokie }
1058acf8916Sokie 
106843e1988Sjohnlev static char *
get_root_fstype()107843e1988Sjohnlev get_root_fstype()
1087c478bd9Sstevel@tonic-gate {
109843e1988Sjohnlev 	static struct statvfs vfs;
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	/* root location */
112843e1988Sjohnlev 	if (statvfs("/", &vfs) < 0) {
113843e1988Sjohnlev 		return ("none");
114843e1988Sjohnlev 	} else {
1157c478bd9Sstevel@tonic-gate 		if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0)
1167c478bd9Sstevel@tonic-gate 			vfs.f_basetype[sizeof ("nfs") - 1] = '\0';
117843e1988Sjohnlev 		return (vfs.f_basetype);
118843e1988Sjohnlev 	}
119843e1988Sjohnlev }
120843e1988Sjohnlev 
121843e1988Sjohnlev /*
122843e1988Sjohnlev  * The following boot properties can be used to configure a network
123843e1988Sjohnlev  * interface in the case of a diskless boot.
124843e1988Sjohnlev  *	host-ip, subnet-mask, server-path, server-name, server-ip.
125843e1988Sjohnlev  *
126843e1988Sjohnlev  * XXX non-diskless case requires "network-interface"?
127843e1988Sjohnlev  */
128843e1988Sjohnlev static boolean_t
boot_properties_present()129843e1988Sjohnlev boot_properties_present()
130843e1988Sjohnlev {
131843e1988Sjohnlev 	/* XXX should use sys/bootprops.h, but it's not delivered */
132843e1988Sjohnlev 	const char *required_properties[] = {
133843e1988Sjohnlev 		"host-ip",
134843e1988Sjohnlev 		"subnet-mask",
135843e1988Sjohnlev 		"server-path",
136843e1988Sjohnlev 		"server-name",
137843e1988Sjohnlev 		"server-ip",
138843e1988Sjohnlev 		NULL,
139843e1988Sjohnlev 	};
140843e1988Sjohnlev 	const char **prop = required_properties;
141843e1988Sjohnlev 	char *prop_value;
142843e1988Sjohnlev 	di_node_t dn;
143843e1988Sjohnlev 
144843e1988Sjohnlev 	if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
145843e1988Sjohnlev 		(void) fprintf(stderr, "%s: di_init: %s\n", program,
146843e1988Sjohnlev 		    strerror(errno));
147843e1988Sjohnlev 		di_fini(dn);
148843e1988Sjohnlev 		return (B_FALSE);
149843e1988Sjohnlev 	}
150843e1988Sjohnlev 
151843e1988Sjohnlev 	while (*prop != NULL) {
152843e1988Sjohnlev 		if (di_prop_lookup_strings(DDI_DEV_T_ANY,
153843e1988Sjohnlev 		    dn, *prop, &prop_value) != 1) {
154843e1988Sjohnlev 			di_fini(dn);
155843e1988Sjohnlev 			return (B_FALSE);
156843e1988Sjohnlev 		}
157843e1988Sjohnlev 		prop++;
158843e1988Sjohnlev 	}
159843e1988Sjohnlev 	di_fini(dn);
160843e1988Sjohnlev 
161843e1988Sjohnlev 	return (B_TRUE);
162843e1988Sjohnlev }
163843e1988Sjohnlev 
164843e1988Sjohnlev static char *
get_first_interface(boolean_t * dhcpflag)1658acf8916Sokie get_first_interface(boolean_t *dhcpflag)
166843e1988Sjohnlev {
167843e1988Sjohnlev 	struct lifnum ifnum;
168843e1988Sjohnlev 	struct lifconf ifconf;
169843e1988Sjohnlev 	struct lifreq *ifr;
1708acf8916Sokie 	static char interface[LIFNAMSIZ];
1718acf8916Sokie 	boolean_t isv4, found_one = B_FALSE;
172843e1988Sjohnlev 
173843e1988Sjohnlev 	ifnum.lifn_family = AF_UNSPEC;
1745d863251Sdme 	ifnum.lifn_flags = 0;
1755d863251Sdme 	ifnum.lifn_count = 0;
176843e1988Sjohnlev 
1778acf8916Sokie 	if (ioctl(s4, SIOCGLIFNUM, &ifnum) < 0) {
178843e1988Sjohnlev 		(void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program,
179843e1988Sjohnlev 		    strerror(errno));
180843e1988Sjohnlev 		return (NULL);
181843e1988Sjohnlev 	}
182843e1988Sjohnlev 
183843e1988Sjohnlev 	ifconf.lifc_family = AF_UNSPEC;
1845d863251Sdme 	ifconf.lifc_flags = 0;
185843e1988Sjohnlev 	ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq);
186843e1988Sjohnlev 	ifconf.lifc_buf = alloca(ifconf.lifc_len);
187843e1988Sjohnlev 
1888acf8916Sokie 	if (ioctl(s4, SIOCGLIFCONF, &ifconf) < 0) {
189843e1988Sjohnlev 		(void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program,
190843e1988Sjohnlev 		    strerror(errno));
191843e1988Sjohnlev 		return (NULL);
192843e1988Sjohnlev 	}
193843e1988Sjohnlev 
194843e1988Sjohnlev 	for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len /
195843e1988Sjohnlev 	    sizeof (ifconf.lifc_req[0])]; ifr++) {
1968acf8916Sokie 		struct lifreq flifr;
1978acf8916Sokie 		struct sockaddr_in *sin;
198843e1988Sjohnlev 
199843e1988Sjohnlev 		if (strchr(ifr->lifr_name, ':') != NULL)
200843e1988Sjohnlev 			continue;	/* skip logical interfaces */
201843e1988Sjohnlev 
2028acf8916Sokie 		isv4 = ifr->lifr_addr.ss_family == AF_INET;
2038acf8916Sokie 
2048acf8916Sokie 		(void) strncpy(flifr.lifr_name, ifr->lifr_name, LIFNAMSIZ);
2058acf8916Sokie 
2068acf8916Sokie 		if (ioctl(isv4 ? s4 : s6, SIOCGLIFFLAGS, &flifr) < 0) {
2078acf8916Sokie 			(void) fprintf(stderr, "%s: SIOCGLIFFLAGS: %s\n",
208843e1988Sjohnlev 			    program, strerror(errno));
209843e1988Sjohnlev 			continue;
210843e1988Sjohnlev 		}
211843e1988Sjohnlev 
2128acf8916Sokie 		if (!(flifr.lifr_flags & IFF_UP) ||
2138acf8916Sokie 		    (flifr.lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT)))
214843e1988Sjohnlev 			continue;
215843e1988Sjohnlev 
2168acf8916Sokie 		/*
2178acf8916Sokie 		 * For the "nfs rarp" and "nfs bootprops"
2188acf8916Sokie 		 * cases, we assume that the first non-virtual
2198acf8916Sokie 		 * IFF_UP interface with a non-zero address is
2208acf8916Sokie 		 * the one used.
2218acf8916Sokie 		 *
2228acf8916Sokie 		 * For the non-zero address check, we only check
2238acf8916Sokie 		 * v4 interfaces, as it's not possible to set the
2248acf8916Sokie 		 * the first logical interface (the only ones we
2258acf8916Sokie 		 * look at here) to ::0; that interface must have
2268acf8916Sokie 		 * a link-local address.
2278acf8916Sokie 		 *
2288acf8916Sokie 		 * If we don't find an IFF_UP interface with a
2298acf8916Sokie 		 * non-zero address, we'll return the last IFF_UP
2308acf8916Sokie 		 * interface seen.
2318acf8916Sokie 		 *
2328acf8916Sokie 		 * Since the order of the interfaces retrieved
2338acf8916Sokie 		 * via SIOCGLIFCONF is not deterministic, this
2348acf8916Sokie 		 * is largely silliness, but (a) "it's always
2358acf8916Sokie 		 * been this way", and (b) no one consumes the
2368acf8916Sokie 		 * interface name in the RARP case anyway.
2378acf8916Sokie 		 */
2388acf8916Sokie 
2398acf8916Sokie 		found_one = B_TRUE;
2408acf8916Sokie 		(void) strncpy(interface, ifr->lifr_name, LIFNAMSIZ);
2418acf8916Sokie 		*dhcpflag = (flifr.lifr_flags & IFF_DHCPRUNNING) != 0;
2428acf8916Sokie 		sin = (struct sockaddr_in *)&ifr->lifr_addr;
2438acf8916Sokie 		if (isv4 && (sin->sin_addr.s_addr == INADDR_ANY)) {
2448acf8916Sokie 			/* keep looking for a non-zero address */
2458acf8916Sokie 			continue;
246843e1988Sjohnlev 		}
2478acf8916Sokie 		return (interface);
248843e1988Sjohnlev 	}
249843e1988Sjohnlev 
2508acf8916Sokie 	return (found_one ? interface : NULL);
251843e1988Sjohnlev }
252843e1988Sjohnlev 
253843e1988Sjohnlev /* ARGSUSED */
254843e1988Sjohnlev int
main(int argc,char * argv[])255843e1988Sjohnlev main(int argc, char *argv[])
256843e1988Sjohnlev {
257843e1988Sjohnlev 	char *root, *interface, *strategy, dummy;
258843e1988Sjohnlev 	long len;
2598acf8916Sokie 	boolean_t dhcp_running = B_FALSE;
260843e1988Sjohnlev 
261843e1988Sjohnlev 	root = interface = strategy = NULL;
262843e1988Sjohnlev 	program = argv[0];
263843e1988Sjohnlev 
264843e1988Sjohnlev 	root = get_root_fstype();
265843e1988Sjohnlev 
2668acf8916Sokie 	if (!open_sockets()) {
2678acf8916Sokie 		(void) fprintf(stderr,
2688acf8916Sokie 		    "%s: cannot get interface information\n", program);
2698acf8916Sokie 		return (2);
2708acf8916Sokie 	}
2718acf8916Sokie 
272843e1988Sjohnlev 	/*
273843e1988Sjohnlev 	 * If diskless, perhaps boot properties were used to configure
274843e1988Sjohnlev 	 * the interface.
275843e1988Sjohnlev 	 */
276843e1988Sjohnlev 	if ((strcmp(root, "nfs") == 0) && boot_properties_present()) {
277843e1988Sjohnlev 		strategy = "bootprops";
278843e1988Sjohnlev 
2798acf8916Sokie 		interface = get_first_interface(&dhcp_running);
280843e1988Sjohnlev 		if (interface == NULL) {
281843e1988Sjohnlev 			(void) fprintf(stderr,
282843e1988Sjohnlev 			    "%s: cannot identify root interface.\n", program);
2838acf8916Sokie 			close_sockets();
284843e1988Sjohnlev 			return (2);
285843e1988Sjohnlev 		}
286843e1988Sjohnlev 
287843e1988Sjohnlev 		(void) printf("%s %s %s\n", root, interface, strategy);
2888acf8916Sokie 		close_sockets();
289843e1988Sjohnlev 		return (0);
2907c478bd9Sstevel@tonic-gate 	}
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	/*
2937c478bd9Sstevel@tonic-gate 	 * Handle the simple case where diskless dhcp tells us everything
2947c478bd9Sstevel@tonic-gate 	 * we need to know.
2957c478bd9Sstevel@tonic-gate 	 */
2967c478bd9Sstevel@tonic-gate 	if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) {
2977c478bd9Sstevel@tonic-gate 		/* interface is first thing in cache. */
2987c478bd9Sstevel@tonic-gate 		strategy = "dhcp";
2997c478bd9Sstevel@tonic-gate 		interface = alloca(len);
3007c478bd9Sstevel@tonic-gate 		(void) sysinfo(SI_DHCP_CACHE, interface, len);
3017c478bd9Sstevel@tonic-gate 		(void) printf("%s %s %s\n", root, interface, strategy);
3028acf8916Sokie 		close_sockets();
3037c478bd9Sstevel@tonic-gate 		return (0);
3047c478bd9Sstevel@tonic-gate 	}
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	/*
3077c478bd9Sstevel@tonic-gate 	 * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle
3087c478bd9Sstevel@tonic-gate 	 * "ufs rarp" (consumers are coded to deal with this reality), so
3097c478bd9Sstevel@tonic-gate 	 * there are three possible situations:
3107c478bd9Sstevel@tonic-gate 	 *
311*bd93c05dSAlexander Eremin 	 *	1. We're either "ufs dhcp" or "zfs dhcp" if there are any
312*bd93c05dSAlexander Eremin 	 *	   interfaces which have obtained their addresses through DHCP.
313*bd93c05dSAlexander Eremin 	 *	   That is, if there are any IFF_UP and non-IFF_VIRTUAL
314*bd93c05dSAlexander Eremin 	 *	   interfaces also have IFF_DHCPRUNNING set.
3157c478bd9Sstevel@tonic-gate 	 *
316*bd93c05dSAlexander Eremin 	 *	2. We're either "ufs none" or "zfs none" if our filesystem
317*bd93c05dSAlexander Eremin 	 *	   is local and there are no interfaces which have obtained
318*bd93c05dSAlexander Eremin 	 *	   their addresses through DHCP.
3197c478bd9Sstevel@tonic-gate 	 *
3207c478bd9Sstevel@tonic-gate 	 *	3. We're "nfs rarp" if our filesystem is remote and there's
3217c478bd9Sstevel@tonic-gate 	 *	   at least IFF_UP non-IFF_VIRTUAL interface (which there
3227c478bd9Sstevel@tonic-gate 	 *	   *must* be, since we're running over NFS somehow), then
3237c478bd9Sstevel@tonic-gate 	 *	   it must be RARP since SI_DHCP_CACHE call above failed.
3247c478bd9Sstevel@tonic-gate 	 *	   It's too bad there isn't an IFF_RARPRUNNING flag.
3257c478bd9Sstevel@tonic-gate 	 */
3267c478bd9Sstevel@tonic-gate 
3278acf8916Sokie 	interface = get_first_interface(&dhcp_running);
3287c478bd9Sstevel@tonic-gate 
3298acf8916Sokie 	if (dhcp_running)
330843e1988Sjohnlev 		strategy = "dhcp";
3317c478bd9Sstevel@tonic-gate 
332*bd93c05dSAlexander Eremin 	if (strcmp(root, "nfs") == 0) {
3337c478bd9Sstevel@tonic-gate 		if (interface == NULL) {
3347c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
335843e1988Sjohnlev 			    "%s: cannot identify root interface.\n", program);
3368acf8916Sokie 			close_sockets();
3377c478bd9Sstevel@tonic-gate 			return (2);
3387c478bd9Sstevel@tonic-gate 		}
3397c478bd9Sstevel@tonic-gate 		if (strategy == NULL)
3407c478bd9Sstevel@tonic-gate 			strategy = "rarp";	/*  must be rarp/bootparams */
3417c478bd9Sstevel@tonic-gate 	} else {
3427c478bd9Sstevel@tonic-gate 		if (interface == NULL || strategy == NULL)
3437c478bd9Sstevel@tonic-gate 			interface = strategy = "none";
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	(void) printf("%s %s %s\n", root, interface, strategy);
3478acf8916Sokie 	close_sockets();
3487c478bd9Sstevel@tonic-gate 	return (0);
3497c478bd9Sstevel@tonic-gate }
350