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