root/nfs/nfs_boot.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nfs_boot_init
  2. nfs_boot_getfh
  3. nfs_boot_init
  4. nfs_boot_getfh
  5. bp_whoami
  6. bp_getfile
  7. md_mount

    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 */

/* [<][>][^][v][top][bottom][index][help] */