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