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