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