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/kern/subr_tftproot.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 /*      $NetBSD: subr_tftproot.c,v 1.25 2020/12/05 17:42:29 thorpej Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 2007 Emmanuel Dreyfus, all rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. All advertising materials mentioning features or use of this software
   15  *    must display the following acknowledgement:
   16  *      This product includes software developed by Emmanuel Dreyfus
   17  * 4. The name of the author may not be used to endorse or promote
   18  *    products derived from this software without specific prior written
   19  *    permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS''
   22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
   23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
   25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   31  * POSSIBILITY OF SUCH DAMAGE.
   32  */
   33 
   34 /*
   35  * Download the root RAMdisk through TFTP at root mount time
   36  */
   37 
   38 #include "opt_tftproot.h"
   39 #include "opt_md.h"
   40 
   41 #include <sys/cdefs.h>
   42 __KERNEL_RCSID(0, "$NetBSD: subr_tftproot.c,v 1.25 2020/12/05 17:42:29 thorpej Exp $");
   43 
   44 #include <sys/param.h>
   45 #include <sys/types.h>
   46 #include <sys/vnode.h>
   47 #include <sys/mount.h>
   48 #include <sys/lwp.h>
   49 #include <sys/kmem.h>
   50 #include <sys/mbuf.h>
   51 #include <sys/socketvar.h>
   52 
   53 #include <net/if.h>
   54 
   55 #include <dev/md.h>
   56 
   57 #include <netinet/in.h>
   58 
   59 #include <nfs/rpcv2.h>
   60 
   61 #include <nfs/nfsproto.h>
   62 #include <nfs/nfs.h>
   63 #include <nfs/nfsmount.h>
   64 #include <nfs/nfsdiskless.h>
   65 #include <nfs/nfs_var.h>
   66 
   67 /*
   68  * Copied from <lib/libsa/tftp.h>
   69  */
   70 
   71 #define SEGSIZE         512             /* data segment size */
   72 
   73 /*
   74  * Packet types.
   75  */
   76 #define RRQ     01                      /* read request */
   77 #define WRQ     02                      /* write request */
   78 #define DATA    03                      /* data packet */
   79 #define ACK     04                      /* acknowledgement */
   80 #define ERROR   05                      /* error code */
   81 
   82 struct  tftphdr {
   83         short   th_opcode;              /* packet type */
   84         union {
   85                 unsigned short tu_block; /* block # */
   86                 short   tu_code;        /* error code */
   87                 char    tu_stuff[1];    /* request packet stuff */
   88         } th_u;
   89         char    th_data[1];             /* data or error string */
   90 } __packed;
   91 
   92 #define th_block        th_u.tu_block
   93 #define th_code         th_u.tu_code
   94 #define th_stuff        th_u.tu_stuff
   95 #define th_msg          th_data
   96 
   97 #define IPPORT_TFTP 69
   98 
   99 #ifdef TFTPROOT_DEBUG
  100 #define DPRINTF(x) printf x
  101 #else
  102 #define DPRINTF(x)
  103 #endif
  104 
  105 struct tftproot_handle {
  106         struct nfs_diskless *trh_nd;
  107         void *trh_base;
  108         size_t trh_len;
  109         unsigned short trh_block;
  110         int trh_flags;
  111 };
  112 
  113 #define TRH_FINISHED    1
  114 
  115 int tftproot_dhcpboot(device_t);
  116 
  117 static int tftproot_getfile(struct tftproot_handle *, struct lwp *);
  118 static int tftproot_recv(struct mbuf **, void *);
  119 
  120 int
  121 tftproot_dhcpboot(device_t bootdv)
  122 {
  123         struct nfs_diskless *nd = NULL;
  124         struct ifnet *ifp = NULL;
  125         struct lwp *l;
  126         struct tftproot_handle trh;
  127         device_t dv;
  128         int error = -1;
  129 
  130         if (rootspec != NULL) {
  131                 int s = pserialize_read_enter();
  132                 IFNET_READER_FOREACH(ifp)
  133                         if (strcmp(rootspec, ifp->if_xname) == 0)
  134                                 break;
  135                 pserialize_read_exit(s);
  136         }
  137 
  138         if ((ifp == NULL) &&
  139             (bootdv != NULL && device_class(bootdv) == DV_IFNET)) {
  140                 int s = pserialize_read_enter();
  141                 IFNET_READER_FOREACH(ifp)
  142                         if (strcmp(device_xname(bootdv), ifp->if_xname) == 0)
  143                                 break;
  144                 pserialize_read_exit(s);
  145         }
  146 
  147         if (ifp == NULL) {
  148                 DPRINTF(("%s():%d ifp is NULL\n", __func__, __LINE__));
  149                 goto out;
  150         }
  151 
  152         dv = device_find_by_xname(ifp->if_xname);
  153 
  154         if ((dv == NULL) || (device_class(dv) != DV_IFNET)) {
  155                 DPRINTF(("%s():%d cannot find device for interface %s\n",
  156                     __func__, __LINE__, ifp->if_xname));
  157                 goto out;
  158         }
  159 
  160         root_device = dv;
  161 
  162         l = curlwp; /* XXX */
  163 
  164         nd = kmem_zalloc(sizeof(*nd), KM_SLEEP);
  165         nd->nd_ifp = ifp;
  166         nd->nd_nomount = 1;
  167 
  168         if ((error = nfs_boot_init(nd, l)) != 0) {
  169                 DPRINTF(("%s():%d nfs_boot_init returned %d\n",
  170                     __func__, __LINE__, error));
  171                 goto out;
  172         }
  173 
  174         /*
  175          * Strip leading "tftp:"
  176          */
  177 #define PREFIX "tftp:"
  178         if (strstr(nd->nd_bootfile, PREFIX) == nd->nd_bootfile)
  179                 (void)memmove(nd->nd_bootfile,
  180                               nd->nd_bootfile + (sizeof(PREFIX) - 1),
  181                               sizeof(nd->nd_bootfile) - sizeof(PREFIX));
  182 #undef PREFIX
  183 
  184         printf("tftproot: bootfile=%s\n", nd->nd_bootfile);
  185 
  186         memset(&trh, 0, sizeof(trh));
  187         trh.trh_nd = nd;
  188         trh.trh_block = 1;
  189 
  190         if ((error = tftproot_getfile(&trh, l)) != 0) {
  191                 DPRINTF(("%s():%d tftproot_getfile returned %d\n",
  192                     __func__, __LINE__, error));
  193                 goto out;
  194         }
  195 
  196         error = 0;
  197 
  198 out:
  199         if (nd)
  200                 kmem_free(nd, sizeof(*nd));
  201 
  202         return error;
  203 }
  204 
  205 static int
  206 tftproot_getfile(struct tftproot_handle *trh, struct lwp *l)
  207 {
  208         struct socket *so = NULL;
  209         struct mbuf *m_serv = NULL;
  210         struct mbuf *m_outbuf = NULL;
  211         struct sockaddr_in sin;
  212         struct tftphdr *tftp;
  213         size_t packetlen, namelen;
  214         int error = -1;
  215         const char octetstr[] = "octet";
  216         size_t hdrlen = sizeof(*tftp) - sizeof(tftp->th_data);
  217         char *cp;
  218         
  219         if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0, l, NULL)) != 0) {
  220                 DPRINTF(("%s():%d socreate returned %d\n",
  221                     __func__, __LINE__, error));
  222                 goto out;
  223         }
  224 
  225         /*
  226          * Set timeout
  227          */
  228         if ((error = nfs_boot_setrecvtimo(so))) {
  229                 DPRINTF(("%s():%d SO_RCVTIMEO failed %d\n",
  230                     __func__, __LINE__, error));
  231                 goto out;
  232         }
  233 
  234         /*
  235          * Set server address and port
  236          */
  237         memcpy(&sin, &trh->trh_nd->nd_root.ndm_saddr, sizeof(sin));
  238         sin.sin_port = htons(IPPORT_TFTP);
  239 
  240         /*
  241          * Set send buffer, prepare the TFTP packet
  242          */
  243         namelen = strlen(trh->trh_nd->nd_bootfile) + 1;
  244         packetlen = sizeof(tftp->th_opcode) + namelen + sizeof(octetstr);
  245         if (packetlen > MSIZE) {
  246                 DPRINTF(("%s():%d boot filename too long (%ld bytes)\n",
  247                     __func__, __LINE__, (long)namelen));
  248                 error = E2BIG;
  249                 goto out;
  250         }
  251 
  252         m_outbuf = m_gethdr(M_WAIT, MT_DATA);
  253         m_clget(m_outbuf, M_WAIT);
  254         m_outbuf->m_len = packetlen;
  255         m_outbuf->m_pkthdr.len = packetlen;
  256         m_reset_rcvif(m_outbuf);
  257 
  258         tftp = mtod(m_outbuf, struct tftphdr *);
  259         memset(tftp, 0, packetlen);
  260 
  261         tftp->th_opcode = htons((short)RRQ);
  262         cp = tftp->th_stuff;
  263         (void)strncpy(cp,  trh->trh_nd->nd_bootfile, namelen);
  264         cp += namelen;
  265         (void)strncpy(cp, octetstr, sizeof(octetstr));
  266 
  267         /*
  268          * Perform the file transfer
  269          */
  270         printf("tftproot: download %s:%s ",
  271             inet_ntoa(sin.sin_addr), trh->trh_nd->nd_bootfile);
  272 
  273         do {
  274                 /*
  275                  * Show progress for every 200 blocks (100kB)
  276                  */
  277 #ifndef TFTPROOT_PROGRESS
  278 #define TFTPROOT_PROGRESS 200
  279 #endif
  280                 if ((trh->trh_block % TFTPROOT_PROGRESS) == 0)
  281                         twiddle();
  282 
  283                 /*
  284                  * Send the packet and receive the answer.
  285                  * We get the sender address here, which should be
  286                  * the same server with a different port
  287                  */
  288                 if ((error = nfs_boot_sendrecv(so, &sin, NULL, m_outbuf,
  289                     tftproot_recv, NULL, &m_serv, trh, l)) != 0) {
  290                         DPRINTF(("%s():%d sendrecv failed %d\n",
  291                             __func__, __LINE__, error));
  292                         goto out;
  293                 }
  294 
  295                 /*
  296                  * Accommodate the packet length for acks.
  297                  * This is really needed only on first pass
  298                  */
  299                 m_outbuf->m_len = hdrlen;
  300                 m_outbuf->m_pkthdr.len = hdrlen;
  301                 tftp->th_opcode = htons((short)ACK);
  302                 tftp->th_block = htons(trh->trh_block);
  303 
  304 
  305                 /*
  306                  * Check for termination
  307                  */
  308                 if (trh->trh_flags & TRH_FINISHED)
  309                         break;
  310 
  311                 trh->trh_block++;
  312         } while (1/* CONSTCOND */);
  313 
  314         printf("\n");
  315 
  316         /*
  317          * Ack the last block. Ignore errors, as we already have the whole
  318          * file.
  319          */
  320         if ((error = (*so->so_send)(so, mtod(m_serv, struct sockaddr *), NULL,
  321             m_outbuf, NULL, 0, l)) != 0) {
  322                 DPRINTF(("%s():%d tftproot: sosend returned %d\n",
  323                     __func__, __LINE__, error));
  324         }
  325 
  326         /* Freed by the protocol */
  327         m_outbuf = NULL;
  328 
  329         /*
  330          * And use it as the root ramdisk.
  331          */
  332         DPRINTF(("%s():%d RAMdisk loaded: %ld@%p\n",
  333             __func__, __LINE__, trh->trh_len, trh->trh_base));
  334         md_root_setconf(trh->trh_base, trh->trh_len);
  335 
  336         error = 0;
  337 out:
  338         if (m_serv)
  339                 m_freem(m_serv);
  340 
  341         if (m_outbuf)
  342                 m_freem(m_outbuf);
  343 
  344         if (so)
  345                 soclose(so);
  346 
  347         return error;
  348 }
  349 
  350 static int
  351 tftproot_recv(struct mbuf **mp, void *ctx)
  352 {
  353         struct tftproot_handle *trh = ctx;
  354         struct tftphdr *tftp;
  355         struct mbuf *m = *mp;
  356         size_t newlen;
  357         size_t hdrlen = sizeof(*tftp) - sizeof(tftp->th_data);
  358 
  359         /*
  360          * Check for short packet
  361          */
  362         if (m->m_pkthdr.len < hdrlen) {
  363                 DPRINTF(("%s():%d short reply (%d bytes)\n",
  364                     __func__, __LINE__, m->m_pkthdr.len));
  365                 return -1;
  366         }
  367 
  368         /*
  369          * Check for packet too large for being a TFTP packet
  370          */
  371         if (m->m_pkthdr.len > hdrlen + SEGSIZE) {
  372                 DPRINTF(("%s():%d packet too big (%d bytes)\n",
  373                     __func__, __LINE__, m->m_pkthdr.len));
  374                 return -1;
  375         }
  376 
  377 
  378         /*
  379          * Examine the TFTP header
  380          */
  381         if (m->m_len > sizeof(*tftp)) {
  382                 if ((m = *mp = m_pullup(m, sizeof(*tftp))) == NULL) {
  383                         DPRINTF(("%s():%d m_pullup failed\n",
  384                             __func__, __LINE__));
  385                         return -1;
  386                 }
  387         }
  388         tftp = mtod(m, struct tftphdr *);
  389 
  390         /*
  391          * We handle data or error
  392          */
  393         switch(ntohs(tftp->th_opcode)) {
  394         case DATA:
  395                 break;
  396                 
  397         case ERROR: {
  398                 char errbuf[SEGSIZE + 1];
  399                 int i;
  400 
  401                 for (i = 0; i < SEGSIZE; i++) {
  402                         if ((tftp->th_data[i] < ' ') ||
  403                             (tftp->th_data[i] > '~')) {
  404                                 errbuf[i] = '\0';
  405                                 break;
  406                         }
  407                         errbuf[i] = tftp->th_data[i];
  408                 }
  409                 errbuf[SEGSIZE] = '\0';
  410 
  411                 printf("tftproot: TFTP server returned error %d, %s\n",
  412                     ntohs(tftp->th_code), errbuf);
  413 
  414                 return -1;
  415                 break;
  416         }
  417 
  418         default:
  419                 DPRINTF(("%s():%d unexpected tftp reply opcode %d\n",
  420                     __func__, __LINE__, ntohs(tftp->th_opcode)));
  421                 return -1;
  422                 break;
  423         }
  424 
  425         /*
  426          * Check for last packet, which does not fill the whole space
  427          */
  428         if (m->m_pkthdr.len < hdrlen + SEGSIZE) {
  429                 DPRINTF(("%s():%d last chunk (%d bytes)\n",
  430                     __func__, __LINE__, m->m_pkthdr.len));
  431                 trh->trh_flags |= TRH_FINISHED;
  432         }
  433 
  434 
  435         if (ntohs(tftp->th_block) != trh->trh_block) {
  436                 DPRINTF(("%s():%d expected block %d, got block %d\n",
  437                     __func__, __LINE__, trh->trh_block, ntohs(tftp->th_block)));
  438                 return -1;
  439         }
  440 
  441         /*
  442          * Grow the receiving buffer to accommodate new data
  443          */
  444         newlen = trh->trh_len + (m->m_pkthdr.len - hdrlen);
  445         if ((trh->trh_base = realloc(trh->trh_base,
  446             newlen, M_TEMP, M_WAITOK)) == NULL) {
  447                 DPRINTF(("%s():%d failed to realloc %ld bytes\n",
  448                     __func__, __LINE__, (long)newlen));
  449                 return -1;
  450         }
  451 
  452         /*
  453          * Copy the data
  454          */
  455         m_copydata(m, hdrlen, m->m_pkthdr.len - hdrlen,
  456                    (char *)trh->trh_base + trh->trh_len);
  457         trh->trh_len = newlen;
  458 
  459         return 0;
  460 }
  461 

Cache object: 7452948afeb2eb35c693099608bf7262


[ 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.