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