xref: /illumos-gate/usr/src/boot/common/dev_net.c (revision 22028508fd28d36ff74dc02c5774a8ba1f0db045)
1 /*
2  * Copyright (c) 1997 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Gordon W. Ross.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
32  */
33 
34 #include <sys/cdefs.h>
35 
36 /*
37  * This module implements a "raw device" interface suitable for
38  * use by the stand-alone I/O library NFS code.  This interface
39  * does not support any "block" access, and exists only for the
40  * purpose of initializing the network interface, getting boot
41  * parameters, and performing the NFS mount.
42  *
43  * At open time, this does:
44  *
45  * find interface      - netif_open()
46  * RARP for IP address - rarp_getipaddress()
47  * RPC/bootparams      - callrpc(d, RPC_BOOTPARAMS, ...)
48  * RPC/mountd          - nfs_mount(sock, ip, path)
49  *
50  * the root file handle from mountd is saved in a global
51  * for use by the NFS open code (NFS/lookup).
52  */
53 
54 #include <machine/stdarg.h>
55 #include <sys/param.h>
56 #include <sys/socket.h>
57 #include <net/if.h>
58 #include <netinet/in.h>
59 #include <netinet/in_systm.h>
60 
61 #include <stand.h>
62 #include <stddef.h>
63 #include <string.h>
64 #include <net.h>
65 #include <netif.h>
66 #include <bootp.h>
67 #include <bootparam.h>
68 
69 #include "dev_net.h"
70 #include "bootstrap.h"
71 
72 #ifdef	NETIF_DEBUG
73 int debug = 0;
74 #endif
75 
76 static char *netdev_name;
77 static int netdev_sock = -1;
78 static int netdev_opens;
79 
80 static int	net_init(void);
81 static int	net_open(struct open_file *, ...);
82 static int	net_close(struct open_file *);
83 static void	net_cleanup(void);
84 static int	net_strategy(void *, int, daddr_t, size_t, char *, size_t *);
85 static int	net_print(int);
86 
87 static int net_getparams(int sock);
88 
89 struct devsw netdev = {
90 	"net",
91 	DEVT_NET,
92 	net_init,
93 	net_strategy,
94 	net_open,
95 	net_close,
96 	noioctl,
97 	net_print,
98 	net_cleanup
99 };
100 
101 static struct uri_scheme {
102 	const char *scheme;
103 	int proto;
104 } uri_schemes[] = {
105 	{ "tftp:/", NET_TFTP },
106 	{ "nfs:/", NET_NFS },
107 };
108 
109 static int
net_init(void)110 net_init(void)
111 {
112 
113 	return (0);
114 }
115 
116 /*
117  * Called by devopen after it sets f->f_dev to our devsw entry.
118  * This opens the low-level device and sets dev->d_opendata.
119  * This is declared with variable arguments...
120  */
121 static int
net_open(struct open_file * f,...)122 net_open(struct open_file *f, ...)
123 {
124 	struct iodesc *d;
125 	va_list args;
126 	struct devdesc *dev;
127 	const char *devname;	/* Device part of file name (or NULL). */
128 	int error = 0;
129 
130 	va_start(args, f);
131 	dev = va_arg(args, struct devdesc *);
132 	va_end(args);
133 
134 	devname = dev->d_dev->dv_name;
135 	/* Before opening another interface, close the previous one first. */
136 	if (netdev_sock >= 0 && strcmp(devname, netdev_name) != 0)
137 		net_cleanup();
138 
139 	/* On first open, do netif open, mount, etc. */
140 	if (netdev_opens == 0) {
141 		/* Find network interface. */
142 		if (netdev_sock < 0) {
143 			netdev_sock = netif_open(dev);
144 			if (netdev_sock < 0) {
145 				printf("%s: netif_open() failed\n", __func__);
146 				return (ENXIO);
147 			}
148 			netdev_name = strdup(devname);
149 #ifdef	NETIF_DEBUG
150 			if (debug)
151 				printf("%s: netif_open() succeeded\n",
152 				    __func__);
153 #endif
154 		}
155 		/*
156 		 * If network params were not set by netif_open(), try to get
157 		 * them via bootp, rarp, etc.
158 		 */
159 		if (rootip.s_addr == 0) {
160 			/* Get root IP address, and path, etc. */
161 			error = net_getparams(netdev_sock);
162 			if (error) {
163 				/* getparams makes its own noise */
164 				free(netdev_name);
165 				netif_close(netdev_sock);
166 				netdev_sock = -1;
167 				return (error);
168 			}
169 		}
170 		/*
171 		 * Set the variables required by the kernel's nfs_diskless
172 		 * mechanism.  This is the minimum set of variables required to
173 		 * mount a root filesystem without needing to obtain additional
174 		 * info from bootp or other sources.
175 		 */
176 		d = socktodesc(netdev_sock);
177 		setenv("boot.netif.hwaddr", ether_sprintf(d->myea), 1);
178 		setenv("boot.netif.ip", inet_ntoa(myip), 1);
179 		setenv("boot.netif.netmask", intoa(netmask), 1);
180 		setenv("boot.netif.gateway", inet_ntoa(gateip), 1);
181 		setenv("boot.netif.server", inet_ntoa(rootip), 1);
182 		if (netproto == NET_TFTP) {
183 			setenv("boot.tftproot.server", inet_ntoa(rootip), 1);
184 			setenv("boot.tftproot.path", rootpath, 1);
185 		} else {
186 			setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
187 			setenv("boot.nfsroot.path", rootpath, 1);
188 		}
189 		if (intf_mtu != 0) {
190 			char mtu[16];
191 			snprintf(mtu, sizeof (mtu), "%u", intf_mtu);
192 			setenv("boot.netif.mtu", mtu, 1);
193 		}
194 	}
195 	netdev_opens++;
196 	dev->d_opendata = &netdev_sock;
197 	return (error);
198 }
199 
200 static int
net_close(struct open_file * f)201 net_close(struct open_file *f)
202 {
203 	struct devdesc *dev;
204 
205 #ifdef	NETIF_DEBUG
206 	if (debug)
207 		printf("%s: opens=%d\n", __func__, netdev_opens);
208 #endif
209 
210 	dev = f->f_devdata;
211 	dev->d_opendata = NULL;
212 
213 	return (0);
214 }
215 
216 static void
net_cleanup(void)217 net_cleanup(void)
218 {
219 
220 	if (netdev_sock >= 0) {
221 #ifdef	NETIF_DEBUG
222 		if (debug)
223 			printf("%s: calling netif_close()\n", __func__);
224 #endif
225 		rootip.s_addr = 0;
226 		free(netdev_name);
227 		netif_close(netdev_sock);
228 		netdev_sock = -1;
229 	}
230 }
231 
232 static int
net_strategy(void * devdata __unused,int rw __unused,daddr_t blk __unused,size_t size __unused,char * buf __unused,size_t * rsize __unused)233 net_strategy(void *devdata __unused, int rw __unused, daddr_t blk __unused,
234     size_t size __unused, char *buf __unused, size_t *rsize __unused)
235 {
236 
237 	return (EIO);
238 }
239 
240 /*
241  * Get info for NFS boot: our IP address, our hostname,
242  * server IP address, and our root path on the server.
243  * There are two ways to do this:  The old, Sun way,
244  * and the more modern, BOOTP/DHCP way. (RFC951, RFC1048)
245  */
246 
247 extern n_long ip_convertaddr(char *p);
248 
249 static int
net_getparams(int sock)250 net_getparams(int sock)
251 {
252 	char buf[MAXHOSTNAMELEN];
253 	n_long rootaddr, smask;
254 
255 	/*
256 	 * Try to get boot info using BOOTP/DHCP.  If we succeed, then
257 	 * the server IP address, gateway, and root path will all
258 	 * be initialized.  If any remain uninitialized, we will
259 	 * use RARP and RPC/bootparam (the Sun way) to get them.
260 	 */
261 	bootp(sock);
262 	if (myip.s_addr != 0)
263 		goto exit;
264 #ifdef	NETIF_DEBUG
265 	if (debug)
266 		printf("%s: BOOTP failed, trying RARP/RPC...\n", __func__);
267 #endif
268 
269 	/*
270 	 * Use RARP to get our IP address.  This also sets our
271 	 * netmask to the "natural" default for our address.
272 	 */
273 	if (rarp_getipaddress(sock)) {
274 		printf("%s: RARP failed\n", __func__);
275 		return (EIO);
276 	}
277 	printf("%s: client addr: %s\n", __func__, inet_ntoa(myip));
278 
279 	/* Get our hostname, server IP address, gateway. */
280 	if (bp_whoami(sock)) {
281 		printf("%s: bootparam/whoami RPC failed\n", __func__);
282 		return (EIO);
283 	}
284 #ifdef	NETIF_DEBUG
285 	if (debug)
286 		printf("%s: client name: %s\n", __func__, hostname);
287 #endif
288 
289 	/*
290 	 * Ignore the gateway from whoami (unreliable).
291 	 * Use the "gateway" parameter instead.
292 	 */
293 	smask = 0;
294 	gateip.s_addr = 0;
295 	if (bp_getfile(sock, "gateway", &gateip, buf) == 0) {
296 		/* Got it!  Parse the netmask. */
297 		smask = ip_convertaddr(buf);
298 	}
299 	if (smask) {
300 		netmask = smask;
301 #ifdef	NETIF_DEBUG
302 		if (debug)
303 			printf("%s: subnet mask: %s\n", __func__,
304 			    intoa(netmask));
305 #endif
306 	}
307 #ifdef	NETIF_DEBUG
308 	if (gateip.s_addr && debug)
309 		printf("%s: net gateway: %s\n", __func__, inet_ntoa(gateip));
310 #endif
311 
312 	/* Get the root server and pathname. */
313 	if (bp_getfile(sock, "root", &rootip, rootpath)) {
314 		printf("%s: bootparam/getfile RPC failed\n", __func__);
315 		return (EIO);
316 	}
317 exit:
318 	if ((rootaddr = net_parse_rootpath()) != INADDR_NONE)
319 		rootip.s_addr = rootaddr;
320 
321 #ifdef	NETIF_DEBUG
322 	if (debug) {
323 		printf("%s: server addr: %s\n", __func__,
324 		    inet_ntoa(rootip));
325 		printf("%s: server path: %s\n", __func__, rootpath);
326 	}
327 #endif
328 
329 	return (0);
330 }
331 
332 static int
net_print(int verbose)333 net_print(int verbose)
334 {
335 	struct netif_driver *drv;
336 	int i, d, cnt;
337 	int ret = 0;
338 
339 	if (netif_drivers[0] == NULL)
340 		return (ret);
341 
342 	printf("%s devices:", netdev.dv_name);
343 	if ((ret = pager_output("\n")) != 0)
344 		return (ret);
345 
346 	cnt = 0;
347 	for (d = 0; netif_drivers[d]; d++) {
348 		drv = netif_drivers[d];
349 		for (i = 0; i < drv->netif_nifs; i++) {
350 			printf("\t%s%d:", netdev.dv_name, cnt++);
351 			if (verbose) {
352 				printf(" (%s%d)", drv->netif_bname,
353 				    drv->netif_ifs[i].dif_unit);
354 			}
355 			if ((ret = pager_output("\n")) != 0)
356 				return (ret);
357 		}
358 	}
359 	return (ret);
360 }
361 
362 /*
363  * Parses the rootpath if present
364  *
365  * The rootpath format can be in the form
366  * <scheme>://IPv4/path
367  * <scheme>:/path
368  *
369  * For compatibility with previous behaviour it also accepts as an NFS scheme
370  * IPv4:/path
371  * /path
372  *
373  * If an IPv4 address has been specified, it will be stripped out and passed
374  * out as the return value of this function in network byte order.
375  *
376  * If no rootpath is present then we will default to TFTP.
377  *
378  * If no global default scheme has been specified and no scheme has been
379  * specified, we will assume that this is an NFS URL.
380  *
381  * The pathname will be stored in the global variable rootpath.
382  */
383 uint32_t
net_parse_rootpath(void)384 net_parse_rootpath(void)
385 {
386 	n_long addr = htonl(INADDR_NONE);
387 	size_t i;
388 	char ip[FNAME_SIZE];
389 	char *ptr, *val;
390 
391 	netproto = NET_NONE;
392 
393 	for (i = 0; i < nitems(uri_schemes); i++) {
394 		if (strncmp(rootpath, uri_schemes[i].scheme,
395 		    strlen(uri_schemes[i].scheme)) != 0)
396 			continue;
397 
398 		netproto = uri_schemes[i].proto;
399 		break;
400 	}
401 	ptr = rootpath;
402 	/* Fallback for compatibility mode */
403 	if (netproto == NET_NONE) {
404 		if (strcmp(rootpath, "/") == 0) {
405 			netproto = NET_TFTP;
406 		} else {
407 			netproto = NET_NFS;
408 			(void) strsep(&ptr, ":");
409 			if (ptr != NULL) {
410 				addr = inet_addr(rootpath);
411 				bcopy(ptr, rootpath, strlen(ptr) + 1);
412 			}
413 		}
414 	} else {
415 		ptr += strlen(uri_schemes[i].scheme);
416 		if (*ptr == '/') {
417 			/*
418 			 * We are in the form <scheme>://, we do expect an ip.
419 			 */
420 			ptr++;
421 			/*
422 			 * XXX when http will be there we will need to check for
423 			 * a port, but right now we do not need it yet.
424 			 * Also will need rework for IPv6.
425 			 */
426 			val = strchr(ptr, '/');
427 			if (val == NULL) {
428 				/* If no pathname component, default to / */
429 				strlcat(rootpath, "/", sizeof (rootpath));
430 				val = strchr(ptr, '/');
431 			}
432 			if (val != NULL) {
433 				snprintf(ip, sizeof (ip), "%.*s",
434 				    (int)((uintptr_t)val - (uintptr_t)ptr),
435 				    ptr);
436 				addr = inet_addr(ip);
437 				if (addr == htonl(INADDR_NONE)) {
438 					printf("Bad IP address: %s\n", ip);
439 				}
440 				bcopy(val, rootpath, strlen(val) + 1);
441 			}
442 		} else {
443 			ptr--;
444 			bcopy(ptr, rootpath, strlen(ptr) + 1);
445 		}
446 	}
447 
448 	return (addr);
449 }
450