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 char * 82 get_root_fstype() 83 { 84 static struct statvfs vfs; 85 86 /* root location */ 87 if (statvfs("/", &vfs) < 0) { 88 return ("none"); 89 } else { 90 if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0) 91 vfs.f_basetype[sizeof ("nfs") - 1] = '\0'; 92 return (vfs.f_basetype); 93 } 94 } 95 96 /* 97 * The following boot properties can be used to configure a network 98 * interface in the case of a diskless boot. 99 * host-ip, subnet-mask, server-path, server-name, server-ip. 100 * 101 * XXX non-diskless case requires "network-interface"? 102 */ 103 static boolean_t 104 boot_properties_present() 105 { 106 /* XXX should use sys/bootprops.h, but it's not delivered */ 107 const char *required_properties[] = { 108 "host-ip", 109 "subnet-mask", 110 "server-path", 111 "server-name", 112 "server-ip", 113 NULL, 114 }; 115 const char **prop = required_properties; 116 char *prop_value; 117 di_node_t dn; 118 119 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) { 120 (void) fprintf(stderr, "%s: di_init: %s\n", program, 121 strerror(errno)); 122 di_fini(dn); 123 return (B_FALSE); 124 } 125 126 while (*prop != NULL) { 127 if (di_prop_lookup_strings(DDI_DEV_T_ANY, 128 dn, *prop, &prop_value) != 1) { 129 di_fini(dn); 130 return (B_FALSE); 131 } 132 prop++; 133 } 134 di_fini(dn); 135 136 return (B_TRUE); 137 } 138 139 static char * 140 get_first_interface() 141 { 142 int fd; 143 struct lifnum ifnum; 144 struct lifconf ifconf; 145 struct lifreq *ifr; 146 static char interface[IFNAMSIZ]; 147 148 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 149 (void) fprintf(stderr, "%s: socket: %s\n", program, 150 strerror(errno)); 151 return (NULL); 152 } 153 154 ifnum.lifn_family = AF_UNSPEC; 155 ifnum.lifn_flags = 0; 156 ifnum.lifn_count = 0; 157 158 if (ioctl(fd, SIOCGLIFNUM, &ifnum) < 0) { 159 (void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program, 160 strerror(errno)); 161 (void) close(fd); 162 return (NULL); 163 } 164 165 ifconf.lifc_family = AF_UNSPEC; 166 ifconf.lifc_flags = 0; 167 ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq); 168 ifconf.lifc_buf = alloca(ifconf.lifc_len); 169 170 if (ioctl(fd, SIOCGLIFCONF, &ifconf) < 0) { 171 (void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program, 172 strerror(errno)); 173 (void) close(fd); 174 return (NULL); 175 } 176 177 for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len / 178 sizeof (ifconf.lifc_req[0])]; ifr++) { 179 180 if (strchr(ifr->lifr_name, ':') != NULL) 181 continue; /* skip logical interfaces */ 182 183 if (ioctl(fd, SIOCGLIFFLAGS, ifr) < 0) { 184 (void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n", 185 program, strerror(errno)); 186 continue; 187 } 188 189 if (ifr->lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT)) 190 continue; 191 192 if (ifr->lifr_flags & IFF_UP) { 193 /* 194 * For the "nfs rarp" and "nfs bootprops" 195 * cases, we assume that the first non-virtual 196 * IFF_UP interface is the one used. 197 * 198 * Since the order of the interfaces retrieved 199 * via SIOCGLIFCONF is not deterministic, this 200 * is largely silliness, but (a) "it's always 201 * been this way", (b) machines booted this 202 * way typically only have one interface, and 203 * (c) no one consumes the interface name in 204 * the RARP case anyway. 205 */ 206 (void) strncpy(interface, ifr->lifr_name, IFNAMSIZ); 207 (void) close(fd); 208 return (interface); 209 } 210 } 211 212 (void) close(fd); 213 214 return (NULL); 215 } 216 217 /* 218 * Is DHCP running on the specified interface? 219 */ 220 static boolean_t 221 check_dhcp_running(char *interface) 222 { 223 int fd; 224 struct ifreq ifr; 225 226 if (interface == NULL) 227 return (B_FALSE); 228 229 (void) strncpy(ifr.ifr_name, interface, IFNAMSIZ); 230 231 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 232 (void) fprintf(stderr, "%s: socket: %s\n", program, 233 strerror(errno)); 234 return (B_FALSE); 235 } 236 237 if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { 238 (void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n", 239 program, strerror(errno)); 240 return (B_FALSE); 241 } 242 243 if (ifr.ifr_flags & IFF_DHCPRUNNING) 244 return (B_TRUE); 245 246 return (B_FALSE); 247 } 248 249 /* ARGSUSED */ 250 int 251 main(int argc, char *argv[]) 252 { 253 char *root, *interface, *strategy, dummy; 254 long len; 255 256 root = interface = strategy = NULL; 257 program = argv[0]; 258 259 root = get_root_fstype(); 260 261 /* 262 * If diskless, perhaps boot properties were used to configure 263 * the interface. 264 */ 265 if ((strcmp(root, "nfs") == 0) && boot_properties_present()) { 266 strategy = "bootprops"; 267 268 interface = get_first_interface(); 269 if (interface == NULL) { 270 (void) fprintf(stderr, 271 "%s: cannot identify root interface.\n", program); 272 return (2); 273 } 274 275 (void) printf("%s %s %s\n", root, interface, strategy); 276 return (0); 277 } 278 279 /* 280 * Handle the simple case where diskless dhcp tells us everything 281 * we need to know. 282 */ 283 if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) { 284 /* interface is first thing in cache. */ 285 strategy = "dhcp"; 286 interface = alloca(len); 287 (void) sysinfo(SI_DHCP_CACHE, interface, len); 288 (void) printf("%s %s %s\n", root, interface, strategy); 289 return (0); 290 } 291 292 /* 293 * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle 294 * "ufs rarp" (consumers are coded to deal with this reality), so 295 * there are three possible situations: 296 * 297 * 1. We're "ufs dhcp" if there are any interfaces which have 298 * obtained their addresses through DHCP. That is, if there 299 * are any IFF_UP and non-IFF_VIRTUAL interfaces also have 300 * IFF_DHCPRUNNING set. 301 * 302 * 2. We're "ufs none" if our filesystem is local and there 303 * are no interfaces which have obtained their addresses 304 * through DHCP. 305 * 306 * 3. We're "nfs rarp" if our filesystem is remote and there's 307 * at least IFF_UP non-IFF_VIRTUAL interface (which there 308 * *must* be, since we're running over NFS somehow), then 309 * it must be RARP since SI_DHCP_CACHE call above failed. 310 * It's too bad there isn't an IFF_RARPRUNNING flag. 311 */ 312 313 interface = get_first_interface(); 314 315 if (check_dhcp_running(interface)) 316 strategy = "dhcp"; 317 318 if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) { 319 if (interface == NULL) { 320 (void) fprintf(stderr, 321 "%s: cannot identify root interface.\n", program); 322 return (2); 323 } 324 if (strategy == NULL) 325 strategy = "rarp"; /* must be rarp/bootparams */ 326 } else { 327 if (interface == NULL || strategy == NULL) 328 interface = strategy = "none"; 329 } 330 331 (void) printf("%s %s %s\n", root, interface, strategy); 332 return (0); 333 } 334