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 dev->d_opendata. 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 dev->d_opendata = &netdev_sock; 197 return (error); 198 } 199 200 static int 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 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 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 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 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 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