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