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 }