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