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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * This program does the following:
32  *
33  * a) Returns:
34  *	0	- if the program successfully determined the net strategy.
35  *	!0	- if an error occurred.
36  *
37  * b) If the program is successful, it prints three tokens to
38  *    stdout: <root fs type> <interface name> <net config strategy>.
39  *    where:
40  *	<root fs type>		-	"nfs" or "ufs"
41  *	<interface name>	-	"hme0" or "none"
42  *	<net config strategy>	-	"dhcp", "rarp", 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", or "none" depending
54  *	on which strategy was used to configure the interface. Is "none" if
55  *	no interface was configured using a net-based strategy.
56  *
57  * CAVEATS: what about autoclient systems? XXX
58  */
59 
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <string.h>
64 #include <sys/types.h>
65 #include <errno.h>
66 #include <alloca.h>
67 #include <sys/systeminfo.h>
68 #include <sys/socket.h>
69 #include <sys/sockio.h>
70 #include <net/if.h>
71 #include <sys/statvfs.h>
72 
73 /* ARGSUSED */
74 int
75 main(int argc, char *argv[])
76 {
77 	struct statvfs	vfs;
78 	char		*root, *interface, *strategy, dummy;
79 	long		len;
80 	int		fd, nifs, nlifr;
81 	struct lifreq	*lifr;
82 	struct lifconf	lifc;
83 
84 	/* root location */
85 	if (statvfs("/", &vfs) < 0)
86 		root = "none";
87 	else {
88 		if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0)
89 			vfs.f_basetype[sizeof ("nfs") - 1] = '\0';
90 		root = vfs.f_basetype;
91 	}
92 
93 	/*
94 	 * Handle the simple case where diskless dhcp tells us everything
95 	 * we need to know.
96 	 */
97 	if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) {
98 		/* interface is first thing in cache. */
99 		strategy = "dhcp";
100 		interface = alloca(len);
101 		(void) sysinfo(SI_DHCP_CACHE, interface, len);
102 		(void) printf("%s %s %s\n", root, interface, strategy);
103 		return (0);
104 	}
105 
106 	/*
107 	 * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle
108 	 * "ufs rarp" (consumers are coded to deal with this reality), so
109 	 * there are three possible situations:
110 	 *
111 	 *	1. We're "ufs dhcp" if there are any interfaces which have
112 	 *	   obtained their addresses through DHCP.  That is, if there
113 	 *	   are any IFF_UP and non-IFF_VIRTUAL interfaces also have
114 	 *	   IFF_DHCPRUNNING set.
115 	 *
116 	 *	2. We're "ufs none" if our filesystem is local and there
117 	 *	   are no interfaces which have obtained their addresses
118 	 *	   through DHCP.
119 	 *
120 	 *	3. We're "nfs rarp" if our filesystem is remote and there's
121 	 *	   at least IFF_UP non-IFF_VIRTUAL interface (which there
122 	 *	   *must* be, since we're running over NFS somehow), then
123 	 *	   it must be RARP since SI_DHCP_CACHE call above failed.
124 	 *	   It's too bad there isn't an IFF_RARPRUNNING flag.
125 	 */
126 
127 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
128 		(void) fprintf(stderr, "%s: socket: %s\n", argv[0],
129 		    strerror(errno));
130 		return (2);
131 	}
132 
133 	if (ioctl(fd, SIOCGIFNUM, &nifs) < 0) {
134 		(void) fprintf(stderr, "%s: SIOCGIFNUM: %s\n", argv[0],
135 		    strerror(errno));
136 		(void) close(fd);
137 		return (2);
138 	}
139 
140 	lifc.lifc_len = nifs * sizeof (struct lifreq);
141 	lifc.lifc_buf = alloca(lifc.lifc_len);
142 	lifc.lifc_flags = 0;
143 	lifc.lifc_family = AF_INET;
144 
145 	if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0) {
146 		(void) fprintf(stderr, "%s: SIOCGLIFCONF: %s\n", argv[0],
147 		    strerror(errno));
148 		(void) close(fd);
149 		return (2);
150 	}
151 
152 	strategy = NULL;
153 	interface = NULL;
154 
155 	nlifr = lifc.lifc_len / sizeof (struct lifreq);
156 	for (lifr = lifc.lifc_req; nlifr > 0; lifr++, nlifr--) {
157 
158 		if (strchr(lifr->lifr_name, ':') != NULL)
159 			continue;	/* skip logical interfaces */
160 
161 		if (ioctl(fd, SIOCGLIFFLAGS, lifr) < 0) {
162 			(void) fprintf(stderr, "%s: SIOCGLIFFLAGS: %s\n",
163 			    argv[0], strerror(errno));
164 			continue;
165 		}
166 
167 		if (lifr->lifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT))
168 			continue;
169 
170 		if (lifr->lifr_flags & IFF_UP) {
171 			/*
172 			 * For the "nfs rarp" case, we assume that the first
173 			 * IFF_UP interface is the one using RARP, so stash
174 			 * away the first interface in case we need it.
175 			 *
176 			 * Since the order of the interfaces retrieved via
177 			 * SIOCGLIFCONF is not deterministic, this is largely
178 			 * silliness, but (a) "it's always been this way", (b)
179 			 * machines booted via diskless RARP typically only
180 			 * have one interface, and (c) no one consumes the
181 			 * interface name in the RARP case anyway.
182 			 */
183 			if (interface == NULL)
184 				interface = lifr->lifr_name;
185 
186 			if (lifr->lifr_flags & IFF_DHCPRUNNING) {
187 				interface = lifr->lifr_name;
188 				strategy = "dhcp";
189 				break;
190 			}
191 		}
192 	}
193 
194 	(void) close(fd);
195 
196 	if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) {
197 		if (interface == NULL) {
198 			(void) fprintf(stderr,
199 			    "%s: cannot identify root interface.\n", argv[0]);
200 			return (2);
201 		}
202 		if (strategy == NULL)
203 			strategy = "rarp";	/*  must be rarp/bootparams */
204 	} else {
205 		if (interface == NULL || strategy == NULL)
206 			interface = strategy = "none";
207 	}
208 
209 	(void) printf("%s %s %s\n", root, interface, strategy);
210 	return (0);
211 }
212