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 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 26 */ 27 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", "ufs" or "zfs" 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 either "ufs dhcp" or "zfs dhcp" if there are any 312 * interfaces which have obtained their addresses through DHCP. 313 * That is, if there are any IFF_UP and non-IFF_VIRTUAL 314 * interfaces also have IFF_DHCPRUNNING set. 315 * 316 * 2. We're either "ufs none" or "zfs none" if our filesystem 317 * is local and there are no interfaces which have obtained 318 * their addresses 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) { 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