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