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 /* 23*7170Sdme * 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 815084Sjohnlev static char * 825084Sjohnlev get_root_fstype() 835084Sjohnlev { 845084Sjohnlev static struct statvfs vfs; 855084Sjohnlev 865084Sjohnlev /* root location */ 875084Sjohnlev if (statvfs("/", &vfs) < 0) { 885084Sjohnlev return ("none"); 895084Sjohnlev } else { 905084Sjohnlev if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0) 915084Sjohnlev vfs.f_basetype[sizeof ("nfs") - 1] = '\0'; 925084Sjohnlev return (vfs.f_basetype); 935084Sjohnlev } 945084Sjohnlev } 955084Sjohnlev 965084Sjohnlev /* 975084Sjohnlev * The following boot properties can be used to configure a network 985084Sjohnlev * interface in the case of a diskless boot. 995084Sjohnlev * host-ip, subnet-mask, server-path, server-name, server-ip. 1005084Sjohnlev * 1015084Sjohnlev * XXX non-diskless case requires "network-interface"? 1025084Sjohnlev */ 1035084Sjohnlev static boolean_t 1045084Sjohnlev boot_properties_present() 1055084Sjohnlev { 1065084Sjohnlev /* XXX should use sys/bootprops.h, but it's not delivered */ 1075084Sjohnlev const char *required_properties[] = { 1085084Sjohnlev "host-ip", 1095084Sjohnlev "subnet-mask", 1105084Sjohnlev "server-path", 1115084Sjohnlev "server-name", 1125084Sjohnlev "server-ip", 1135084Sjohnlev NULL, 1145084Sjohnlev }; 1155084Sjohnlev const char **prop = required_properties; 1165084Sjohnlev char *prop_value; 1175084Sjohnlev di_node_t dn; 1185084Sjohnlev 1195084Sjohnlev if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) { 1205084Sjohnlev (void) fprintf(stderr, "%s: di_init: %s\n", program, 1215084Sjohnlev strerror(errno)); 1225084Sjohnlev di_fini(dn); 1235084Sjohnlev return (B_FALSE); 1245084Sjohnlev } 1255084Sjohnlev 1265084Sjohnlev while (*prop != NULL) { 1275084Sjohnlev if (di_prop_lookup_strings(DDI_DEV_T_ANY, 1285084Sjohnlev dn, *prop, &prop_value) != 1) { 1295084Sjohnlev di_fini(dn); 1305084Sjohnlev return (B_FALSE); 1315084Sjohnlev } 1325084Sjohnlev prop++; 1335084Sjohnlev } 1345084Sjohnlev di_fini(dn); 1355084Sjohnlev 1365084Sjohnlev return (B_TRUE); 1375084Sjohnlev } 1385084Sjohnlev 1395084Sjohnlev static char * 1405084Sjohnlev get_first_interface() 1415084Sjohnlev { 1425084Sjohnlev int fd; 1435084Sjohnlev struct lifnum ifnum; 1445084Sjohnlev struct lifconf ifconf; 1455084Sjohnlev struct lifreq *ifr; 1465084Sjohnlev static char interface[IFNAMSIZ]; 1475084Sjohnlev 1485084Sjohnlev if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 1495084Sjohnlev (void) fprintf(stderr, "%s: socket: %s\n", program, 1505084Sjohnlev strerror(errno)); 1515084Sjohnlev return (NULL); 1525084Sjohnlev } 1535084Sjohnlev 1545084Sjohnlev ifnum.lifn_family = AF_UNSPEC; 155*7170Sdme ifnum.lifn_flags = 0; 156*7170Sdme ifnum.lifn_count = 0; 1575084Sjohnlev 1585084Sjohnlev if (ioctl(fd, SIOCGLIFNUM, &ifnum) < 0) { 1595084Sjohnlev (void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program, 1605084Sjohnlev strerror(errno)); 1615084Sjohnlev (void) close(fd); 1625084Sjohnlev return (NULL); 1635084Sjohnlev } 1645084Sjohnlev 1655084Sjohnlev ifconf.lifc_family = AF_UNSPEC; 166*7170Sdme ifconf.lifc_flags = 0; 1675084Sjohnlev ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq); 1685084Sjohnlev ifconf.lifc_buf = alloca(ifconf.lifc_len); 1695084Sjohnlev 1705084Sjohnlev if (ioctl(fd, SIOCGLIFCONF, &ifconf) < 0) { 1715084Sjohnlev (void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program, 1725084Sjohnlev strerror(errno)); 1735084Sjohnlev (void) close(fd); 1745084Sjohnlev return (NULL); 1755084Sjohnlev } 1765084Sjohnlev 1775084Sjohnlev for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len / 1785084Sjohnlev sizeof (ifconf.lifc_req[0])]; ifr++) { 1795084Sjohnlev 1805084Sjohnlev if (strchr(ifr->lifr_name, ':') != NULL) 1815084Sjohnlev continue; /* skip logical interfaces */ 1825084Sjohnlev 1835084Sjohnlev if (ioctl(fd, SIOCGLIFFLAGS, ifr) < 0) { 1845084Sjohnlev (void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n", 1855084Sjohnlev program, strerror(errno)); 1865084Sjohnlev continue; 1875084Sjohnlev } 1885084Sjohnlev 1895084Sjohnlev if (ifr->lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT)) 1905084Sjohnlev continue; 1915084Sjohnlev 1925084Sjohnlev if (ifr->lifr_flags & IFF_UP) { 1935084Sjohnlev /* 1945084Sjohnlev * For the "nfs rarp" and "nfs bootprops" 1955084Sjohnlev * cases, we assume that the first non-virtual 1965084Sjohnlev * IFF_UP interface is the one used. 1975084Sjohnlev * 1985084Sjohnlev * Since the order of the interfaces retrieved 1995084Sjohnlev * via SIOCGLIFCONF is not deterministic, this 2005084Sjohnlev * is largely silliness, but (a) "it's always 2015084Sjohnlev * been this way", (b) machines booted this 2025084Sjohnlev * way typically only have one interface, and 2035084Sjohnlev * (c) no one consumes the interface name in 2045084Sjohnlev * the RARP case anyway. 2055084Sjohnlev */ 2065084Sjohnlev (void) strncpy(interface, ifr->lifr_name, IFNAMSIZ); 2075084Sjohnlev (void) close(fd); 2085084Sjohnlev return (interface); 2095084Sjohnlev } 2105084Sjohnlev } 2115084Sjohnlev 2125084Sjohnlev (void) close(fd); 2135084Sjohnlev 2145084Sjohnlev return (NULL); 2155084Sjohnlev } 2165084Sjohnlev 2175084Sjohnlev /* 2185084Sjohnlev * Is DHCP running on the specified interface? 2195084Sjohnlev */ 2205084Sjohnlev static boolean_t 2215084Sjohnlev check_dhcp_running(char *interface) 2225084Sjohnlev { 2235084Sjohnlev int fd; 2245084Sjohnlev struct ifreq ifr; 2255084Sjohnlev 2265084Sjohnlev if (interface == NULL) 2275084Sjohnlev return (B_FALSE); 2285084Sjohnlev 2295084Sjohnlev (void) strncpy(ifr.ifr_name, interface, IFNAMSIZ); 2305084Sjohnlev 2315084Sjohnlev if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 2325084Sjohnlev (void) fprintf(stderr, "%s: socket: %s\n", program, 2335084Sjohnlev strerror(errno)); 2345084Sjohnlev return (B_FALSE); 2355084Sjohnlev } 2365084Sjohnlev 2375084Sjohnlev if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { 2385084Sjohnlev (void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n", 2395084Sjohnlev program, strerror(errno)); 2405084Sjohnlev return (B_FALSE); 2415084Sjohnlev } 2425084Sjohnlev 2435084Sjohnlev if (ifr.ifr_flags & IFF_DHCPRUNNING) 2445084Sjohnlev return (B_TRUE); 2455084Sjohnlev 2465084Sjohnlev return (B_FALSE); 2475084Sjohnlev } 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate /* ARGSUSED */ 2500Sstevel@tonic-gate int 2510Sstevel@tonic-gate main(int argc, char *argv[]) 2520Sstevel@tonic-gate { 2535084Sjohnlev char *root, *interface, *strategy, dummy; 2545084Sjohnlev long len; 2555084Sjohnlev 2565084Sjohnlev root = interface = strategy = NULL; 2575084Sjohnlev program = argv[0]; 2585084Sjohnlev 2595084Sjohnlev root = get_root_fstype(); 2600Sstevel@tonic-gate 2615084Sjohnlev /* 2625084Sjohnlev * If diskless, perhaps boot properties were used to configure 2635084Sjohnlev * the interface. 2645084Sjohnlev */ 2655084Sjohnlev if ((strcmp(root, "nfs") == 0) && boot_properties_present()) { 2665084Sjohnlev strategy = "bootprops"; 2675084Sjohnlev 2685084Sjohnlev interface = get_first_interface(); 2695084Sjohnlev if (interface == NULL) { 2705084Sjohnlev (void) fprintf(stderr, 2715084Sjohnlev "%s: cannot identify root interface.\n", program); 2725084Sjohnlev return (2); 2735084Sjohnlev } 2745084Sjohnlev 2755084Sjohnlev (void) printf("%s %s %s\n", root, interface, strategy); 2765084Sjohnlev return (0); 2770Sstevel@tonic-gate } 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate /* 2800Sstevel@tonic-gate * Handle the simple case where diskless dhcp tells us everything 2810Sstevel@tonic-gate * we need to know. 2820Sstevel@tonic-gate */ 2830Sstevel@tonic-gate if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) { 2840Sstevel@tonic-gate /* interface is first thing in cache. */ 2850Sstevel@tonic-gate strategy = "dhcp"; 2860Sstevel@tonic-gate interface = alloca(len); 2870Sstevel@tonic-gate (void) sysinfo(SI_DHCP_CACHE, interface, len); 2880Sstevel@tonic-gate (void) printf("%s %s %s\n", root, interface, strategy); 2890Sstevel@tonic-gate return (0); 2900Sstevel@tonic-gate } 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate /* 2930Sstevel@tonic-gate * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle 2940Sstevel@tonic-gate * "ufs rarp" (consumers are coded to deal with this reality), so 2950Sstevel@tonic-gate * there are three possible situations: 2960Sstevel@tonic-gate * 2970Sstevel@tonic-gate * 1. We're "ufs dhcp" if there are any interfaces which have 2980Sstevel@tonic-gate * obtained their addresses through DHCP. That is, if there 2990Sstevel@tonic-gate * are any IFF_UP and non-IFF_VIRTUAL interfaces also have 3000Sstevel@tonic-gate * IFF_DHCPRUNNING set. 3010Sstevel@tonic-gate * 3020Sstevel@tonic-gate * 2. We're "ufs none" if our filesystem is local and there 3030Sstevel@tonic-gate * are no interfaces which have obtained their addresses 3040Sstevel@tonic-gate * through DHCP. 3050Sstevel@tonic-gate * 3060Sstevel@tonic-gate * 3. We're "nfs rarp" if our filesystem is remote and there's 3070Sstevel@tonic-gate * at least IFF_UP non-IFF_VIRTUAL interface (which there 3080Sstevel@tonic-gate * *must* be, since we're running over NFS somehow), then 3090Sstevel@tonic-gate * it must be RARP since SI_DHCP_CACHE call above failed. 3100Sstevel@tonic-gate * It's too bad there isn't an IFF_RARPRUNNING flag. 3110Sstevel@tonic-gate */ 3120Sstevel@tonic-gate 3135084Sjohnlev interface = get_first_interface(); 3140Sstevel@tonic-gate 3155084Sjohnlev if (check_dhcp_running(interface)) 3165084Sjohnlev strategy = "dhcp"; 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) { 3190Sstevel@tonic-gate if (interface == NULL) { 3200Sstevel@tonic-gate (void) fprintf(stderr, 3215084Sjohnlev "%s: cannot identify root interface.\n", program); 3220Sstevel@tonic-gate return (2); 3230Sstevel@tonic-gate } 3240Sstevel@tonic-gate if (strategy == NULL) 3250Sstevel@tonic-gate strategy = "rarp"; /* must be rarp/bootparams */ 3260Sstevel@tonic-gate } else { 3270Sstevel@tonic-gate if (interface == NULL || strategy == NULL) 3280Sstevel@tonic-gate interface = strategy = "none"; 3290Sstevel@tonic-gate } 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate (void) printf("%s %s %s\n", root, interface, strategy); 3320Sstevel@tonic-gate return (0); 3330Sstevel@tonic-gate } 334