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 /*- 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 <sys/param.h> 51 #include <sys/socket.h> 52 #include <sys/stdarg.h> 53 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 #ifndef NETPROTO_DEFAULT 70 # define NETPROTO_DEFAULT NET_NFS 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 static void net_setparams(int sock); 86 87 struct devsw netdev = { 88 .dv_name = "net", 89 .dv_type = DEVT_NET, 90 .dv_init = net_init, 91 .dv_strategy = net_strategy, 92 .dv_open = net_open, 93 .dv_close = net_close, 94 .dv_ioctl = noioctl, 95 .dv_print = net_print, 96 .dv_cleanup = net_cleanup, 97 }; 98 99 static struct uri_scheme { 100 const char *scheme; 101 int proto; 102 } uri_schemes[] = { 103 { "tftp:/", NET_TFTP }, 104 { "nfs:/", NET_NFS }, 105 }; 106 107 static int 108 net_init(void) 109 { 110 111 return (0); 112 } 113 114 /* 115 * Called by devopen after it sets f->f_dev to our devsw entry. 116 * This opens the low-level device and sets dev->d_opendata. 117 * This is declared with variable arguments... 118 */ 119 static int 120 net_open(struct open_file *f, ...) 121 { 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 DEBUG_PRINTF(1,("%s: netif_open() succeeded %#x\n", 147 __func__, rootip.s_addr)); 148 } 149 /* 150 * If network params were not set by netif_open(), try to get 151 * them via bootp, rarp, etc. 152 */ 153 if (rootip.s_addr == 0) { 154 /* Get root IP address, and path, etc. */ 155 error = net_getparams(netdev_sock); 156 if (error) { 157 /* getparams makes its own noise */ 158 free(netdev_name); 159 netif_close(netdev_sock); 160 netdev_sock = -1; 161 return (error); 162 } 163 } 164 net_setparams(netdev_sock); 165 } 166 netdev_opens++; 167 dev->d_opendata = &netdev_sock; 168 return (error); 169 } 170 171 /* 172 * Configure a network interface without opening a file on it. This is used 173 * by consumers which need DHCP configuration before switching to a firmware 174 * network protocol. 175 */ 176 int 177 net_configure(struct devdesc *dev) 178 { 179 int error; 180 181 if (netdev_sock >= 0) 182 return (0); 183 184 netdev_sock = netif_open(dev); 185 if (netdev_sock < 0) 186 return (ENXIO); 187 188 error = 0; 189 if (rootip.s_addr == 0) 190 error = net_getparams(netdev_sock); 191 if (error == 0) 192 net_setparams(netdev_sock); 193 if (error != 0) { 194 netif_close(netdev_sock); 195 netdev_sock = -1; 196 return (error); 197 } 198 199 netdev_name = strdup(dev->d_dev->dv_name); 200 if (netdev_name == NULL) { 201 netif_close(netdev_sock); 202 netdev_sock = -1; 203 return (ENOMEM); 204 } 205 return (error); 206 } 207 208 void 209 net_deconfigure(void) 210 { 211 212 net_cleanup(); 213 } 214 215 static int 216 net_close(struct open_file *f) 217 { 218 struct devdesc *dev; 219 220 DEBUG_PRINTF(2,("%s: opens=%d\n", __func__, netdev_opens)); 221 222 dev = f->f_devdata; 223 dev->d_opendata = NULL; 224 225 return (0); 226 } 227 228 static void 229 net_cleanup(void) 230 { 231 232 if (netdev_sock >= 0) { 233 DEBUG_PRINTF(1,("%s: calling netif_close()\n", __func__)); 234 rootip.s_addr = 0; 235 free(netdev_name); 236 netif_close(netdev_sock); 237 netdev_sock = -1; 238 } 239 } 240 241 static int 242 net_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, 243 size_t *rsize) 244 { 245 246 return (EIO); 247 } 248 249 #define SUPPORT_BOOTP 250 251 /* 252 * Get info for NFS boot: our IP address, our hostname, 253 * server IP address, and our root path on the server. 254 * There are two ways to do this: The old, Sun way, 255 * and the more modern, BOOTP way. (RFC951, RFC1048) 256 * 257 * The default is to use the Sun bootparams RPC 258 * (because that is what the kernel will do). 259 * MD code can make try_bootp initialied data, 260 * which will override this common definition. 261 */ 262 #ifdef SUPPORT_BOOTP 263 int try_bootp = 1; 264 #endif 265 266 extern n_long ip_convertaddr(char *p); 267 268 static int 269 net_getparams(int sock) 270 { 271 char buf[MAXHOSTNAMELEN]; 272 const char *val; 273 n_long rootaddr, smask; 274 275 #ifdef SUPPORT_BOOTP 276 /* 277 * Try to get boot info using BOOTP. If we succeed, then 278 * the server IP address, gateway, and root path will all 279 * be initialized. If any remain uninitialized, we will 280 * use RARP and RPC/bootparam (the Sun way) to get them. 281 */ 282 if (try_bootp) 283 bootp(sock); 284 if (myip.s_addr != 0) 285 goto exit; 286 DEBUG_PRINTF(1,("%s: BOOTP failed, trying RARP/RPC...\n", __func__)); 287 #endif 288 289 /* 290 * Use RARP to get our IP address. This also sets our 291 * netmask to the "natural" default for our address. 292 */ 293 if (rarp_getipaddress(sock)) { 294 printf("%s: RARP failed\n", __func__); 295 return (EIO); 296 } 297 printf("%s: client addr: %s\n", __func__, inet_ntoa(myip)); 298 299 /* Get our hostname, server IP address, gateway. */ 300 if (bp_whoami(sock)) { 301 printf("%s: bootparam/whoami RPC failed\n", __func__); 302 return (EIO); 303 } 304 DEBUG_PRINTF(1,("%s: client name: %s\n", __func__, hostname)); 305 306 /* 307 * Ignore the gateway from whoami (unreliable). 308 * Use the "gateway" parameter instead. 309 */ 310 smask = 0; 311 gateip.s_addr = 0; 312 if (bp_getfile(sock, "gateway", &gateip, buf) == 0) { 313 /* Got it! Parse the netmask. */ 314 smask = ip_convertaddr(buf); 315 } 316 if (smask) { 317 netmask = smask; 318 DEBUG_PRINTF(1,("%s: subnet mask: %s\n", __func__, 319 intoa(netmask))); 320 } 321 if (gateip.s_addr) 322 DEBUG_PRINTF(1,("%s: net gateway: %s\n", __func__, 323 inet_ntoa(gateip))); 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 ((val = getenv("dhcp.root-path")) != NULL) 332 strlcpy(rootpath, val, sizeof(rootpath)); 333 if ((rootaddr = net_parse_rootpath()) != htonl(INADDR_NONE)) 334 rootip.s_addr = rootaddr; 335 336 DEBUG_PRINTF(1,("%s: proto: %d\n", __func__, netproto)); 337 DEBUG_PRINTF(1,("%s: server addr: %s\n", __func__, inet_ntoa(rootip))); 338 DEBUG_PRINTF(1,("%s: server port: %d\n", __func__, rootport)); 339 DEBUG_PRINTF(1,("%s: server path: %s\n", __func__, rootpath)); 340 341 return (0); 342 } 343 344 static void 345 net_setparams(int sock) 346 { 347 struct iodesc *d; 348 349 /* 350 * Set the variables required by the kernel's nfs_diskless mechanism. 351 */ 352 d = socktodesc(sock); 353 setenv("boot.netif.hwaddr", ether_sprintf(d->myea), 1); 354 setenv("boot.netif.ip", inet_ntoa(myip), 1); 355 setenv("boot.netif.netmask", intoa(netmask), 1); 356 setenv("boot.netif.gateway", inet_ntoa(gateip), 1); 357 setenv("boot.netif.server", inet_ntoa(rootip), 1); 358 if (netproto == NET_TFTP) { 359 setenv("boot.tftproot.server", inet_ntoa(rootip), 1); 360 setenv("boot.tftproot.path", rootpath, 1); 361 } else if (netproto == NET_NFS) { 362 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); 363 setenv("boot.nfsroot.path", rootpath, 1); 364 } 365 if (intf_mtu != 0) { 366 char mtu[16]; 367 368 snprintf(mtu, sizeof(mtu), "%u", intf_mtu); 369 setenv("boot.netif.mtu", mtu, 1); 370 } 371 372 DEBUG_PRINTF(1,("%s: netproto=%d\n", __func__, netproto)); 373 } 374 375 static int 376 net_print(int verbose) 377 { 378 struct netif_driver *drv; 379 int i, d, cnt; 380 int ret = 0; 381 382 if (netif_drivers[0] == NULL) 383 return (ret); 384 385 printf("%s devices:", netdev.dv_name); 386 if ((ret = pager_output("\n")) != 0) 387 return (ret); 388 389 cnt = 0; 390 for (d = 0; netif_drivers[d]; d++) { 391 drv = netif_drivers[d]; 392 for (i = 0; i < drv->netif_nifs; i++) { 393 printf("\t%s%d:", netdev.dv_name, cnt++); 394 if (verbose) { 395 printf(" (%s%d)", drv->netif_bname, 396 drv->netif_ifs[i].dif_unit); 397 } 398 if ((ret = pager_output("\n")) != 0) 399 return (ret); 400 } 401 } 402 return (ret); 403 } 404 405 bool 406 is_tftp(void) 407 { 408 return (netproto == NET_TFTP); 409 } 410 411 /* 412 * Parses the rootpath if present 413 * 414 * The rootpath format can be in the form 415 * <scheme>://ip[:port]/path 416 * <scheme>:/path 417 * 418 * For compatibility with previous behaviour it also accepts as an NFS scheme 419 * ip:/path 420 * /path 421 * 422 * If an ip is set it returns it in network byte order. 423 * The default scheme defined in the global netproto, if not set it defaults to 424 * NFS. 425 * It leaves just the pathname in the global rootpath. 426 */ 427 uint32_t 428 net_parse_rootpath(void) 429 { 430 n_long addr = 0; 431 size_t i; 432 char ip[FNAME_SIZE]; 433 char *ptr, *portp, *val; 434 435 netproto = NET_NONE; 436 437 for (i = 0; i < nitems(uri_schemes); i++) { 438 if (strncmp(rootpath, uri_schemes[i].scheme, 439 strlen(uri_schemes[i].scheme)) != 0) 440 continue; 441 442 netproto = uri_schemes[i].proto; 443 break; 444 } 445 ptr = rootpath; 446 /* Fallback for compatibility mode */ 447 if (netproto == NET_NONE) { 448 netproto = NETPROTO_DEFAULT; 449 (void)strsep(&ptr, ":"); 450 if (ptr != NULL) { 451 addr = inet_addr(rootpath); 452 DEBUG_PRINTF(1,("rootpath=%s addr=%#x\n", 453 rootpath, addr)); 454 bcopy(ptr, rootpath, strlen(ptr) + 1); 455 } 456 } else { 457 ptr += strlen(uri_schemes[i].scheme); 458 if (*ptr == '/') { 459 /* we are in the form <scheme>://, we do expect an ip */ 460 ptr++; 461 portp = val = strchr(ptr, ':'); 462 if (val != NULL) { 463 val++; 464 rootport = strtol(val, NULL, 10); 465 } 466 val = strchr(ptr, '/'); 467 if (val != NULL) { 468 if (portp == NULL) 469 portp = val; 470 snprintf(ip, sizeof(ip), "%.*s", 471 (int)(portp - ptr), 472 ptr); 473 addr = inet_addr(ip); 474 DEBUG_PRINTF(1,("ip=%s addr=%#x\n", 475 ip, addr)); 476 bcopy(val, rootpath, strlen(val) + 1); 477 } 478 } else { 479 ptr--; 480 bcopy(ptr, rootpath, strlen(ptr) + 1); 481 } 482 } 483 if (addr == 0) 484 addr = htonl(INADDR_NONE); 485 return (addr); 486 } 487