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> 78843e1988Sjohnlev 79843e1988Sjohnlev static char *program; 80843e1988Sjohnlev 818acf8916Sokie static int s4, s6; /* inet and inet6 sockets */ 828acf8916Sokie 838acf8916Sokie static boolean_t 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 1008acf8916Sokie close_sockets(void) 1018acf8916Sokie { 1028acf8916Sokie (void) close(s4); 1038acf8916Sokie (void) close(s6); 1048acf8916Sokie } 1058acf8916Sokie 106843e1988Sjohnlev static char * 107843e1988Sjohnlev get_root_fstype() 108843e1988Sjohnlev { 109843e1988Sjohnlev static struct statvfs vfs; 110843e1988Sjohnlev 111843e1988Sjohnlev /* root location */ 112843e1988Sjohnlev if (statvfs("/", &vfs) < 0) { 113843e1988Sjohnlev return ("none"); 114843e1988Sjohnlev } else { 115843e1988Sjohnlev if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0) 116843e1988Sjohnlev 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 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 * 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 216843e1988Sjohnlev /* 217843e1988Sjohnlev * For the "nfs rarp" and "nfs bootprops" 218843e1988Sjohnlev * 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. 231843e1988Sjohnlev * 232843e1988Sjohnlev * Since the order of the interfaces retrieved 233843e1988Sjohnlev * via SIOCGLIFCONF is not deterministic, this 234843e1988Sjohnlev * 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. 237843e1988Sjohnlev */ 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; 2468acf8916Sokie } 247843e1988Sjohnlev return (interface); 248843e1988Sjohnlev } 249843e1988Sjohnlev 2508acf8916Sokie return (found_one ? interface : NULL); 251843e1988Sjohnlev } 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate /* ARGSUSED */ 2547c478bd9Sstevel@tonic-gate int 2557c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 2567c478bd9Sstevel@tonic-gate { 2577c478bd9Sstevel@tonic-gate char *root, *interface, *strategy, dummy; 2587c478bd9Sstevel@tonic-gate long len; 2598acf8916Sokie boolean_t dhcp_running = B_FALSE; 2607c478bd9Sstevel@tonic-gate 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) 3307c478bd9Sstevel@tonic-gate 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