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