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 2007 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 156 if (ioctl(fd, SIOCGLIFNUM, &ifnum) < 0) { 157 (void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program, 158 strerror(errno)); 159 (void) close(fd); 160 return (NULL); 161 } 162 163 ifconf.lifc_family = AF_UNSPEC; 164 ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq); 165 ifconf.lifc_buf = alloca(ifconf.lifc_len); 166 167 if (ioctl(fd, SIOCGLIFCONF, &ifconf) < 0) { 168 (void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program, 169 strerror(errno)); 170 (void) close(fd); 171 return (NULL); 172 } 173 174 for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len / 175 sizeof (ifconf.lifc_req[0])]; ifr++) { 176 177 if (strchr(ifr->lifr_name, ':') != NULL) 178 continue; /* skip logical interfaces */ 179 180 if (ioctl(fd, SIOCGLIFFLAGS, ifr) < 0) { 181 (void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n", 182 program, strerror(errno)); 183 continue; 184 } 185 186 if (ifr->lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT)) 187 continue; 188 189 if (ifr->lifr_flags & IFF_UP) { 190 /* 191 * For the "nfs rarp" and "nfs bootprops" 192 * cases, we assume that the first non-virtual 193 * IFF_UP interface is the one used. 194 * 195 * Since the order of the interfaces retrieved 196 * via SIOCGLIFCONF is not deterministic, this 197 * is largely silliness, but (a) "it's always 198 * been this way", (b) machines booted this 199 * way typically only have one interface, and 200 * (c) no one consumes the interface name in 201 * the RARP case anyway. 202 */ 203 (void) strncpy(interface, ifr->lifr_name, IFNAMSIZ); 204 (void) close(fd); 205 return (interface); 206 } 207 } 208 209 (void) close(fd); 210 211 return (NULL); 212 } 213 214 /* 215 * Is DHCP running on the specified interface? 216 */ 217 static boolean_t 218 check_dhcp_running(char *interface) 219 { 220 int fd; 221 struct ifreq ifr; 222 223 if (interface == NULL) 224 return (B_FALSE); 225 226 (void) strncpy(ifr.ifr_name, interface, IFNAMSIZ); 227 228 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 229 (void) fprintf(stderr, "%s: socket: %s\n", program, 230 strerror(errno)); 231 return (B_FALSE); 232 } 233 234 if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { 235 (void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n", 236 program, strerror(errno)); 237 return (B_FALSE); 238 } 239 240 if (ifr.ifr_flags & IFF_DHCPRUNNING) 241 return (B_TRUE); 242 243 return (B_FALSE); 244 } 245 246 /* ARGSUSED */ 247 int 248 main(int argc, char *argv[]) 249 { 250 char *root, *interface, *strategy, dummy; 251 long len; 252 253 root = interface = strategy = NULL; 254 program = argv[0]; 255 256 root = get_root_fstype(); 257 258 /* 259 * If diskless, perhaps boot properties were used to configure 260 * the interface. 261 */ 262 if ((strcmp(root, "nfs") == 0) && boot_properties_present()) { 263 strategy = "bootprops"; 264 265 interface = get_first_interface(); 266 if (interface == NULL) { 267 (void) fprintf(stderr, 268 "%s: cannot identify root interface.\n", program); 269 return (2); 270 } 271 272 (void) printf("%s %s %s\n", root, interface, strategy); 273 return (0); 274 } 275 276 /* 277 * Handle the simple case where diskless dhcp tells us everything 278 * we need to know. 279 */ 280 if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) { 281 /* interface is first thing in cache. */ 282 strategy = "dhcp"; 283 interface = alloca(len); 284 (void) sysinfo(SI_DHCP_CACHE, interface, len); 285 (void) printf("%s %s %s\n", root, interface, strategy); 286 return (0); 287 } 288 289 /* 290 * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle 291 * "ufs rarp" (consumers are coded to deal with this reality), so 292 * there are three possible situations: 293 * 294 * 1. We're "ufs dhcp" if there are any interfaces which have 295 * obtained their addresses through DHCP. That is, if there 296 * are any IFF_UP and non-IFF_VIRTUAL interfaces also have 297 * IFF_DHCPRUNNING set. 298 * 299 * 2. We're "ufs none" if our filesystem is local and there 300 * are no interfaces which have obtained their addresses 301 * through DHCP. 302 * 303 * 3. We're "nfs rarp" if our filesystem is remote and there's 304 * at least IFF_UP non-IFF_VIRTUAL interface (which there 305 * *must* be, since we're running over NFS somehow), then 306 * it must be RARP since SI_DHCP_CACHE call above failed. 307 * It's too bad there isn't an IFF_RARPRUNNING flag. 308 */ 309 310 interface = get_first_interface(); 311 312 if (check_dhcp_running(interface)) 313 strategy = "dhcp"; 314 315 if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) { 316 if (interface == NULL) { 317 (void) fprintf(stderr, 318 "%s: cannot identify root interface.\n", program); 319 return (2); 320 } 321 if (strategy == NULL) 322 strategy = "rarp"; /* must be rarp/bootparams */ 323 } else { 324 if (interface == NULL || strategy == NULL) 325 interface = strategy = "none"; 326 } 327 328 (void) printf("%s %s %s\n", root, interface, strategy); 329 return (0); 330 } 331