The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/nfs/nfs_boot.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 0cdc2653397cf0715e5321831db1aac7


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.