Home | History | Annotate | Download | only in netstrategy
      1     0   stevel /*
      2     0   stevel  * CDDL HEADER START
      3     0   stevel  *
      4     0   stevel  * The contents of this file are subject to the terms of the
      5  5084  johnlev  * Common Development and Distribution License (the "License").
      6  5084  johnlev  * You may not use this file except in compliance with the License.
      7     0   stevel  *
      8     0   stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9     0   stevel  * or http://www.opensolaris.org/os/licensing.
     10     0   stevel  * See the License for the specific language governing permissions
     11     0   stevel  * and limitations under the License.
     12     0   stevel  *
     13     0   stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14     0   stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15     0   stevel  * If applicable, add the following below this CDDL HEADER, with the
     16     0   stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17     0   stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18     0   stevel  *
     19     0   stevel  * CDDL HEADER END
     20     0   stevel  */
     21   761     meem 
     22     0   stevel /*
     23  7170      dme  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24     0   stevel  * Use is subject to license terms.
     25     0   stevel  */
     26     0   stevel 
     27     0   stevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28     0   stevel 
     29     0   stevel /*
     30     0   stevel  * This program does the following:
     31     0   stevel  *
     32     0   stevel  * a) Returns:
     33     0   stevel  *	0	- if the program successfully determined the net strategy.
     34     0   stevel  *	!0	- if an error occurred.
     35     0   stevel  *
     36     0   stevel  * b) If the program is successful, it prints three tokens to
     37     0   stevel  *    stdout: <root fs type> <interface name> <net config strategy>.
     38     0   stevel  *    where:
     39     0   stevel  *	<root fs type>		-	"nfs" or "ufs"
     40     0   stevel  *	<interface name>	-	"hme0" or "none"
     41  5084  johnlev  *	<net config strategy>	-	"dhcp", "rarp", "bootprops"
     42  5084  johnlev  *					or "none"
     43     0   stevel  *
     44     0   stevel  *    Eg:
     45     0   stevel  *	# /sbin/netstrategy
     46     0   stevel  *	ufs hme0 dhcp
     47     0   stevel  *
     48     0   stevel  *    <root fs type> identifies the system's root file system type.
     49     0   stevel  *
     50     0   stevel  *    <interface name> is the 16 char name of the root interface, and is only
     51     0   stevel  *	set if rarp/dhcp was used to configure the interface.
     52     0   stevel  *
     53  5084  johnlev  *    <net config strategy> can be either "rarp", "dhcp", "bootprops", or
     54  5084  johnlev  *	"none" depending on which strategy was used to configure the
     55  5084  johnlev  *	interface. Is "none" if no interface was configured using a
     56  5084  johnlev  *	net-based strategy.
     57     0   stevel  *
     58     0   stevel  * CAVEATS: what about autoclient systems? XXX
     59  5084  johnlev  *
     60  5084  johnlev  * The logic here must match that in usr/src/uts/common/fs/nfs/nfs_dlinet.c,
     61  5084  johnlev  * in particular that code (which implements diskless boot) imposes an
     62  5084  johnlev  * ordering on possible ways of configuring network interfaces.
     63     0   stevel  */
     64     0   stevel 
     65     0   stevel #include <stdio.h>
     66     0   stevel #include <stdlib.h>
     67     0   stevel #include <unistd.h>
     68     0   stevel #include <string.h>
     69     0   stevel #include <sys/types.h>
     70     0   stevel #include <errno.h>
     71     0   stevel #include <alloca.h>
     72     0   stevel #include <sys/systeminfo.h>
     73     0   stevel #include <sys/socket.h>
     74     0   stevel #include <sys/sockio.h>
     75     0   stevel #include <net/if.h>
     76     0   stevel #include <sys/statvfs.h>
     77  5084  johnlev #include <libdevinfo.h>
     78  5084  johnlev 
     79  5084  johnlev static char *program;
     80  5084  johnlev 
     81  7254     okie static int s4, s6;	/* inet and inet6 sockets */
     82  7254     okie 
     83  7254     okie static boolean_t
     84  7254     okie open_sockets(void)
     85  7254     okie {
     86  7254     okie 	if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
     87  7254     okie 		(void) fprintf(stderr, "%s: inet socket: %s\n", program,
     88  7254     okie 		    strerror(errno));
     89  7254     okie 		return (B_FALSE);
     90  7254     okie 	}
     91  7254     okie 	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
     92  7254     okie 		(void) fprintf(stderr, "%s: inet6 socket: %s\n", program,
     93  7254     okie 		    strerror(errno));
     94  7254     okie 		return (B_FALSE);
     95  7254     okie 	}
     96  7254     okie 	return (B_TRUE);
     97  7254     okie }
     98  7254     okie 
     99  7254     okie static void
    100  7254     okie close_sockets(void)
    101  7254     okie {
    102  7254     okie 	(void) close(s4);
    103  7254     okie 	(void) close(s6);
    104  7254     okie }
    105  7254     okie 
    106  5084  johnlev static char *
    107  5084  johnlev get_root_fstype()
    108  5084  johnlev {
    109  5084  johnlev 	static struct statvfs vfs;
    110  5084  johnlev 
    111  5084  johnlev 	/* root location */
    112  5084  johnlev 	if (statvfs("/", &vfs) < 0) {
    113  5084  johnlev 		return ("none");
    114  5084  johnlev 	} else {
    115  5084  johnlev 		if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0)
    116  5084  johnlev 			vfs.f_basetype[sizeof ("nfs") - 1] = '\0';
    117  5084  johnlev 		return (vfs.f_basetype);
    118  5084  johnlev 	}
    119  5084  johnlev }
    120  5084  johnlev 
    121  5084  johnlev /*
    122  5084  johnlev  * The following boot properties can be used to configure a network
    123  5084  johnlev  * interface in the case of a diskless boot.
    124  5084  johnlev  *	host-ip, subnet-mask, server-path, server-name, server-ip.
    125  5084  johnlev  *
    126  5084  johnlev  * XXX non-diskless case requires "network-interface"?
    127  5084  johnlev  */
    128  5084  johnlev static boolean_t
    129  5084  johnlev boot_properties_present()
    130  5084  johnlev {
    131  5084  johnlev 	/* XXX should use sys/bootprops.h, but it's not delivered */
    132  5084  johnlev 	const char *required_properties[] = {
    133  5084  johnlev 		"host-ip",
    134  5084  johnlev 		"subnet-mask",
    135  5084  johnlev 		"server-path",
    136  5084  johnlev 		"server-name",
    137  5084  johnlev 		"server-ip",
    138  5084  johnlev 		NULL,
    139  5084  johnlev 	};
    140  5084  johnlev 	const char **prop = required_properties;
    141  5084  johnlev 	char *prop_value;
    142  5084  johnlev 	di_node_t dn;
    143  5084  johnlev 
    144  5084  johnlev 	if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
    145  5084  johnlev 		(void) fprintf(stderr, "%s: di_init: %s\n", program,
    146  5084  johnlev 		    strerror(errno));
    147  5084  johnlev 		di_fini(dn);
    148  5084  johnlev 		return (B_FALSE);
    149  5084  johnlev 	}
    150  5084  johnlev 
    151  5084  johnlev 	while (*prop != NULL) {
    152  5084  johnlev 		if (di_prop_lookup_strings(DDI_DEV_T_ANY,
    153  5084  johnlev 		    dn, *prop, &prop_value) != 1) {
    154  5084  johnlev 			di_fini(dn);
    155  5084  johnlev 			return (B_FALSE);
    156  5084  johnlev 		}
    157  5084  johnlev 		prop++;
    158  5084  johnlev 	}
    159  5084  johnlev 	di_fini(dn);
    160  5084  johnlev 
    161  5084  johnlev 	return (B_TRUE);
    162  5084  johnlev }
    163  5084  johnlev 
    164  5084  johnlev static char *
    165  7254     okie get_first_interface(boolean_t *dhcpflag)
    166  5084  johnlev {
    167  5084  johnlev 	struct lifnum ifnum;
    168  5084  johnlev 	struct lifconf ifconf;
    169  5084  johnlev 	struct lifreq *ifr;
    170  7254     okie 	static char interface[LIFNAMSIZ];
    171  7254     okie 	boolean_t isv4, found_one = B_FALSE;
    172  5084  johnlev 
    173  5084  johnlev 	ifnum.lifn_family = AF_UNSPEC;
    174  7170      dme 	ifnum.lifn_flags = 0;
    175  7170      dme 	ifnum.lifn_count = 0;
    176  5084  johnlev 
    177  7254     okie 	if (ioctl(s4, SIOCGLIFNUM, &ifnum) < 0) {
    178  5084  johnlev 		(void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program,
    179  5084  johnlev 		    strerror(errno));
    180  5084  johnlev 		return (NULL);
    181  5084  johnlev 	}
    182  5084  johnlev 
    183  5084  johnlev 	ifconf.lifc_family = AF_UNSPEC;
    184  7170      dme 	ifconf.lifc_flags = 0;
    185  5084  johnlev 	ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq);
    186  5084  johnlev 	ifconf.lifc_buf = alloca(ifconf.lifc_len);
    187  5084  johnlev 
    188  7254     okie 	if (ioctl(s4, SIOCGLIFCONF, &ifconf) < 0) {
    189  5084  johnlev 		(void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program,
    190  5084  johnlev 		    strerror(errno));
    191  5084  johnlev 		return (NULL);
    192  5084  johnlev 	}
    193  5084  johnlev 
    194  5084  johnlev 	for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len /
    195  5084  johnlev 	    sizeof (ifconf.lifc_req[0])]; ifr++) {
    196  7254     okie 		struct lifreq flifr;
    197  7254     okie 		struct sockaddr_in *sin;
    198  5084  johnlev 
    199  5084  johnlev 		if (strchr(ifr->lifr_name, ':') != NULL)
    200  5084  johnlev 			continue;	/* skip logical interfaces */
    201  5084  johnlev 
    202  7254     okie 		isv4 = ifr->lifr_addr.ss_family == AF_INET;
    203  7254     okie 
    204  7254     okie 		(void) strncpy(flifr.lifr_name, ifr->lifr_name, LIFNAMSIZ);
    205  7254     okie 
    206  7254     okie 		if (ioctl(isv4 ? s4 : s6, SIOCGLIFFLAGS, &flifr) < 0) {
    207  7254     okie 			(void) fprintf(stderr, "%s: SIOCGLIFFLAGS: %s\n",
    208  5084  johnlev 			    program, strerror(errno));
    209  5084  johnlev 			continue;
    210  5084  johnlev 		}
    211  5084  johnlev 
    212  7254     okie 		if (!(flifr.lifr_flags & IFF_UP) ||
    213  7254     okie 		    (flifr.lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT)))
    214  5084  johnlev 			continue;
    215  5084  johnlev 
    216  7254     okie 		/*
    217  7254     okie 		 * For the "nfs rarp" and "nfs bootprops"
    218  7254     okie 		 * cases, we assume that the first non-virtual
    219  7254     okie 		 * IFF_UP interface with a non-zero address is
    220  7254     okie 		 * the one used.
    221  7254     okie 		 *
    222  7254     okie 		 * For the non-zero address check, we only check
    223  7254     okie 		 * v4 interfaces, as it's not possible to set the
    224  7254     okie 		 * the first logical interface (the only ones we
    225  7254     okie 		 * look at here) to ::0; that interface must have
    226  7254     okie 		 * a link-local address.
    227  7254     okie 		 *
    228  7254     okie 		 * If we don't find an IFF_UP interface with a
    229  7254     okie 		 * non-zero address, we'll return the last IFF_UP
    230  7254     okie 		 * interface seen.
    231  7254     okie 		 *
    232  7254     okie 		 * Since the order of the interfaces retrieved
    233  7254     okie 		 * via SIOCGLIFCONF is not deterministic, this
    234  7254     okie 		 * is largely silliness, but (a) "it's always
    235  7254     okie 		 * been this way", and (b) no one consumes the
    236  7254     okie 		 * interface name in the RARP case anyway.
    237  7254     okie 		 */
    238  7254     okie 
    239  7254     okie 		found_one = B_TRUE;
    240  7254     okie 		(void) strncpy(interface, ifr->lifr_name, LIFNAMSIZ);
    241  7254     okie 		*dhcpflag = (flifr.lifr_flags & IFF_DHCPRUNNING) != 0;
    242  7254     okie 		sin = (struct sockaddr_in *)&ifr->lifr_addr;
    243  7254     okie 		if (isv4 && (sin->sin_addr.s_addr == INADDR_ANY)) {
    244  7254     okie 			/* keep looking for a non-zero address */
    245  7254     okie 			continue;
    246  5084  johnlev 		}
    247  7254     okie 		return (interface);
    248  5084  johnlev 	}
    249  5084  johnlev 
    250  7254     okie 	return (found_one ? interface : NULL);
    251  5084  johnlev }
    252     0   stevel 
    253     0   stevel /* ARGSUSED */
    254     0   stevel int
    255     0   stevel main(int argc, char *argv[])
    256     0   stevel {
    257  5084  johnlev 	char *root, *interface, *strategy, dummy;
    258  5084  johnlev 	long len;
    259  7254     okie 	boolean_t dhcp_running = B_FALSE;
    260     0   stevel 
    261  5084  johnlev 	root = interface = strategy = NULL;
    262  5084  johnlev 	program = argv[0];
    263  5084  johnlev 
    264  5084  johnlev 	root = get_root_fstype();
    265  7254     okie 
    266  7254     okie 	if (!open_sockets()) {
    267  7254     okie 		(void) fprintf(stderr,
    268  7254     okie 		    "%s: cannot get interface information\n", program);
    269  7254     okie 		return (2);
    270  7254     okie 	}
    271  5084  johnlev 
    272  5084  johnlev 	/*
    273  5084  johnlev 	 * If diskless, perhaps boot properties were used to configure
    274  5084  johnlev 	 * the interface.
    275  5084  johnlev 	 */
    276  5084  johnlev 	if ((strcmp(root, "nfs") == 0) && boot_properties_present()) {
    277  5084  johnlev 		strategy = "bootprops";
    278  5084  johnlev 
    279  7254     okie 		interface = get_first_interface(&dhcp_running);
    280  5084  johnlev 		if (interface == NULL) {
    281  5084  johnlev 			(void) fprintf(stderr,
    282  5084  johnlev 			    "%s: cannot identify root interface.\n", program);
    283  7254     okie 			close_sockets();
    284  5084  johnlev 			return (2);
    285  5084  johnlev 		}
    286  5084  johnlev 
    287  5084  johnlev 		(void) printf("%s %s %s\n", root, interface, strategy);
    288  7254     okie 		close_sockets();
    289  5084  johnlev 		return (0);
    290     0   stevel 	}
    291     0   stevel 
    292     0   stevel 	/*
    293     0   stevel 	 * Handle the simple case where diskless dhcp tells us everything
    294     0   stevel 	 * we need to know.
    295     0   stevel 	 */
    296     0   stevel 	if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) {
    297     0   stevel 		/* interface is first thing in cache. */
    298     0   stevel 		strategy = "dhcp";
    299     0   stevel 		interface = alloca(len);
    300     0   stevel 		(void) sysinfo(SI_DHCP_CACHE, interface, len);
    301     0   stevel 		(void) printf("%s %s %s\n", root, interface, strategy);
    302  7254     okie 		close_sockets();
    303     0   stevel 		return (0);
    304     0   stevel 	}
    305     0   stevel 
    306     0   stevel 	/*
    307     0   stevel 	 * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle
    308     0   stevel 	 * "ufs rarp" (consumers are coded to deal with this reality), so
    309     0   stevel 	 * there are three possible situations:
    310     0   stevel 	 *
    311     0   stevel 	 *	1. We're "ufs dhcp" if there are any interfaces which have
    312     0   stevel 	 *	   obtained their addresses through DHCP.  That is, if there
    313     0   stevel 	 *	   are any IFF_UP and non-IFF_VIRTUAL interfaces also have
    314     0   stevel 	 *	   IFF_DHCPRUNNING set.
    315     0   stevel 	 *
    316     0   stevel 	 *	2. We're "ufs none" if our filesystem is local and there
    317     0   stevel 	 *	   are no interfaces which have obtained their addresses
    318     0   stevel 	 *	   through DHCP.
    319     0   stevel 	 *
    320     0   stevel 	 *	3. We're "nfs rarp" if our filesystem is remote and there's
    321     0   stevel 	 *	   at least IFF_UP non-IFF_VIRTUAL interface (which there
    322     0   stevel 	 *	   *must* be, since we're running over NFS somehow), then
    323     0   stevel 	 *	   it must be RARP since SI_DHCP_CACHE call above failed.
    324     0   stevel 	 *	   It's too bad there isn't an IFF_RARPRUNNING flag.
    325     0   stevel 	 */
    326     0   stevel 
    327  7254     okie 	interface = get_first_interface(&dhcp_running);
    328     0   stevel 
    329  7254     okie 	if (dhcp_running)
    330  5084  johnlev 		strategy = "dhcp";
    331     0   stevel 
    332     0   stevel 	if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) {
    333     0   stevel 		if (interface == NULL) {
    334     0   stevel 			(void) fprintf(stderr,
    335  5084  johnlev 			    "%s: cannot identify root interface.\n", program);
    336  7254     okie 			close_sockets();
    337     0   stevel 			return (2);
    338     0   stevel 		}
    339     0   stevel 		if (strategy == NULL)
    340     0   stevel 			strategy = "rarp";	/*  must be rarp/bootparams */
    341     0   stevel 	} else {
    342     0   stevel 		if (interface == NULL || strategy == NULL)
    343     0   stevel 			interface = strategy = "none";
    344     0   stevel 	}
    345     0   stevel 
    346     0   stevel 	(void) printf("%s %s %s\n", root, interface, strategy);
    347  7254     okie 	close_sockets();
    348     0   stevel 	return (0);
    349     0   stevel }
    350