17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5843e1988Sjohnlev * Common Development and Distribution License (the "License").
6843e1988Sjohnlev * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
21595aa6e4Smeem
227c478bd9Sstevel@tonic-gate /*
235d863251Sdme * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
25*bd93c05dSAlexander Eremin * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
267c478bd9Sstevel@tonic-gate */
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate * This program does the following:
317c478bd9Sstevel@tonic-gate *
327c478bd9Sstevel@tonic-gate * a) Returns:
337c478bd9Sstevel@tonic-gate * 0 - if the program successfully determined the net strategy.
347c478bd9Sstevel@tonic-gate * !0 - if an error occurred.
357c478bd9Sstevel@tonic-gate *
367c478bd9Sstevel@tonic-gate * b) If the program is successful, it prints three tokens to
377c478bd9Sstevel@tonic-gate * stdout: <root fs type> <interface name> <net config strategy>.
387c478bd9Sstevel@tonic-gate * where:
39*bd93c05dSAlexander Eremin * <root fs type> - "nfs", "ufs" or "zfs"
407c478bd9Sstevel@tonic-gate * <interface name> - "hme0" or "none"
41843e1988Sjohnlev * <net config strategy> - "dhcp", "rarp", "bootprops"
42843e1988Sjohnlev * or "none"
437c478bd9Sstevel@tonic-gate *
447c478bd9Sstevel@tonic-gate * Eg:
457c478bd9Sstevel@tonic-gate * # /sbin/netstrategy
467c478bd9Sstevel@tonic-gate * ufs hme0 dhcp
477c478bd9Sstevel@tonic-gate *
487c478bd9Sstevel@tonic-gate * <root fs type> identifies the system's root file system type.
497c478bd9Sstevel@tonic-gate *
507c478bd9Sstevel@tonic-gate * <interface name> is the 16 char name of the root interface, and is only
517c478bd9Sstevel@tonic-gate * set if rarp/dhcp was used to configure the interface.
527c478bd9Sstevel@tonic-gate *
53843e1988Sjohnlev * <net config strategy> can be either "rarp", "dhcp", "bootprops", or
54843e1988Sjohnlev * "none" depending on which strategy was used to configure the
55843e1988Sjohnlev * interface. Is "none" if no interface was configured using a
56843e1988Sjohnlev * net-based strategy.
577c478bd9Sstevel@tonic-gate *
587c478bd9Sstevel@tonic-gate * CAVEATS: what about autoclient systems? XXX
59843e1988Sjohnlev *
60843e1988Sjohnlev * The logic here must match that in usr/src/uts/common/fs/nfs/nfs_dlinet.c,
61843e1988Sjohnlev * in particular that code (which implements diskless boot) imposes an
62843e1988Sjohnlev * ordering on possible ways of configuring network interfaces.
637c478bd9Sstevel@tonic-gate */
647c478bd9Sstevel@tonic-gate
657c478bd9Sstevel@tonic-gate #include <stdio.h>
667c478bd9Sstevel@tonic-gate #include <stdlib.h>
677c478bd9Sstevel@tonic-gate #include <unistd.h>
687c478bd9Sstevel@tonic-gate #include <string.h>
697c478bd9Sstevel@tonic-gate #include <sys/types.h>
707c478bd9Sstevel@tonic-gate #include <errno.h>
717c478bd9Sstevel@tonic-gate #include <alloca.h>
727c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h>
737c478bd9Sstevel@tonic-gate #include <sys/socket.h>
747c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
757c478bd9Sstevel@tonic-gate #include <net/if.h>
767c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
77843e1988Sjohnlev #include <libdevinfo.h>
787c478bd9Sstevel@tonic-gate
79843e1988Sjohnlev static char *program;
80843e1988Sjohnlev
818acf8916Sokie static int s4, s6; /* inet and inet6 sockets */
828acf8916Sokie
838acf8916Sokie static boolean_t
open_sockets(void)848acf8916Sokie open_sockets(void)
858acf8916Sokie {
868acf8916Sokie if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
878acf8916Sokie (void) fprintf(stderr, "%s: inet socket: %s\n", program,
888acf8916Sokie strerror(errno));
898acf8916Sokie return (B_FALSE);
908acf8916Sokie }
918acf8916Sokie if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
928acf8916Sokie (void) fprintf(stderr, "%s: inet6 socket: %s\n", program,
938acf8916Sokie strerror(errno));
948acf8916Sokie return (B_FALSE);
958acf8916Sokie }
968acf8916Sokie return (B_TRUE);
978acf8916Sokie }
988acf8916Sokie
998acf8916Sokie static void
close_sockets(void)1008acf8916Sokie close_sockets(void)
1018acf8916Sokie {
1028acf8916Sokie (void) close(s4);
1038acf8916Sokie (void) close(s6);
1048acf8916Sokie }
1058acf8916Sokie
106843e1988Sjohnlev static char *
get_root_fstype()107843e1988Sjohnlev get_root_fstype()
1087c478bd9Sstevel@tonic-gate {
109843e1988Sjohnlev static struct statvfs vfs;
1107c478bd9Sstevel@tonic-gate
1117c478bd9Sstevel@tonic-gate /* root location */
112843e1988Sjohnlev if (statvfs("/", &vfs) < 0) {
113843e1988Sjohnlev return ("none");
114843e1988Sjohnlev } else {
1157c478bd9Sstevel@tonic-gate if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0)
1167c478bd9Sstevel@tonic-gate vfs.f_basetype[sizeof ("nfs") - 1] = '\0';
117843e1988Sjohnlev return (vfs.f_basetype);
118843e1988Sjohnlev }
119843e1988Sjohnlev }
120843e1988Sjohnlev
121843e1988Sjohnlev /*
122843e1988Sjohnlev * The following boot properties can be used to configure a network
123843e1988Sjohnlev * interface in the case of a diskless boot.
124843e1988Sjohnlev * host-ip, subnet-mask, server-path, server-name, server-ip.
125843e1988Sjohnlev *
126843e1988Sjohnlev * XXX non-diskless case requires "network-interface"?
127843e1988Sjohnlev */
128843e1988Sjohnlev static boolean_t
boot_properties_present()129843e1988Sjohnlev boot_properties_present()
130843e1988Sjohnlev {
131843e1988Sjohnlev /* XXX should use sys/bootprops.h, but it's not delivered */
132843e1988Sjohnlev const char *required_properties[] = {
133843e1988Sjohnlev "host-ip",
134843e1988Sjohnlev "subnet-mask",
135843e1988Sjohnlev "server-path",
136843e1988Sjohnlev "server-name",
137843e1988Sjohnlev "server-ip",
138843e1988Sjohnlev NULL,
139843e1988Sjohnlev };
140843e1988Sjohnlev const char **prop = required_properties;
141843e1988Sjohnlev char *prop_value;
142843e1988Sjohnlev di_node_t dn;
143843e1988Sjohnlev
144843e1988Sjohnlev if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
145843e1988Sjohnlev (void) fprintf(stderr, "%s: di_init: %s\n", program,
146843e1988Sjohnlev strerror(errno));
147843e1988Sjohnlev di_fini(dn);
148843e1988Sjohnlev return (B_FALSE);
149843e1988Sjohnlev }
150843e1988Sjohnlev
151843e1988Sjohnlev while (*prop != NULL) {
152843e1988Sjohnlev if (di_prop_lookup_strings(DDI_DEV_T_ANY,
153843e1988Sjohnlev dn, *prop, &prop_value) != 1) {
154843e1988Sjohnlev di_fini(dn);
155843e1988Sjohnlev return (B_FALSE);
156843e1988Sjohnlev }
157843e1988Sjohnlev prop++;
158843e1988Sjohnlev }
159843e1988Sjohnlev di_fini(dn);
160843e1988Sjohnlev
161843e1988Sjohnlev return (B_TRUE);
162843e1988Sjohnlev }
163843e1988Sjohnlev
164843e1988Sjohnlev static char *
get_first_interface(boolean_t * dhcpflag)1658acf8916Sokie get_first_interface(boolean_t *dhcpflag)
166843e1988Sjohnlev {
167843e1988Sjohnlev struct lifnum ifnum;
168843e1988Sjohnlev struct lifconf ifconf;
169843e1988Sjohnlev struct lifreq *ifr;
1708acf8916Sokie static char interface[LIFNAMSIZ];
1718acf8916Sokie boolean_t isv4, found_one = B_FALSE;
172843e1988Sjohnlev
173843e1988Sjohnlev ifnum.lifn_family = AF_UNSPEC;
1745d863251Sdme ifnum.lifn_flags = 0;
1755d863251Sdme ifnum.lifn_count = 0;
176843e1988Sjohnlev
1778acf8916Sokie if (ioctl(s4, SIOCGLIFNUM, &ifnum) < 0) {
178843e1988Sjohnlev (void) fprintf(stderr, "%s: SIOCGLIFNUM: %s\n", program,
179843e1988Sjohnlev strerror(errno));
180843e1988Sjohnlev return (NULL);
181843e1988Sjohnlev }
182843e1988Sjohnlev
183843e1988Sjohnlev ifconf.lifc_family = AF_UNSPEC;
1845d863251Sdme ifconf.lifc_flags = 0;
185843e1988Sjohnlev ifconf.lifc_len = ifnum.lifn_count * sizeof (struct lifreq);
186843e1988Sjohnlev ifconf.lifc_buf = alloca(ifconf.lifc_len);
187843e1988Sjohnlev
1888acf8916Sokie if (ioctl(s4, SIOCGLIFCONF, &ifconf) < 0) {
189843e1988Sjohnlev (void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", program,
190843e1988Sjohnlev strerror(errno));
191843e1988Sjohnlev return (NULL);
192843e1988Sjohnlev }
193843e1988Sjohnlev
194843e1988Sjohnlev for (ifr = ifconf.lifc_req; ifr < &ifconf.lifc_req[ifconf.lifc_len /
195843e1988Sjohnlev sizeof (ifconf.lifc_req[0])]; ifr++) {
1968acf8916Sokie struct lifreq flifr;
1978acf8916Sokie struct sockaddr_in *sin;
198843e1988Sjohnlev
199843e1988Sjohnlev if (strchr(ifr->lifr_name, ':') != NULL)
200843e1988Sjohnlev continue; /* skip logical interfaces */
201843e1988Sjohnlev
2028acf8916Sokie isv4 = ifr->lifr_addr.ss_family == AF_INET;
2038acf8916Sokie
2048acf8916Sokie (void) strncpy(flifr.lifr_name, ifr->lifr_name, LIFNAMSIZ);
2058acf8916Sokie
2068acf8916Sokie if (ioctl(isv4 ? s4 : s6, SIOCGLIFFLAGS, &flifr) < 0) {
2078acf8916Sokie (void) fprintf(stderr, "%s: SIOCGLIFFLAGS: %s\n",
208843e1988Sjohnlev program, strerror(errno));
209843e1988Sjohnlev continue;
210843e1988Sjohnlev }
211843e1988Sjohnlev
2128acf8916Sokie if (!(flifr.lifr_flags & IFF_UP) ||
2138acf8916Sokie (flifr.lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT)))
214843e1988Sjohnlev continue;
215843e1988Sjohnlev
2168acf8916Sokie /*
2178acf8916Sokie * For the "nfs rarp" and "nfs bootprops"
2188acf8916Sokie * cases, we assume that the first non-virtual
2198acf8916Sokie * IFF_UP interface with a non-zero address is
2208acf8916Sokie * the one used.
2218acf8916Sokie *
2228acf8916Sokie * For the non-zero address check, we only check
2238acf8916Sokie * v4 interfaces, as it's not possible to set the
2248acf8916Sokie * the first logical interface (the only ones we
2258acf8916Sokie * look at here) to ::0; that interface must have
2268acf8916Sokie * a link-local address.
2278acf8916Sokie *
2288acf8916Sokie * If we don't find an IFF_UP interface with a
2298acf8916Sokie * non-zero address, we'll return the last IFF_UP
2308acf8916Sokie * interface seen.
2318acf8916Sokie *
2328acf8916Sokie * Since the order of the interfaces retrieved
2338acf8916Sokie * via SIOCGLIFCONF is not deterministic, this
2348acf8916Sokie * is largely silliness, but (a) "it's always
2358acf8916Sokie * been this way", and (b) no one consumes the
2368acf8916Sokie * interface name in the RARP case anyway.
2378acf8916Sokie */
2388acf8916Sokie
2398acf8916Sokie found_one = B_TRUE;
2408acf8916Sokie (void) strncpy(interface, ifr->lifr_name, LIFNAMSIZ);
2418acf8916Sokie *dhcpflag = (flifr.lifr_flags & IFF_DHCPRUNNING) != 0;
2428acf8916Sokie sin = (struct sockaddr_in *)&ifr->lifr_addr;
2438acf8916Sokie if (isv4 && (sin->sin_addr.s_addr == INADDR_ANY)) {
2448acf8916Sokie /* keep looking for a non-zero address */
2458acf8916Sokie continue;
246843e1988Sjohnlev }
2478acf8916Sokie return (interface);
248843e1988Sjohnlev }
249843e1988Sjohnlev
2508acf8916Sokie return (found_one ? interface : NULL);
251843e1988Sjohnlev }
252843e1988Sjohnlev
253843e1988Sjohnlev /* ARGSUSED */
254843e1988Sjohnlev int
main(int argc,char * argv[])255843e1988Sjohnlev main(int argc, char *argv[])
256843e1988Sjohnlev {
257843e1988Sjohnlev char *root, *interface, *strategy, dummy;
258843e1988Sjohnlev long len;
2598acf8916Sokie boolean_t dhcp_running = B_FALSE;
260843e1988Sjohnlev
261843e1988Sjohnlev root = interface = strategy = NULL;
262843e1988Sjohnlev program = argv[0];
263843e1988Sjohnlev
264843e1988Sjohnlev root = get_root_fstype();
265843e1988Sjohnlev
2668acf8916Sokie if (!open_sockets()) {
2678acf8916Sokie (void) fprintf(stderr,
2688acf8916Sokie "%s: cannot get interface information\n", program);
2698acf8916Sokie return (2);
2708acf8916Sokie }
2718acf8916Sokie
272843e1988Sjohnlev /*
273843e1988Sjohnlev * If diskless, perhaps boot properties were used to configure
274843e1988Sjohnlev * the interface.
275843e1988Sjohnlev */
276843e1988Sjohnlev if ((strcmp(root, "nfs") == 0) && boot_properties_present()) {
277843e1988Sjohnlev strategy = "bootprops";
278843e1988Sjohnlev
2798acf8916Sokie interface = get_first_interface(&dhcp_running);
280843e1988Sjohnlev if (interface == NULL) {
281843e1988Sjohnlev (void) fprintf(stderr,
282843e1988Sjohnlev "%s: cannot identify root interface.\n", program);
2838acf8916Sokie close_sockets();
284843e1988Sjohnlev return (2);
285843e1988Sjohnlev }
286843e1988Sjohnlev
287843e1988Sjohnlev (void) printf("%s %s %s\n", root, interface, strategy);
2888acf8916Sokie close_sockets();
289843e1988Sjohnlev return (0);
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate
2927c478bd9Sstevel@tonic-gate /*
2937c478bd9Sstevel@tonic-gate * Handle the simple case where diskless dhcp tells us everything
2947c478bd9Sstevel@tonic-gate * we need to know.
2957c478bd9Sstevel@tonic-gate */
2967c478bd9Sstevel@tonic-gate if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) {
2977c478bd9Sstevel@tonic-gate /* interface is first thing in cache. */
2987c478bd9Sstevel@tonic-gate strategy = "dhcp";
2997c478bd9Sstevel@tonic-gate interface = alloca(len);
3007c478bd9Sstevel@tonic-gate (void) sysinfo(SI_DHCP_CACHE, interface, len);
3017c478bd9Sstevel@tonic-gate (void) printf("%s %s %s\n", root, interface, strategy);
3028acf8916Sokie close_sockets();
3037c478bd9Sstevel@tonic-gate return (0);
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate
3067c478bd9Sstevel@tonic-gate /*
3077c478bd9Sstevel@tonic-gate * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle
3087c478bd9Sstevel@tonic-gate * "ufs rarp" (consumers are coded to deal with this reality), so
3097c478bd9Sstevel@tonic-gate * there are three possible situations:
3107c478bd9Sstevel@tonic-gate *
311*bd93c05dSAlexander Eremin * 1. We're either "ufs dhcp" or "zfs dhcp" if there are any
312*bd93c05dSAlexander Eremin * interfaces which have obtained their addresses through DHCP.
313*bd93c05dSAlexander Eremin * That is, if there are any IFF_UP and non-IFF_VIRTUAL
314*bd93c05dSAlexander Eremin * interfaces also have IFF_DHCPRUNNING set.
3157c478bd9Sstevel@tonic-gate *
316*bd93c05dSAlexander Eremin * 2. We're either "ufs none" or "zfs none" if our filesystem
317*bd93c05dSAlexander Eremin * is local and there are no interfaces which have obtained
318*bd93c05dSAlexander Eremin * their addresses through DHCP.
3197c478bd9Sstevel@tonic-gate *
3207c478bd9Sstevel@tonic-gate * 3. We're "nfs rarp" if our filesystem is remote and there's
3217c478bd9Sstevel@tonic-gate * at least IFF_UP non-IFF_VIRTUAL interface (which there
3227c478bd9Sstevel@tonic-gate * *must* be, since we're running over NFS somehow), then
3237c478bd9Sstevel@tonic-gate * it must be RARP since SI_DHCP_CACHE call above failed.
3247c478bd9Sstevel@tonic-gate * It's too bad there isn't an IFF_RARPRUNNING flag.
3257c478bd9Sstevel@tonic-gate */
3267c478bd9Sstevel@tonic-gate
3278acf8916Sokie interface = get_first_interface(&dhcp_running);
3287c478bd9Sstevel@tonic-gate
3298acf8916Sokie if (dhcp_running)
330843e1988Sjohnlev strategy = "dhcp";
3317c478bd9Sstevel@tonic-gate
332*bd93c05dSAlexander Eremin if (strcmp(root, "nfs") == 0) {
3337c478bd9Sstevel@tonic-gate if (interface == NULL) {
3347c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
335843e1988Sjohnlev "%s: cannot identify root interface.\n", program);
3368acf8916Sokie close_sockets();
3377c478bd9Sstevel@tonic-gate return (2);
3387c478bd9Sstevel@tonic-gate }
3397c478bd9Sstevel@tonic-gate if (strategy == NULL)
3407c478bd9Sstevel@tonic-gate strategy = "rarp"; /* must be rarp/bootparams */
3417c478bd9Sstevel@tonic-gate } else {
3427c478bd9Sstevel@tonic-gate if (interface == NULL || strategy == NULL)
3437c478bd9Sstevel@tonic-gate interface = strategy = "none";
3447c478bd9Sstevel@tonic-gate }
3457c478bd9Sstevel@tonic-gate
3467c478bd9Sstevel@tonic-gate (void) printf("%s %s %s\n", root, interface, strategy);
3478acf8916Sokie close_sockets();
3487c478bd9Sstevel@tonic-gate return (0);
3497c478bd9Sstevel@tonic-gate }
350