xref: /illumos-gate/usr/src/boot/sys/boot/common/dev_net.c (revision 46baa6ff)
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
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 f->f_devdata.
119  * This is declared with variable arguments...
120  */
121 static int
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 	f->f_devdata = &netdev_sock;
197 	return (error);
198 }
199 
200 static int
201 net_close(struct open_file *f)
202 {
203 
204 #ifdef	NETIF_DEBUG
205 	if (debug)
206 		printf("%s: opens=%d\n", __func__, netdev_opens);
207 #endif
208 
209 	f->f_devdata = NULL;
210 
211 	return (0);
212 }
213 
214 static void
215 net_cleanup(void)
216 {
217 
218 	if (netdev_sock >= 0) {
219 #ifdef	NETIF_DEBUG
220 		if (debug)
221 			printf("%s: calling netif_close()\n", __func__);
222 #endif
223 		rootip.s_addr = 0;
224 		free(netdev_name);
225 		netif_close(netdev_sock);
226 		netdev_sock = -1;
227 	}
228 }
229 
230 static int
231 net_strategy(void *devdata __unused, int rw __unused, daddr_t blk __unused,
232     size_t size __unused, char *buf __unused, size_t *rsize __unused)
233 {
234 
235 	return (EIO);
236 }
237 
238 /*
239  * Get info for NFS boot: our IP address, our hostname,
240  * server IP address, and our root path on the server.
241  * There are two ways to do this:  The old, Sun way,
242  * and the more modern, BOOTP/DHCP way. (RFC951, RFC1048)
243  */
244 
245 extern n_long ip_convertaddr(char *p);
246 
247 static int
248 net_getparams(int sock)
249 {
250 	char buf[MAXHOSTNAMELEN];
251 	n_long rootaddr, smask;
252 
253 	/*
254 	 * Try to get boot info using BOOTP/DHCP.  If we succeed, then
255 	 * the server IP address, gateway, and root path will all
256 	 * be initialized.  If any remain uninitialized, we will
257 	 * use RARP and RPC/bootparam (the Sun way) to get them.
258 	 */
259 	bootp(sock);
260 	if (myip.s_addr != 0)
261 		goto exit;
262 #ifdef	NETIF_DEBUG
263 	if (debug)
264 		printf("%s: BOOTP failed, trying RARP/RPC...\n", __func__);
265 #endif
266 
267 	/*
268 	 * Use RARP to get our IP address.  This also sets our
269 	 * netmask to the "natural" default for our address.
270 	 */
271 	if (rarp_getipaddress(sock)) {
272 		printf("%s: RARP failed\n", __func__);
273 		return (EIO);
274 	}
275 	printf("%s: client addr: %s\n", __func__, inet_ntoa(myip));
276 
277 	/* Get our hostname, server IP address, gateway. */
278 	if (bp_whoami(sock)) {
279 		printf("%s: bootparam/whoami RPC failed\n", __func__);
280 		return (EIO);
281 	}
282 #ifdef	NETIF_DEBUG
283 	if (debug)
284 		printf("%s: client name: %s\n", __func__, hostname);
285 #endif
286 
287 	/*
288 	 * Ignore the gateway from whoami (unreliable).
289 	 * Use the "gateway" parameter instead.
290 	 */
291 	smask = 0;
292 	gateip.s_addr = 0;
293 	if (bp_getfile(sock, "gateway", &gateip, buf) == 0) {
294 		/* Got it!  Parse the netmask. */
295 		smask = ip_convertaddr(buf);
296 	}
297 	if (smask) {
298 		netmask = smask;
299 #ifdef	NETIF_DEBUG
300 		if (debug)
301 			printf("%s: subnet mask: %s\n", __func__,
302 			    intoa(netmask));
303 #endif
304 	}
305 #ifdef	NETIF_DEBUG
306 	if (gateip.s_addr && debug)
307 		printf("%s: net gateway: %s\n", __func__, inet_ntoa(gateip));
308 #endif
309 
310 	/* Get the root server and pathname. */
311 	if (bp_getfile(sock, "root", &rootip, rootpath)) {
312 		printf("%s: bootparam/getfile RPC failed\n", __func__);
313 		return (EIO);
314 	}
315 exit:
316 	if ((rootaddr = net_parse_rootpath()) != INADDR_NONE)
317 		rootip.s_addr = rootaddr;
318 
319 #ifdef	NETIF_DEBUG
320 	if (debug) {
321 		printf("%s: server addr: %s\n", __func__,
322 		    inet_ntoa(rootip));
323 		printf("%s: server path: %s\n", __func__, rootpath);
324 	}
325 #endif
326 
327 	return (0);
328 }
329 
330 static int
331 net_print(int verbose)
332 {
333 	struct netif_driver *drv;
334 	int i, d, cnt;
335 	int ret = 0;
336 
337 	if (netif_drivers[0] == NULL)
338 		return (ret);
339 
340 	printf("%s devices:", netdev.dv_name);
341 	if ((ret = pager_output("\n")) != 0)
342 		return (ret);
343 
344 	cnt = 0;
345 	for (d = 0; netif_drivers[d]; d++) {
346 		drv = netif_drivers[d];
347 		for (i = 0; i < drv->netif_nifs; i++) {
348 			printf("\t%s%d:", netdev.dv_name, cnt++);
349 			if (verbose) {
350 				printf(" (%s%d)", drv->netif_bname,
351 				    drv->netif_ifs[i].dif_unit);
352 			}
353 			if ((ret = pager_output("\n")) != 0)
354 				return (ret);
355 		}
356 	}
357 	return (ret);
358 }
359 
360 /*
361  * Parses the rootpath if present
362  *
363  * The rootpath format can be in the form
364  * <scheme>://IPv4/path
365  * <scheme>:/path
366  *
367  * For compatibility with previous behaviour it also accepts as an NFS scheme
368  * IPv4:/path
369  * /path
370  *
371  * If an IPv4 address has been specified, it will be stripped out and passed
372  * out as the return value of this function in network byte order.
373  *
374  * If no rootpath is present then we will default to TFTP.
375  *
376  * If no global default scheme has been specified and no scheme has been
377  * specified, we will assume that this is an NFS URL.
378  *
379  * The pathname will be stored in the global variable rootpath.
380  */
381 uint32_t
382 net_parse_rootpath(void)
383 {
384 	n_long addr = htonl(INADDR_NONE);
385 	size_t i;
386 	char ip[FNAME_SIZE];
387 	char *ptr, *val;
388 
389 	netproto = NET_NONE;
390 
391 	for (i = 0; i < nitems(uri_schemes); i++) {
392 		if (strncmp(rootpath, uri_schemes[i].scheme,
393 		    strlen(uri_schemes[i].scheme)) != 0)
394 			continue;
395 
396 		netproto = uri_schemes[i].proto;
397 		break;
398 	}
399 	ptr = rootpath;
400 	/* Fallback for compatibility mode */
401 	if (netproto == NET_NONE) {
402 		if (strcmp(rootpath, "/") == 0) {
403 			netproto = NET_TFTP;
404 		} else {
405 			netproto = NET_NFS;
406 			(void) strsep(&ptr, ":");
407 			if (ptr != NULL) {
408 				addr = inet_addr(rootpath);
409 				bcopy(ptr, rootpath, strlen(ptr) + 1);
410 			}
411 		}
412 	} else {
413 		ptr += strlen(uri_schemes[i].scheme);
414 		if (*ptr == '/') {
415 			/*
416 			 * We are in the form <scheme>://, we do expect an ip.
417 			 */
418 			ptr++;
419 			/*
420 			 * XXX when http will be there we will need to check for
421 			 * a port, but right now we do not need it yet.
422 			 * Also will need rework for IPv6.
423 			 */
424 			val = strchr(ptr, '/');
425 			if (val == NULL) {
426 				/* If no pathname component, default to / */
427 				strlcat(rootpath, "/", sizeof (rootpath));
428 				val = strchr(ptr, '/');
429 			}
430 			if (val != NULL) {
431 				snprintf(ip, sizeof (ip), "%.*s",
432 				    (int)((uintptr_t)val - (uintptr_t)ptr),
433 				    ptr);
434 				addr = inet_addr(ip);
435 				if (addr == htonl(INADDR_NONE)) {
436 					printf("Bad IP address: %s\n", ip);
437 				}
438 				bcopy(val, rootpath, strlen(val) + 1);
439 			}
440 		} else {
441 			ptr--;
442 			bcopy(ptr, rootpath, strlen(ptr) + 1);
443 		}
444 	}
445 
446 	return (addr);
447 }
448