1 /* $NetBSD: nfs_boot.c,v 1.33 1997/05/27 23:37:39 gwr Exp $ */ 2 3 /* 4 * Copyright (c) 1995 Adam Glass, Gordon Ross 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the authors may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/conf.h> 34 #include <sys/device.h> 35 #include <sys/ioctl.h> 36 #include <sys/proc.h> 37 #include <sys/mount.h> 38 #include <sys/mbuf.h> 39 #include <sys/reboot.h> 40 #include <sys/socket.h> 41 #include <sys/socketvar.h> 42 43 #include <net/if.h> 44 #include <net/route.h> 45 46 #include <net/if_ether.h> 47 48 #include <netinet/in.h> 49 #include <netinet/if_inarp.h> 50 51 #include <nfs/rpcv2.h> 52 #include <nfs/nfsproto.h> 53 #include <nfs/nfs.h> 54 #include <nfs/nfsdiskless.h> 55 #include <nfs/krpc.h> 56 #include <nfs/xdr_subs.h> 57 #include <nfs/nfs_var.h> 58 59 #include "ether.h" 60 #if NETHER == 0 61 62 int nfs_boot_init(nd, procp) 63 struct nfs_diskless *nd; 64 struct proc *procp; 65 { 66 printf("nfs_boot: NETHER == 0\n"); 67 return (ENXIO); 68 } 69 70 int 71 nfs_boot_getfh(ndm) 72 struct nfs_dlmount *ndm; 73 { 74 return (ENXIO); 75 } 76 77 #else /* NETHER */ 78 79 /* 80 * Support for NFS diskless booting, specifically getting information 81 * about where to boot from, what pathnames, etc. 82 * 83 * This implememtation uses RARP and the bootparam RPC. 84 * We are forced to implement RPC anyway (to get file handles) 85 * so we might as well take advantage of it for bootparam too. 86 * 87 * The diskless boot sequence goes as follows: 88 * (1) Use RARP to get our interface address 89 * (2) Use RPC/bootparam/whoami to get our hostname, 90 * our IP address, and the server's IP address. 91 * (3) Use RPC/bootparam/getfile to get the root path 92 * (4) Use RPC/mountd to get the root file handle 93 * (5) Use RPC/bootparam/getfile to get the swap path 94 * (6) Use RPC/mountd to get the swap file handle 95 * 96 * (This happens to be the way Sun does it too.) 97 */ 98 99 /* bootparam RPC */ 100 static int bp_whoami __P((struct sockaddr_in *bpsin, 101 struct in_addr *my_ip, struct in_addr *gw_ip)); 102 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key, 103 struct nfs_dlmount *ndm)); 104 105 /* mountd RPC */ 106 static int md_mount __P((struct sockaddr_in *mdsin, char *path, 107 struct nfs_args *argp)); 108 109 /* 110 * Called with an empty nfs_diskless struct to be filled in. 111 */ 112 int 113 nfs_boot_init(nd, procp) 114 struct nfs_diskless *nd; 115 struct proc *procp; 116 { 117 struct ifreq ireq; 118 struct in_addr my_ip, gw_ip; 119 struct sockaddr_in bp_sin; 120 struct sockaddr_in *sin; 121 struct ifnet *ifp; 122 struct socket *so; 123 int error; 124 125 /* 126 * Find an interface, rarp for its ip address, stuff it, the 127 * implied broadcast addr, and netmask into a nfs_diskless struct. 128 * 129 * This was moved here from nfs_vfsops.c because this procedure 130 * would be quite different if someone decides to write (i.e.) a 131 * BOOTP version of this file (might not use RARP, etc.) 132 */ 133 134 /* 135 * Find a network interface. 136 */ 137 ifp = ifunit(root_device->dv_xname); 138 if (ifp == NULL) { 139 printf("nfs_boot: no suitable interface\n"); 140 return (ENXIO); 141 } 142 bcopy(ifp->if_xname, ireq.ifr_name, IFNAMSIZ); 143 printf("nfs_boot: using network interface '%s'\n", 144 ireq.ifr_name); 145 146 /* 147 * Bring up the interface. 148 * 149 * Get the old interface flags and or IFF_UP into them; if 150 * IFF_UP set blindly, interface selection can be clobbered. 151 */ 152 so = 0; 153 error = socreate(AF_INET, &so, SOCK_DGRAM, 0); 154 if (error) { 155 printf("nfs_boot: socreate, error=%d\n", error); 156 so = 0; 157 goto out; 158 } 159 error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ireq, procp); 160 if (error) { 161 printf("nfs_boot: GIFFLAGS, error=%d\n", error); 162 goto out; 163 } 164 ireq.ifr_flags |= IFF_UP; 165 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp); 166 if (error) { 167 printf("nfs_boot: SIFFLAGS, error=%d\n", error); 168 goto out; 169 } 170 171 /* 172 * Do RARP for the interface address. 173 */ 174 if ((error = revarpwhoami(&my_ip, ifp)) != 0) { 175 printf("revarp failed, error=%d\n", error); 176 error = EIO; /* XXX */ 177 goto out; 178 } 179 printf("nfs_boot: client_addr=0x%x\n", 180 (u_int32_t)ntohl(my_ip.s_addr)); 181 182 /* 183 * Do enough of ifconfig(8) so that the chosen interface 184 * can talk to the servers. (just set the address) 185 */ 186 sin = (struct sockaddr_in *)&ireq.ifr_addr; 187 bzero((caddr_t)sin, sizeof(*sin)); 188 sin->sin_len = sizeof(*sin); 189 sin->sin_family = AF_INET; 190 sin->sin_addr.s_addr = my_ip.s_addr; 191 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp); 192 if (error) { 193 printf("nfs_boot: set if addr, error=%d\n", error); 194 goto out; 195 } 196 197 /* 198 * Get client name and gateway address. 199 * RPC: bootparam/whoami 200 * Use the old broadcast address for the WHOAMI 201 * call because we do not yet know our netmask. 202 * The server address returned by the WHOAMI call 203 * is used for all subsequent booptaram RPCs. 204 */ 205 bzero((caddr_t)&bp_sin, sizeof(bp_sin)); 206 bp_sin.sin_len = sizeof(bp_sin); 207 bp_sin.sin_family = AF_INET; 208 bp_sin.sin_addr.s_addr = INADDR_BROADCAST; 209 hostnamelen = MAXHOSTNAMELEN; 210 211 /* Do the RPC/bootparam/whoami. */ 212 error = bp_whoami(&bp_sin, &my_ip, &gw_ip); 213 if (error) { 214 printf("nfs_boot: bootparam whoami, error=%d\n", error); 215 goto out; 216 } 217 printf("nfs_boot: server_addr=0x%x\n", 218 (u_int32_t)ntohl(bp_sin.sin_addr.s_addr)); 219 printf("nfs_boot: hostname=%s\n", hostname); 220 221 #ifdef NFS_BOOT_GATEWAY 222 /* 223 * XXX - This code is conditionally compiled only because 224 * many bootparam servers (in particular, SunOS 4.1.3) 225 * always set the gateway address to their own address. 226 * The bootparam server is not necessarily the gateway. 227 * We could just believe the server, and at worst you would 228 * need to delete the incorrect default route before adding 229 * the correct one, but for simplicity, ignore the gateway. 230 * If your server is OK, you can turn on this option. 231 * 232 * If the gateway address is set, add a default route. 233 * (The mountd RPCs may go across a gateway.) 234 */ 235 if (gw_ip.s_addr) { 236 struct sockaddr dst, gw, mask; 237 /* Destination: (default) */ 238 bzero((caddr_t)&dst, sizeof(dst)); 239 dst.sa_len = sizeof(dst); 240 dst.sa_family = AF_INET; 241 /* Gateway: */ 242 bzero((caddr_t)&gw, sizeof(gw)); 243 sin = (struct sockaddr_in *)&gw; 244 sin->sin_len = sizeof(gw); 245 sin->sin_family = AF_INET; 246 sin->sin_addr.s_addr = gw_ip.s_addr; 247 /* Mask: (zero length) */ 248 bzero(&mask, sizeof(mask)); 249 250 printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr)); 251 /* add, dest, gw, mask, flags, 0 */ 252 error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw, 253 &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); 254 if (error) 255 printf("nfs_boot: add route, error=%d\n", error); 256 } 257 #endif 258 259 bcopy(&bp_sin, &nd->nd_boot, sizeof(bp_sin)); 260 261 /* 262 * Now fetch the server:pathname strings and server IP 263 * for root and swap. Missing swap is not fatal. 264 */ 265 error = bp_getfile(&nd->nd_boot, "root", &nd->nd_root); 266 if (error) { 267 printf("nfs_boot: bootparam get root: %d\n", error); 268 goto out; 269 } 270 271 error = bp_getfile(&nd->nd_boot, "swap", &nd->nd_swap); 272 if (error) { 273 printf("nfs_boot: bootparam get swap: %d", error); 274 error = 0; 275 } 276 277 out: 278 if (so) soclose(so); 279 280 return (error); 281 } 282 283 int 284 nfs_boot_getfh(ndm) 285 struct nfs_dlmount *ndm; /* output */ 286 { 287 struct nfs_args *args; 288 struct sockaddr_in *sin; 289 char *pathname; 290 int error; 291 292 args = &ndm->ndm_args; 293 294 /* Initialize mount args. */ 295 bzero((caddr_t) args, sizeof(*args)); 296 args->addr = &ndm->ndm_saddr; 297 args->addrlen = args->addr->sa_len; 298 #ifdef NFS_BOOT_TCP 299 args->sotype = SOCK_STREAM; 300 #else 301 args->sotype = SOCK_DGRAM; 302 #endif 303 args->fh = ndm->ndm_fh; 304 args->hostname = ndm->ndm_host; 305 args->flags = NFSMNT_RESVPORT | NFSMNT_NFSV3; 306 307 #ifdef NFS_BOOT_OPTIONS 308 args->flags |= NFS_BOOT_OPTIONS; 309 #endif 310 #ifdef NFS_BOOT_RWSIZE 311 /* 312 * Reduce rsize,wsize for interfaces that consistently 313 * drop fragments of long UDP messages. (i.e. wd8003). 314 * You can always change these later via remount. 315 */ 316 args->flags |= NFSMNT_WSIZE | NFSMNT_RSIZE; 317 args->wsize = NFS_BOOT_RWSIZE; 318 args->rsize = NFS_BOOT_RWSIZE; 319 #endif 320 321 /* 322 * Find the pathname part of the "server:pathname" 323 * string left in ndm->ndm_host by nfs_boot_init. 324 */ 325 pathname = strchr(ndm->ndm_host, ':'); 326 if (pathname == 0) { 327 printf("nfs_boot: getfh - no pathname\n"); 328 return (EIO); 329 } 330 pathname++; 331 332 /* 333 * Get file handle using RPC to mountd/mount 334 */ 335 sin = (struct sockaddr_in *)&ndm->ndm_saddr; 336 error = md_mount(sin, pathname, args); 337 if (error) { 338 printf("nfs_boot: mountd `%s', error=%d\n", 339 ndm->ndm_host, error); 340 return (error); 341 } 342 343 /* Set port number for NFS use. */ 344 /* XXX: NFS port is always 2049, right? */ 345 #ifdef NFS_BOOT_TCP 346 retry: 347 #endif 348 error = krpc_portmap(sin, NFS_PROG, 349 (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2, 350 (args->sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP, 351 &sin->sin_port); 352 if (sin->sin_port == htons(0)) 353 error = EIO; 354 if (error) { 355 #ifdef NFS_BOOT_TCP 356 if (args->sotype == SOCK_STREAM) { 357 args->sotype = SOCK_DGRAM; 358 goto retry; 359 } 360 #endif 361 printf("nfs_boot: portmap NFS, error=%d\n", error); 362 } 363 364 return (error); 365 } 366 367 368 /* 369 * RPC: bootparam/whoami 370 * Given client IP address, get: 371 * client name (hostname) 372 * domain name (domainname) 373 * gateway address 374 * 375 * The hostname and domainname are set here for convenience. 376 * 377 * Note - bpsin is initialized to the broadcast address, 378 * and will be replaced with the bootparam server address 379 * after this call is complete. Have to use PMAP_PROC_CALL 380 * to make sure we get responses only from a servers that 381 * know about us (don't want to broadcast a getport call). 382 */ 383 static int 384 bp_whoami(bpsin, my_ip, gw_ip) 385 struct sockaddr_in *bpsin; 386 struct in_addr *my_ip; 387 struct in_addr *gw_ip; 388 { 389 /* RPC structures for PMAPPROC_CALLIT */ 390 struct whoami_call { 391 u_int32_t call_prog; 392 u_int32_t call_vers; 393 u_int32_t call_proc; 394 u_int32_t call_arglen; 395 } *call; 396 struct callit_reply { 397 u_int32_t port; 398 u_int32_t encap_len; 399 /* encapsulated data here */ 400 } *reply; 401 402 struct mbuf *m, *from; 403 struct sockaddr_in *sin; 404 int error, msg_len; 405 int16_t port; 406 407 /* 408 * Build request message for PMAPPROC_CALLIT. 409 */ 410 m = m_get(M_WAIT, MT_DATA); 411 call = mtod(m, struct whoami_call *); 412 m->m_len = sizeof(*call); 413 call->call_prog = txdr_unsigned(BOOTPARAM_PROG); 414 call->call_vers = txdr_unsigned(BOOTPARAM_VERS); 415 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI); 416 417 /* 418 * append encapsulated data (client IP address) 419 */ 420 m->m_next = xdr_inaddr_encode(my_ip); 421 call->call_arglen = txdr_unsigned(m->m_next->m_len); 422 423 /* RPC: portmap/callit */ 424 bpsin->sin_port = htons(PMAPPORT); 425 from = NULL; 426 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 427 PMAPPROC_CALLIT, &m, &from); 428 if (error) 429 return error; 430 431 /* 432 * Parse result message. 433 */ 434 if (m->m_len < sizeof(*reply)) { 435 m = m_pullup(m, sizeof(*reply)); 436 if (m == NULL) 437 goto bad; 438 } 439 reply = mtod(m, struct callit_reply *); 440 port = fxdr_unsigned(u_int32_t, reply->port); 441 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len); 442 m_adj(m, sizeof(*reply)); 443 444 /* 445 * Save bootparam server address 446 */ 447 sin = mtod(from, struct sockaddr_in *); 448 bpsin->sin_port = htons(port); 449 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 450 451 /* client name */ 452 hostnamelen = MAXHOSTNAMELEN-1; 453 m = xdr_string_decode(m, hostname, &hostnamelen); 454 if (m == NULL) 455 goto bad; 456 457 /* domain name */ 458 domainnamelen = MAXHOSTNAMELEN-1; 459 m = xdr_string_decode(m, domainname, &domainnamelen); 460 if (m == NULL) 461 goto bad; 462 463 /* gateway address */ 464 m = xdr_inaddr_decode(m, gw_ip); 465 if (m == NULL) 466 goto bad; 467 468 /* success */ 469 goto out; 470 471 bad: 472 printf("nfs_boot: bootparam_whoami: bad reply\n"); 473 error = EBADRPC; 474 475 out: 476 if (from) 477 m_freem(from); 478 if (m) 479 m_freem(m); 480 return(error); 481 } 482 483 484 /* 485 * RPC: bootparam/getfile 486 * Given client name and file "key", get: 487 * server name 488 * server IP address 489 * server pathname 490 */ 491 static int 492 bp_getfile(bpsin, key, ndm) 493 struct sockaddr_in *bpsin; 494 char *key; 495 struct nfs_dlmount *ndm; 496 { 497 char pathname[MNAMELEN]; 498 struct in_addr inaddr; 499 struct sockaddr_in *sin; 500 struct mbuf *m; 501 char *serv_name; 502 int error, sn_len, path_len; 503 504 /* 505 * Build request message. 506 */ 507 508 /* client name (hostname) */ 509 m = xdr_string_encode(hostname, hostnamelen); 510 if (m == NULL) 511 return (ENOMEM); 512 513 /* key name (root or swap) */ 514 m->m_next = xdr_string_encode(key, strlen(key)); 515 if (m->m_next == NULL) 516 return (ENOMEM); 517 518 /* RPC: bootparam/getfile */ 519 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 520 BOOTPARAM_GETFILE, &m, NULL); 521 if (error) 522 return error; 523 524 /* 525 * Parse result message. 526 */ 527 528 /* server name */ 529 serv_name = &ndm->ndm_host[0]; 530 sn_len = sizeof(ndm->ndm_host) - 1; 531 m = xdr_string_decode(m, serv_name, &sn_len); 532 if (m == NULL) 533 goto bad; 534 535 /* server IP address (mountd/NFS) */ 536 m = xdr_inaddr_decode(m, &inaddr); 537 if (m == NULL) 538 goto bad; 539 540 /* server pathname */ 541 path_len = sizeof(pathname) - 1; 542 m = xdr_string_decode(m, pathname, &path_len); 543 if (m == NULL) 544 goto bad; 545 546 /* 547 * Store the results in the nfs_dlmount. 548 * The strings become "server:pathname" 549 */ 550 sin = (struct sockaddr_in *) &ndm->ndm_saddr; 551 bzero((caddr_t)sin, sizeof(*sin)); 552 sin->sin_len = sizeof(*sin); 553 sin->sin_family = AF_INET; 554 sin->sin_addr = inaddr; 555 if ((sn_len + 1 + path_len + 1) > sizeof(ndm->ndm_host)) { 556 printf("nfs_boot: getfile name too long\n"); 557 error = EIO; 558 goto out; 559 } 560 ndm->ndm_host[sn_len] = ':'; 561 bcopy(pathname, ndm->ndm_host + sn_len + 1, path_len + 1); 562 563 /* success */ 564 goto out; 565 566 bad: 567 printf("nfs_boot: bootparam_getfile: bad reply\n"); 568 error = EBADRPC; 569 570 out: 571 m_freem(m); 572 return(0); 573 } 574 575 576 /* 577 * RPC: mountd/mount 578 * Given a server pathname, get an NFS file handle. 579 * Also, sets sin->sin_port to the NFS service port. 580 */ 581 static int 582 md_mount(mdsin, path, argp) 583 struct sockaddr_in *mdsin; /* mountd server address */ 584 char *path; 585 struct nfs_args *argp; 586 { 587 /* The RPC structures */ 588 struct rdata { 589 u_int32_t errno; 590 union { 591 u_int8_t v2fh[NFSX_V2FH]; 592 struct { 593 u_int32_t fhlen; 594 u_int8_t fh[1]; 595 } v3fh; 596 } fh; 597 } *rdata; 598 struct mbuf *m; 599 u_int8_t *fh; 600 int minlen, error; 601 602 retry: 603 /* Get port number for MOUNTD. */ 604 error = krpc_portmap(mdsin, RPCPROG_MNT, 605 (argp->flags & NFSMNT_NFSV3) ? RPCMNT_VER3 : RPCMNT_VER1, 606 IPPROTO_UDP, &mdsin->sin_port); 607 if (error) 608 return error; 609 610 m = xdr_string_encode(path, strlen(path)); 611 if (m == NULL) 612 return ENOMEM; 613 614 /* Do RPC to mountd. */ 615 error = krpc_call(mdsin, RPCPROG_MNT, (argp->flags & NFSMNT_NFSV3) ? 616 RPCMNT_VER3 : RPCMNT_VER1, RPCMNT_MOUNT, &m, NULL); 617 if (error) { 618 if ((error==RPC_PROGMISMATCH) && (argp->flags & NFSMNT_NFSV3)) { 619 argp->flags &= ~NFSMNT_NFSV3; 620 goto retry; 621 } 622 return error; /* message already freed */ 623 } 624 625 /* The reply might have only the errno. */ 626 if (m->m_len < 4) 627 goto bad; 628 /* Have at least errno, so check that. */ 629 rdata = mtod(m, struct rdata *); 630 error = fxdr_unsigned(u_int32_t, rdata->errno); 631 if (error) 632 goto out; 633 634 /* Have errno==0, so the fh must be there. */ 635 if (argp->flags & NFSMNT_NFSV3){ 636 argp->fhsize = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen); 637 if (argp->fhsize > NFSX_V3FHMAX) 638 goto bad; 639 minlen = 2 * sizeof(u_int32_t) + argp->fhsize; 640 } else { 641 argp->fhsize = NFSX_V2FH; 642 minlen = sizeof(u_int32_t) + argp->fhsize; 643 } 644 645 if (m->m_len < minlen) { 646 m = m_pullup(m, minlen); 647 if (m == NULL) 648 return(EBADRPC); 649 rdata = mtod(m, struct rdata *); 650 } 651 652 fh = (argp->flags & NFSMNT_NFSV3) ? rdata->fh.v3fh.fh : rdata->fh.v2fh; 653 bcopy(fh, argp->fh, argp->fhsize); 654 655 goto out; 656 657 bad: 658 error = EBADRPC; 659 660 out: 661 m_freem(m); 662 return error; 663 } 664 665 #endif /* NETHER */ 666