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