1 0 stevel /* 2 0 stevel * CDDL HEADER START 3 0 stevel * 4 0 stevel * The contents of this file are subject to the terms of the 5 5084 johnlev * Common Development and Distribution License (the "License"). 6 5084 johnlev * You may not use this file except in compliance with the License. 7 0 stevel * 8 0 stevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 0 stevel * or http://www.opensolaris.org/os/licensing. 10 0 stevel * See the License for the specific language governing permissions 11 0 stevel * and limitations under the License. 12 0 stevel * 13 0 stevel * When distributing Covered Code, include this CDDL HEADER in each 14 0 stevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 0 stevel * If applicable, add the following below this CDDL HEADER, with the 16 0 stevel * fields enclosed by brackets "[]" replaced with your own identifying 17 0 stevel * information: Portions Copyright [yyyy] [name of copyright owner] 18 0 stevel * 19 0 stevel * CDDL HEADER END 20 0 stevel */ 21 761 meem 22 0 stevel /* 23 7170 dme * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 0 stevel * Use is subject to license terms. 25 0 stevel */ 26 0 stevel 27 0 stevel #pragma ident "%Z%%M% %I% %E% SMI" 28 0 stevel 29 0 stevel /* 30 0 stevel * This program does the following: 31 0 stevel * 32 0 stevel * a) Returns: 33 0 stevel * 0 - if the program successfully determined the net strategy. 34 0 stevel * !0 - if an error occurred. 35 0 stevel * 36 0 stevel * b) If the program is successful, it prints three tokens to 37 0 stevel * stdout: <root fs type> <interface name> <net config strategy>. 38 0 stevel * where: 39 0 stevel * <root fs type> - "nfs" or "ufs" 40 0 stevel * <interface name> - "hme0" or "none" 41 5084 johnlev * <net config strategy> - "dhcp", "rarp", "bootprops" 42 5084 johnlev * or "none" 43 0 stevel * 44 0 stevel * Eg: 45 0 stevel * # /sbin/netstrategy 46 0 stevel * ufs hme0 dhcp 47 0 stevel * 48 0 stevel * <root fs type> identifies the system's root file system type. 49 0 stevel * 50 0 stevel * <interface name> is the 16 char name of the root interface, and is only 51 0 stevel * set if rarp/dhcp was used to configure the interface. 52 0 stevel * 53 5084 johnlev * <net config strategy> can be either "rarp", "dhcp", "bootprops", or 54 5084 johnlev * "none" depending on which strategy was used to configure the 55 5084 johnlev * interface. Is "none" if no interface was configured using a 56 5084 johnlev * net-based strategy. 57 0 stevel * 58 0 stevel * CAVEATS: what about autoclient systems? XXX 59 5084 johnlev * 60 5084 johnlev * The logic here must match that in usr/src/uts/common/fs/nfs/nfs_dlinet.c, 61 5084 johnlev * in particular that code (which implements diskless boot) imposes an 62 5084 johnlev * ordering on possible ways of configuring network interfaces. 63 0 stevel */ 64 0 stevel 65 0 stevel #include <stdio.h> 66 0 stevel #include <stdlib.h> 67 0 stevel #include <unistd.h> 68 0 stevel #include <string.h> 69 0 stevel #include <sys/types.h> 70 0 stevel #include <errno.h> 71 0 stevel #include <alloca.h> 72 0 stevel #include <sys/systeminfo.h> 73 0 stevel #include <sys/socket.h> 74 0 stevel #include <sys/sockio.h> 75 0 stevel #include <net/if.h> 76 0 stevel #include <sys/statvfs.h> 77 5084 johnlev #include <libdevinfo.h> 78 5084 johnlev 79 5084 johnlev static char *program; 80 5084 johnlev 81 7254 okie static int s4, s6; /* inet and inet6 sockets */ 82 7254 okie 83 7254 okie static boolean_t 84 7254 okie open_sockets(void) 85 7254 okie { 86 7254 okie if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 87 7254 okie (void) fprintf(stderr, "%s: inet socket: %s\n", program, 88 7254 okie strerror(errno)); 89 7254 okie return (B_FALSE); 90 7254 okie } 91 7254 okie if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { 92 7254 okie (void) fprintf(stderr, "%s: inet6 socket: %s\n", program, 93 7254 okie strerror(errno)); 94 7254 okie return (B_FALSE); 95 7254 okie } 96 7254 okie return (B_TRUE); 97 7254 okie } 98 7254 okie 99 7254 okie static void 100 7254 okie close_sockets(void) 101 7254 okie { 102 7254 okie (void) close(s4); 103 7254 okie (void) close(s6); 104 7254 okie } 105 7254 okie 106 5084 johnlev static char * 107 5084 johnlev get_root_fstype() 108 5084 johnlev { 109 5084 johnlev static struct statvfs vfs; 110 5084 johnlev 111 5084 johnlev /* root location */ 112 5084 johnlev if (statvfs("/", &vfs) < 0) { 113 5084 johnlev return ("none"); 114 5084 johnlev } else { 115 5084 johnlev if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0) 116 5084 johnlev vfs.f_basetype[sizeof ("nfs") - 1] = '\0'; 117 5084 johnlev return (vfs.f_basetype); 118 5084 johnlev } 119 5084 johnlev } 120 5084 johnlev 121 5084 johnlev /* 122 5084 johnlev * The following boot properties can be used to configure a network 123 5084 johnlev * interface in the case of a diskless boot. 124 5084 johnlev * host-ip, subnet-mask, server-path, server-name, server-ip. 125 5084 johnlev * 126 5084 johnlev * XXX non-diskless case requires "network-interface"? 127 5084 johnlev */ 128 5084 johnlev static boolean_t 129 5084 johnlev boot_properties_present() 130 5084 johnlev { 131 5084 johnlev /* XXX should use sys/bootprops.h, but it's not delivered */ 132 5084 johnlev const char *required_properties[] = { 133 5084 johnlev "host-ip", 134 5084 johnlev "subnet-mask", 135 5084 johnlev "server-path", 136 5084 johnlev "server-name", 137 5084 johnlev "server-ip", 138 5084 johnlev NULL, 139 5084 johnlev }; 140 5084 johnlev const char **prop = required_properties; 141 5084 johnlev char *prop_value; 142 5084 johnlev di_node_t dn; 143 5084 johnlev 144 5084 johnlev if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) { 145 5084 johnlev (void) fprintf(stderr, "%s: di_init: %s\n", program, 146 5084 johnlev strerror(errno)); 147 5084 johnlev di_fini(dn); 148 5084 johnlev return (B_FALSE); 149 5084 johnlev } 150 5084 johnlev 151 5084 johnlev while (*prop != NULL) { 152 5084 johnlev if (di_prop_lookup_strings(DDI_DEV_T_ANY, 153 5084 johnlev dn, *prop, &prop_value) != 1) { 154 5084 johnlev di_fini(dn); 155 5084 johnlev return (B_FALSE); 156 5084 johnlev } 157 5084 johnlev prop++; 158 5084 johnlev } 159 5084 johnlev di_fini(dn); 160 5084 johnlev 161 5084 johnlev return (B_TRUE); 162 5084 johnlev } 163 5084 johnlev 164 5084 johnlev static char * 165 7254 okie get_first_interface(boolean_t *dhcpflag) 166 5084 johnlev { 167 5084 johnlev struct lifnum ifnum; 168 5084 johnlev struct lifconf ifconf; 169 5084 johnlev struct lifreq *ifr; 170 7254 okie static char interface[LIFNAMSIZ]; 171 7254 okie boolean_t isv4, found_one = B_FALSE; 172 5084 johnlev 173 5084 johnlev ifnum.lifn_family = AF_UNSPEC; 174 7170 dme ifnum.lifn_flags = 0; 175 7170 dme ifnum.lifn_count = 0; 176 5084 johnlev 177 7254 okie if (ioctl(s4, SIOCGLIFNUM, &ifnum) < 0) { 178 5084 johnlev (void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program, 179 5084 johnlev strerror(errno)); 180 5084 johnlev return (NULL); 181 5084 johnlev } 182 5084 johnlev 183 5084 johnlev ifconf.lifc_family = AF_UNSPEC; 184 7170 dme ifconf.lifc_flags = 0; 185 5084 johnlev ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq); 186 5084 johnlev ifconf.lifc_buf = alloca(ifconf.lifc_len); 187 5084 johnlev 188 7254 okie if (ioctl(s4, SIOCGLIFCONF, &ifconf) < 0) { 189 5084 johnlev (void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program, 190 5084 johnlev strerror(errno)); 191 5084 johnlev return (NULL); 192 5084 johnlev } 193 5084 johnlev 194 5084 johnlev for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len / 195 5084 johnlev sizeof (ifconf.lifc_req[0])]; ifr++) { 196 7254 okie struct lifreq flifr; 197 7254 okie struct sockaddr_in *sin; 198 5084 johnlev 199 5084 johnlev if (strchr(ifr->lifr_name, ':') != NULL) 200 5084 johnlev continue; /* skip logical interfaces */ 201 5084 johnlev 202 7254 okie isv4 = ifr->lifr_addr.ss_family == AF_INET; 203 7254 okie 204 7254 okie (void) strncpy(flifr.lifr_name, ifr->lifr_name, LIFNAMSIZ); 205 7254 okie 206 7254 okie if (ioctl(isv4 ? s4 : s6, SIOCGLIFFLAGS, &flifr) < 0) { 207 7254 okie (void) fprintf(stderr, "%s: SIOCGLIFFLAGS: %s\n", 208 5084 johnlev program, strerror(errno)); 209 5084 johnlev continue; 210 5084 johnlev } 211 5084 johnlev 212 7254 okie if (!(flifr.lifr_flags & IFF_UP) || 213 7254 okie (flifr.lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT))) 214 5084 johnlev continue; 215 5084 johnlev 216 7254 okie /* 217 7254 okie * For the "nfs rarp" and "nfs bootprops" 218 7254 okie * cases, we assume that the first non-virtual 219 7254 okie * IFF_UP interface with a non-zero address is 220 7254 okie * the one used. 221 7254 okie * 222 7254 okie * For the non-zero address check, we only check 223 7254 okie * v4 interfaces, as it's not possible to set the 224 7254 okie * the first logical interface (the only ones we 225 7254 okie * look at here) to ::0; that interface must have 226 7254 okie * a link-local address. 227 7254 okie * 228 7254 okie * If we don't find an IFF_UP interface with a 229 7254 okie * non-zero address, we'll return the last IFF_UP 230 7254 okie * interface seen. 231 7254 okie * 232 7254 okie * Since the order of the interfaces retrieved 233 7254 okie * via SIOCGLIFCONF is not deterministic, this 234 7254 okie * is largely silliness, but (a) "it's always 235 7254 okie * been this way", and (b) no one consumes the 236 7254 okie * interface name in the RARP case anyway. 237 7254 okie */ 238 7254 okie 239 7254 okie found_one = B_TRUE; 240 7254 okie (void) strncpy(interface, ifr->lifr_name, LIFNAMSIZ); 241 7254 okie *dhcpflag = (flifr.lifr_flags & IFF_DHCPRUNNING) != 0; 242 7254 okie sin = (struct sockaddr_in *)&ifr->lifr_addr; 243 7254 okie if (isv4 && (sin->sin_addr.s_addr == INADDR_ANY)) { 244 7254 okie /* keep looking for a non-zero address */ 245 7254 okie continue; 246 5084 johnlev } 247 7254 okie return (interface); 248 5084 johnlev } 249 5084 johnlev 250 7254 okie return (found_one ? interface : NULL); 251 5084 johnlev } 252 0 stevel 253 0 stevel /* ARGSUSED */ 254 0 stevel int 255 0 stevel main(int argc, char *argv[]) 256 0 stevel { 257 5084 johnlev char *root, *interface, *strategy, dummy; 258 5084 johnlev long len; 259 7254 okie boolean_t dhcp_running = B_FALSE; 260 0 stevel 261 5084 johnlev root = interface = strategy = NULL; 262 5084 johnlev program = argv[0]; 263 5084 johnlev 264 5084 johnlev root = get_root_fstype(); 265 7254 okie 266 7254 okie if (!open_sockets()) { 267 7254 okie (void) fprintf(stderr, 268 7254 okie "%s: cannot get interface information\n", program); 269 7254 okie return (2); 270 7254 okie } 271 5084 johnlev 272 5084 johnlev /* 273 5084 johnlev * If diskless, perhaps boot properties were used to configure 274 5084 johnlev * the interface. 275 5084 johnlev */ 276 5084 johnlev if ((strcmp(root, "nfs") == 0) && boot_properties_present()) { 277 5084 johnlev strategy = "bootprops"; 278 5084 johnlev 279 7254 okie interface = get_first_interface(&dhcp_running); 280 5084 johnlev if (interface == NULL) { 281 5084 johnlev (void) fprintf(stderr, 282 5084 johnlev "%s: cannot identify root interface.\n", program); 283 7254 okie close_sockets(); 284 5084 johnlev return (2); 285 5084 johnlev } 286 5084 johnlev 287 5084 johnlev (void) printf("%s %s %s\n", root, interface, strategy); 288 7254 okie close_sockets(); 289 5084 johnlev return (0); 290 0 stevel } 291 0 stevel 292 0 stevel /* 293 0 stevel * Handle the simple case where diskless dhcp tells us everything 294 0 stevel * we need to know. 295 0 stevel */ 296 0 stevel if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) { 297 0 stevel /* interface is first thing in cache. */ 298 0 stevel strategy = "dhcp"; 299 0 stevel interface = alloca(len); 300 0 stevel (void) sysinfo(SI_DHCP_CACHE, interface, len); 301 0 stevel (void) printf("%s %s %s\n", root, interface, strategy); 302 7254 okie close_sockets(); 303 0 stevel return (0); 304 0 stevel } 305 0 stevel 306 0 stevel /* 307 0 stevel * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle 308 0 stevel * "ufs rarp" (consumers are coded to deal with this reality), so 309 0 stevel * there are three possible situations: 310 0 stevel * 311 0 stevel * 1. We're "ufs dhcp" if there are any interfaces which have 312 0 stevel * obtained their addresses through DHCP. That is, if there 313 0 stevel * are any IFF_UP and non-IFF_VIRTUAL interfaces also have 314 0 stevel * IFF_DHCPRUNNING set. 315 0 stevel * 316 0 stevel * 2. We're "ufs none" if our filesystem is local and there 317 0 stevel * are no interfaces which have obtained their addresses 318 0 stevel * through DHCP. 319 0 stevel * 320 0 stevel * 3. We're "nfs rarp" if our filesystem is remote and there's 321 0 stevel * at least IFF_UP non-IFF_VIRTUAL interface (which there 322 0 stevel * *must* be, since we're running over NFS somehow), then 323 0 stevel * it must be RARP since SI_DHCP_CACHE call above failed. 324 0 stevel * It's too bad there isn't an IFF_RARPRUNNING flag. 325 0 stevel */ 326 0 stevel 327 7254 okie interface = get_first_interface(&dhcp_running); 328 0 stevel 329 7254 okie if (dhcp_running) 330 5084 johnlev strategy = "dhcp"; 331 0 stevel 332 0 stevel if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) { 333 0 stevel if (interface == NULL) { 334 0 stevel (void) fprintf(stderr, 335 5084 johnlev "%s: cannot identify root interface.\n", program); 336 7254 okie close_sockets(); 337 0 stevel return (2); 338 0 stevel } 339 0 stevel if (strategy == NULL) 340 0 stevel strategy = "rarp"; /* must be rarp/bootparams */ 341 0 stevel } else { 342 0 stevel if (interface == NULL || strategy == NULL) 343 0 stevel interface = strategy = "none"; 344 0 stevel } 345 0 stevel 346 0 stevel (void) printf("%s %s %s\n", root, interface, strategy); 347 7254 okie close_sockets(); 348 0 stevel return (0); 349 0 stevel } 350