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