1 /* $NetBSD: nfs_boot.c,v 1.32 1997/03/17 17:41:45 thorpej 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 panic("nfs_boot_init: no ether"); 67 } 68 69 void 70 nfs_boot_getfh(bpsin, key, ndmntp) 71 struct sockaddr_in *bpsin; 72 char *key; 73 struct nfs_dlmount *ndmntp; 74 { 75 /* can not get here */ 76 } 77 78 #else /* NETHER */ 79 80 /* 81 * Support for NFS diskless booting, specifically getting information 82 * about where to boot from, what pathnames, etc. 83 * 84 * This implememtation uses RARP and the bootparam RPC. 85 * We are forced to implement RPC anyway (to get file handles) 86 * so we might as well take advantage of it for bootparam too. 87 * 88 * The diskless boot sequence goes as follows: 89 * (1) Use RARP to get our interface address 90 * (2) Use RPC/bootparam/whoami to get our hostname, 91 * our IP address, and the server's IP address. 92 * (3) Use RPC/bootparam/getfile to get the root path 93 * (4) Use RPC/mountd to get the root file handle 94 * (5) Use RPC/bootparam/getfile to get the swap path 95 * (6) Use RPC/mountd to get the swap file handle 96 * 97 * (This happens to be the way Sun does it too.) 98 */ 99 100 /* bootparam RPC */ 101 static int bp_whoami __P((struct sockaddr_in *bpsin, 102 struct in_addr *my_ip, struct in_addr *gw_ip)); 103 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key, 104 struct sockaddr_in *mdsin, char *servname, char *path)); 105 106 /* mountd RPC */ 107 static int md_mount __P((struct sockaddr_in *mdsin, char *path, 108 struct nfs_args *argp)); 109 110 /* 111 * Called with an empty nfs_diskless struct to be filled in. 112 */ 113 int 114 nfs_boot_init(nd, procp) 115 struct nfs_diskless *nd; 116 struct proc *procp; 117 { 118 struct ifreq ireq; 119 struct in_addr my_ip, gw_ip; 120 struct sockaddr_in bp_sin; 121 struct sockaddr_in *sin; 122 struct ifnet *ifp; 123 struct socket *so; 124 int error; 125 126 /* 127 * Find an interface, rarp for its ip address, stuff it, the 128 * implied broadcast addr, and netmask into a nfs_diskless struct. 129 * 130 * This was moved here from nfs_vfsops.c because this procedure 131 * would be quite different if someone decides to write (i.e.) a 132 * BOOTP version of this file (might not use RARP, etc.) 133 */ 134 135 /* 136 * Find a network interface. 137 */ 138 ifp = ifunit(root_device->dv_xname); 139 if (ifp == NULL) { 140 printf("nfs_boot: no suitable interface\n"); 141 return (ENXIO); 142 } 143 bcopy(ifp->if_xname, ireq.ifr_name, IFNAMSIZ); 144 printf("nfs_boot: using network interface '%s'\n", 145 ireq.ifr_name); 146 147 /* 148 * Bring up the interface. 149 * 150 * Get the old interface flags and or IFF_UP into them; if 151 * IFF_UP set blindly, interface selection can be clobbered. 152 */ 153 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) 154 panic("nfs_boot: socreate, error=%d", error); 155 error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ireq, procp); 156 if (error) 157 panic("nfs_boot: GIFFLAGS, error=%d", error); 158 ireq.ifr_flags |= IFF_UP; 159 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp); 160 if (error) 161 panic("nfs_boot: SIFFLAGS, error=%d", error); 162 163 /* 164 * Do RARP for the interface address. 165 */ 166 if ((error = revarpwhoami(&my_ip, ifp)) != 0) { 167 printf("revarp failed, error=%d\n", error); 168 return (EIO); 169 } 170 printf("nfs_boot: client_addr=0x%x\n", (u_int32_t)ntohl(my_ip.s_addr)); 171 172 /* 173 * Do enough of ifconfig(8) so that the chosen interface 174 * can talk to the servers. (just set the address) 175 */ 176 sin = (struct sockaddr_in *)&ireq.ifr_addr; 177 bzero((caddr_t)sin, sizeof(*sin)); 178 sin->sin_len = sizeof(*sin); 179 sin->sin_family = AF_INET; 180 sin->sin_addr.s_addr = my_ip.s_addr; 181 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp); 182 if (error) 183 panic("nfs_boot: set if addr, error=%d", error); 184 185 soclose(so); 186 187 /* 188 * Get client name and gateway address. 189 * RPC: bootparam/whoami 190 * Use the old broadcast address for the WHOAMI 191 * call because we do not yet know our netmask. 192 * The server address returned by the WHOAMI call 193 * is used for all subsequent booptaram RPCs. 194 */ 195 bzero((caddr_t)&bp_sin, sizeof(bp_sin)); 196 bp_sin.sin_len = sizeof(bp_sin); 197 bp_sin.sin_family = AF_INET; 198 bp_sin.sin_addr.s_addr = INADDR_BROADCAST; 199 hostnamelen = MAXHOSTNAMELEN; 200 201 /* this returns gateway IP address */ 202 error = bp_whoami(&bp_sin, &my_ip, &gw_ip); 203 if (error) { 204 printf("nfs_boot: bootparam whoami, error=%d\n", error); 205 return (error); 206 } 207 printf("nfs_boot: server_addr=0x%x\n", 208 (u_int32_t)ntohl(bp_sin.sin_addr.s_addr)); 209 printf("nfs_boot: hostname=%s\n", hostname); 210 211 #ifdef NFS_BOOT_GATEWAY 212 /* 213 * XXX - This code is conditionally compiled only because 214 * many bootparam servers (in particular, SunOS 4.1.3) 215 * always set the gateway address to their own address. 216 * The bootparam server is not necessarily the gateway. 217 * We could just believe the server, and at worst you would 218 * need to delete the incorrect default route before adding 219 * the correct one, but for simplicity, ignore the gateway. 220 * If your server is OK, you can turn on this option. 221 * 222 * If the gateway address is set, add a default route. 223 * (The mountd RPCs may go across a gateway.) 224 */ 225 if (gw_ip.s_addr) { 226 struct sockaddr dst, gw, mask; 227 /* Destination: (default) */ 228 bzero((caddr_t)&dst, sizeof(dst)); 229 dst.sa_len = sizeof(dst); 230 dst.sa_family = AF_INET; 231 /* Gateway: */ 232 bzero((caddr_t)&gw, sizeof(gw)); 233 sin = (struct sockaddr_in *)&gw; 234 sin->sin_len = sizeof(gw); 235 sin->sin_family = AF_INET; 236 sin->sin_addr.s_addr = gw_ip.s_addr; 237 /* Mask: (zero length) */ 238 bzero(&mask, sizeof(mask)); 239 240 printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr)); 241 /* add, dest, gw, mask, flags, 0 */ 242 error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw, 243 &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); 244 if (error) 245 printf("nfs_boot: add route, error=%d\n", error); 246 } 247 #endif 248 249 bcopy(&bp_sin, &nd->nd_boot, sizeof(bp_sin)); 250 251 return (0); 252 } 253 254 void 255 nfs_boot_getfh(bpsin, key, ndmntp) 256 struct sockaddr_in *bpsin; /* bootparam server */ 257 char *key; /* root or swap */ 258 struct nfs_dlmount *ndmntp; /* output */ 259 { 260 struct nfs_args *args; 261 char pathname[MAXPATHLEN]; 262 char *sp, *dp, *endp; 263 struct sockaddr_in *sin; 264 int error; 265 266 args = &ndmntp->ndm_args; 267 268 /* Initialize mount args. */ 269 bzero((caddr_t) args, sizeof(*args)); 270 args->addr = &ndmntp->ndm_saddr; 271 args->addrlen = args->addr->sa_len; 272 #ifdef NFS_BOOT_TCP 273 args->sotype = SOCK_STREAM; 274 #else 275 args->sotype = SOCK_DGRAM; 276 #endif 277 args->fh = ndmntp->ndm_fh; 278 args->hostname = ndmntp->ndm_host; 279 args->flags = NFSMNT_RESVPORT | NFSMNT_NFSV3; 280 281 #ifdef NFS_BOOT_OPTIONS 282 args->flags |= NFS_BOOT_OPTIONS; 283 #endif 284 #ifdef NFS_BOOT_RWSIZE 285 /* 286 * Reduce rsize,wsize for interfaces that consistently 287 * drop fragments of long UDP messages. (i.e. wd8003). 288 * You can always change these later via remount. 289 */ 290 args->flags |= NFSMNT_WSIZE | NFSMNT_RSIZE; 291 args->wsize = NFS_BOOT_RWSIZE; 292 args->rsize = NFS_BOOT_RWSIZE; 293 #endif 294 295 sin = (void*)&ndmntp->ndm_saddr; 296 297 /* 298 * Get server:pathname for "key" (root or swap) 299 * using RPC to bootparam/getfile 300 */ 301 error = bp_getfile(bpsin, key, sin, ndmntp->ndm_host, pathname); 302 if (error) 303 panic("nfs_boot: bootparam get %s: %d", key, error); 304 305 /* 306 * Get file handle for "key" (root or swap) 307 * using RPC to mountd/mount 308 */ 309 error = md_mount(sin, pathname, args); 310 if (error) 311 panic("nfs_boot: mountd %s, error=%d", key, error); 312 313 /* Set port number for NFS use. */ 314 /* XXX: NFS port is always 2049, right? */ 315 #ifdef NFS_BOOT_TCP 316 retry: 317 #endif 318 error = krpc_portmap(sin, NFS_PROG, 319 (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2, 320 (args->sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP, 321 &sin->sin_port); 322 323 if (error || (sin->sin_port == htons(0))) { 324 #ifdef NFS_BOOT_TCP 325 if (args->sotype == SOCK_STREAM) { 326 args->sotype = SOCK_DGRAM; 327 goto retry; 328 } 329 #endif 330 panic("nfs_boot: portmap NFS, error=%d", error); 331 } 332 333 /* Construct remote path (for getmntinfo(3)) */ 334 dp = ndmntp->ndm_host; 335 endp = dp + MNAMELEN - 1; 336 dp += strlen(dp); 337 *dp++ = ':'; 338 for (sp = pathname; *sp && dp < endp;) 339 *dp++ = *sp++; 340 *dp = '\0'; 341 342 } 343 344 345 /* 346 * RPC: bootparam/whoami 347 * Given client IP address, get: 348 * client name (hostname) 349 * domain name (domainname) 350 * gateway address 351 * 352 * The hostname and domainname are set here for convenience. 353 * 354 * Note - bpsin is initialized to the broadcast address, 355 * and will be replaced with the bootparam server address 356 * after this call is complete. Have to use PMAP_PROC_CALL 357 * to make sure we get responses only from a servers that 358 * know about us (don't want to broadcast a getport call). 359 */ 360 static int 361 bp_whoami(bpsin, my_ip, gw_ip) 362 struct sockaddr_in *bpsin; 363 struct in_addr *my_ip; 364 struct in_addr *gw_ip; 365 { 366 /* RPC structures for PMAPPROC_CALLIT */ 367 struct whoami_call { 368 u_int32_t call_prog; 369 u_int32_t call_vers; 370 u_int32_t call_proc; 371 u_int32_t call_arglen; 372 } *call; 373 struct callit_reply { 374 u_int32_t port; 375 u_int32_t encap_len; 376 /* encapsulated data here */ 377 } *reply; 378 379 struct mbuf *m, *from; 380 struct sockaddr_in *sin; 381 int error, msg_len; 382 int16_t port; 383 384 /* 385 * Build request message for PMAPPROC_CALLIT. 386 */ 387 m = m_get(M_WAIT, MT_DATA); 388 call = mtod(m, struct whoami_call *); 389 m->m_len = sizeof(*call); 390 call->call_prog = txdr_unsigned(BOOTPARAM_PROG); 391 call->call_vers = txdr_unsigned(BOOTPARAM_VERS); 392 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI); 393 394 /* 395 * append encapsulated data (client IP address) 396 */ 397 m->m_next = xdr_inaddr_encode(my_ip); 398 call->call_arglen = txdr_unsigned(m->m_next->m_len); 399 400 /* RPC: portmap/callit */ 401 bpsin->sin_port = htons(PMAPPORT); 402 from = NULL; 403 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 404 PMAPPROC_CALLIT, &m, &from); 405 if (error) 406 return error; 407 408 /* 409 * Parse result message. 410 */ 411 if (m->m_len < sizeof(*reply)) { 412 m = m_pullup(m, sizeof(*reply)); 413 if (m == NULL) 414 goto bad; 415 } 416 reply = mtod(m, struct callit_reply *); 417 port = fxdr_unsigned(u_int32_t, reply->port); 418 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len); 419 m_adj(m, sizeof(*reply)); 420 421 /* 422 * Save bootparam server address 423 */ 424 sin = mtod(from, struct sockaddr_in *); 425 bpsin->sin_port = htons(port); 426 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 427 428 /* client name */ 429 hostnamelen = MAXHOSTNAMELEN-1; 430 m = xdr_string_decode(m, hostname, &hostnamelen); 431 if (m == NULL) 432 goto bad; 433 434 /* domain name */ 435 domainnamelen = MAXHOSTNAMELEN-1; 436 m = xdr_string_decode(m, domainname, &domainnamelen); 437 if (m == NULL) 438 goto bad; 439 440 /* gateway address */ 441 m = xdr_inaddr_decode(m, gw_ip); 442 if (m == NULL) 443 goto bad; 444 445 /* success */ 446 goto out; 447 448 bad: 449 printf("nfs_boot: bootparam_whoami: bad reply\n"); 450 error = EBADRPC; 451 452 out: 453 if (from) 454 m_freem(from); 455 if (m) 456 m_freem(m); 457 return(error); 458 } 459 460 461 /* 462 * RPC: bootparam/getfile 463 * Given client name and file "key", get: 464 * server name 465 * server IP address 466 * server pathname 467 */ 468 static int 469 bp_getfile(bpsin, key, md_sin, serv_name, pathname) 470 struct sockaddr_in *bpsin; 471 char *key; 472 struct sockaddr_in *md_sin; 473 char *serv_name; 474 char *pathname; 475 { 476 struct mbuf *m; 477 struct sockaddr_in *sin; 478 struct in_addr inaddr; 479 int error, sn_len, path_len; 480 481 /* 482 * Build request message. 483 */ 484 485 /* client name (hostname) */ 486 m = xdr_string_encode(hostname, hostnamelen); 487 if (m == NULL) 488 return (ENOMEM); 489 490 /* key name (root or swap) */ 491 m->m_next = xdr_string_encode(key, strlen(key)); 492 if (m->m_next == NULL) 493 return (ENOMEM); 494 495 /* RPC: bootparam/getfile */ 496 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 497 BOOTPARAM_GETFILE, &m, NULL); 498 if (error) 499 return error; 500 501 /* 502 * Parse result message. 503 */ 504 505 /* server name */ 506 sn_len = MNAMELEN-1; 507 m = xdr_string_decode(m, serv_name, &sn_len); 508 if (m == NULL) 509 goto bad; 510 511 /* server IP address (mountd/NFS) */ 512 m = xdr_inaddr_decode(m, &inaddr); 513 if (m == NULL) 514 goto bad; 515 516 /* server pathname */ 517 path_len = MAXPATHLEN-1; 518 m = xdr_string_decode(m, pathname, &path_len); 519 if (m == NULL) 520 goto bad; 521 522 /* setup server socket address */ 523 sin = md_sin; 524 bzero((caddr_t)sin, sizeof(*sin)); 525 sin->sin_len = sizeof(*sin); 526 sin->sin_family = AF_INET; 527 sin->sin_addr = inaddr; 528 529 /* success */ 530 goto out; 531 532 bad: 533 printf("nfs_boot: bootparam_getfile: bad reply\n"); 534 error = EBADRPC; 535 536 out: 537 m_freem(m); 538 return(0); 539 } 540 541 542 /* 543 * RPC: mountd/mount 544 * Given a server pathname, get an NFS file handle. 545 * Also, sets sin->sin_port to the NFS service port. 546 */ 547 static int 548 md_mount(mdsin, path, argp) 549 struct sockaddr_in *mdsin; /* mountd server address */ 550 char *path; 551 struct nfs_args *argp; 552 { 553 /* The RPC structures */ 554 struct rdata { 555 u_int32_t errno; 556 union { 557 u_int8_t v2fh[NFSX_V2FH]; 558 struct { 559 u_int32_t fhlen; 560 u_int8_t fh[1]; 561 } v3fh; 562 } fh; 563 } *rdata; 564 struct mbuf *m; 565 u_int8_t *fh; 566 int minlen, error; 567 568 retry: 569 /* Get port number for MOUNTD. */ 570 error = krpc_portmap(mdsin, RPCPROG_MNT, 571 (argp->flags & NFSMNT_NFSV3) ? RPCMNT_VER3 : RPCMNT_VER1, 572 IPPROTO_UDP, &mdsin->sin_port); 573 if (error) 574 return error; 575 576 m = xdr_string_encode(path, strlen(path)); 577 if (m == NULL) 578 return ENOMEM; 579 580 /* Do RPC to mountd. */ 581 error = krpc_call(mdsin, RPCPROG_MNT, (argp->flags & NFSMNT_NFSV3) ? 582 RPCMNT_VER3 : RPCMNT_VER1, RPCMNT_MOUNT, &m, NULL); 583 if (error) { 584 if ((error==RPC_PROGMISMATCH) && (argp->flags & NFSMNT_NFSV3)) { 585 argp->flags &= ~NFSMNT_NFSV3; 586 goto retry; 587 } 588 return error; /* message already freed */ 589 } 590 591 /* The reply might have only the errno. */ 592 if (m->m_len < 4) 593 goto bad; 594 /* Have at least errno, so check that. */ 595 rdata = mtod(m, struct rdata *); 596 error = fxdr_unsigned(u_int32_t, rdata->errno); 597 if (error) 598 goto out; 599 600 /* Have errno==0, so the fh must be there. */ 601 if (argp->flags & NFSMNT_NFSV3){ 602 argp->fhsize = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen); 603 if (argp->fhsize > NFSX_V3FHMAX) 604 goto bad; 605 minlen = 2 * sizeof(u_int32_t) + argp->fhsize; 606 } else { 607 argp->fhsize = NFSX_V2FH; 608 minlen = sizeof(u_int32_t) + argp->fhsize; 609 } 610 611 if (m->m_len < minlen) { 612 m = m_pullup(m, minlen); 613 if (m == NULL) 614 return(EBADRPC); 615 rdata = mtod(m, struct rdata *); 616 } 617 618 fh = (argp->flags & NFSMNT_NFSV3) ? rdata->fh.v3fh.fh : rdata->fh.v2fh; 619 bcopy(fh, argp->fh, argp->fhsize); 620 621 goto out; 622 623 bad: 624 error = EBADRPC; 625 626 out: 627 m_freem(m); 628 return error; 629 } 630 631 #endif /* NETHER */ 632