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