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