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