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