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.1.2.2 2007/05/13 10:29:46 jdc 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.1.2.2 2007/05/13 10:29:46 jdc Exp $");
   43 
   44 #include <sys/param.h>
   45 #include <sys/types.h>
   46 #include <sys/mount.h>
   47 #include <sys/lwp.h>
   48 #include <sys/malloc.h>
   49 #include <sys/mbuf.h>
   50 #include <sys/timevar.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 extern void       mdattach(int);
   68 
   69 /* 
   70  * Copied from <lib/libsa/tftp.h> 
   71  */
   72 
   73 #define SEGSIZE         512             /* data segment size */
   74 
   75 /*
   76  * Packet types.
   77  */
   78 #define RRQ     01                      /* read request */
   79 #define WRQ     02                      /* write request */
   80 #define DATA    03                      /* data packet */
   81 #define ACK     04                      /* acknowledgement */
   82 #define ERROR   05                      /* error code */
   83 
   84 struct  tftphdr {
   85         short   th_opcode;              /* packet type */
   86         union {
   87                 unsigned short tu_block; /* block # */
   88                 short   tu_code;        /* error code */
   89                 char    tu_stuff[1];    /* request packet stuff */
   90         } th_u;
   91         char    th_data[1];             /* data or error string */
   92 } __packed;
   93 
   94 #define th_block        th_u.tu_block
   95 #define th_code         th_u.tu_code
   96 #define th_stuff        th_u.tu_stuff
   97 #define th_msg          th_data
   98 
   99 #define IPPORT_TFTP 69
  100 
  101 #ifdef TFTPROOT_DEBUG
  102 #define DPRINTF(x) printf x
  103 #else
  104 #define DPRINTF(x)
  105 #endif
  106 
  107 struct tftproot_handle {
  108         struct nfs_diskless *trh_nd;
  109         void *trh_base;
  110         size_t trh_len;
  111         unsigned short trh_block;
  112         int trh_flags;
  113 };
  114 
  115 #define TRH_FINISHED    1
  116 
  117 int tftproot_dhcpboot(struct device *);
  118 
  119 static int tftproot_getfile(struct tftproot_handle *, struct lwp *);
  120 static int tftproot_recv __P((struct mbuf*, void*));
  121 
  122 int
  123 tftproot_dhcpboot(bootdv)
  124         struct device *bootdv;
  125 {
  126         struct nfs_diskless *nd = NULL;
  127         struct ifnet *ifp = NULL;
  128         struct lwp *l;
  129         struct tftproot_handle trh;
  130         struct device *dv;
  131         int error = -1;
  132 
  133         if (rootspec != NULL) {
  134                 IFNET_FOREACH(ifp)
  135                         if (strcmp(rootspec, ifp->if_xname) == 0)
  136                                 break;
  137         } else if ((bootdv != NULL && device_class(bootdv) == DV_IFNET)) {
  138                 IFNET_FOREACH(ifp)
  139                         if (strcmp(bootdv->dv_xname, ifp->if_xname) == 0)
  140                                 break;
  141         }
  142 
  143         if (ifp == NULL) {
  144                 DPRINTF(("%s():%d ifp is NULL\n", __func__, __LINE__));
  145                 goto out;
  146         }
  147 
  148         for (dv = TAILQ_FIRST(&alldevs); dv != NULL;
  149              dv = TAILQ_NEXT(dv, dv_list))
  150                 if (strcmp(dv->dv_xname, ifp->if_xname) == 0)
  151                         break;
  152 
  153         if ((dv == NULL) || (device_class(dv) != DV_IFNET)) {
  154                 DPRINTF(("%s():%d cannot find device for interface %s\n",
  155                     __func__, __LINE__, ifp->if_xname));
  156                 goto out;
  157         }
  158 
  159         root_device = dv;
  160 
  161         l = curlwp; /* XXX */
  162 
  163         nd = malloc(sizeof(*nd), M_NFSMNT, M_WAITOK);
  164         bzero(nd, sizeof(nd));
  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         bzero(&trh, 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                 free(nd, M_NFSMNT);
  201 
  202         return error;
  203 }
  204 
  205 static int 
  206 tftproot_getfile(trh, l)
  207         struct tftproot_handle *trh;
  208         struct lwp *l;
  209 {
  210         struct socket *so = NULL;
  211         struct mbuf *m_serv = NULL;
  212         struct mbuf *m_outbuf = NULL;
  213         struct sockaddr_in *sin;
  214         struct tftphdr *tftp;
  215         size_t packetlen, namelen;
  216         int error = -1;
  217         const char octetstr[] = "octet";
  218         size_t hdrlen = sizeof(*tftp) - sizeof(tftp->th_data);
  219         char *cp;
  220         /* struct device *dv; */
  221         
  222         if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0, l)) != 0) {
  223                 DPRINTF(("%s():%d socreate returned %d\n", 
  224                     __func__, __LINE__, error));
  225                 goto out;
  226         }
  227 
  228         /*
  229          * Set timeout
  230          */
  231         if ((error = nfs_boot_setrecvtimo(so))) {
  232                 DPRINTF(("%s():%d SO_RCVTIMEO failed %d\n", 
  233                     __func__, __LINE__, error));
  234                 goto out;
  235         }
  236 
  237         /*
  238          * Set server address and port
  239          */
  240         m_serv = m_get(M_WAIT, MT_SONAME);
  241         m_serv->m_len = sizeof(*sin);
  242         sin = mtod(m_serv, struct sockaddr_in *);
  243         memcpy(sin, &trh->trh_nd->nd_root.ndm_saddr, sizeof(*sin));
  244         sin->sin_port = htons(IPPORT_TFTP);
  245 
  246         /*
  247          * Set send buffer, prepare the TFTP packet
  248          */
  249         namelen = strlen(trh->trh_nd->nd_bootfile) + 1;
  250         packetlen = sizeof(tftp->th_opcode) + namelen + sizeof(octetstr);
  251         if (packetlen > MSIZE) {
  252                 DPRINTF(("%s():%d boot filename too long (%ld bytes)\n", 
  253                     __func__, __LINE__, (long)namelen));
  254                 goto out;
  255         }
  256 
  257         m_outbuf = m_gethdr(M_WAIT, MT_DATA);
  258         m_clget(m_outbuf, M_WAIT);
  259         m_outbuf->m_len = packetlen;
  260         m_outbuf->m_pkthdr.len = packetlen;
  261         m_outbuf->m_pkthdr.rcvif = NULL;
  262 
  263         tftp = mtod(m_outbuf, struct tftphdr *);
  264         memset(tftp, 0, packetlen);
  265 
  266         tftp->th_opcode = htons((short)RRQ);
  267         cp = tftp->th_stuff;
  268         (void)strncpy(cp,  trh->trh_nd->nd_bootfile, namelen);
  269         cp += namelen;
  270         (void)strncpy(cp, octetstr, sizeof(octetstr));
  271 
  272         /* 
  273          * Perform the file transfer
  274          */
  275         sin = (struct sockaddr_in *)&trh->trh_nd->nd_root.ndm_saddr;
  276         printf("tftproot: download %s:%s ", 
  277             inet_ntoa(sin->sin_addr), trh->trh_nd->nd_bootfile);
  278 
  279         do {
  280                 /*
  281                  * Show progress for every 200 blocks (100kB)
  282                  */
  283 #ifndef TFTPROOT_PROGRESS
  284 #define TFTPROOT_PROGRESS 200
  285 #endif
  286                 if ((trh->trh_block % TFTPROOT_PROGRESS) == 0)
  287                         twiddle();
  288 
  289                 /* 
  290                  * Send the packet and receive the answer. 
  291                  * We get the sender address here, which should be
  292                  * the same server with a different port
  293                  */
  294                 if ((error = nfs_boot_sendrecv(so, m_serv, NULL, m_outbuf,
  295                     tftproot_recv, NULL, &m_serv, trh, l)) != 0) {
  296                         DPRINTF(("%s():%d sendrecv failed %d\n", 
  297                             __func__, __LINE__, error));
  298                         goto out;
  299                 }
  300 
  301                 /* 
  302                  * Accomodate the packet length for acks.
  303                  * This is really needed only on first pass
  304                  */
  305                 m_outbuf->m_len = hdrlen;
  306                 m_outbuf->m_pkthdr.len = hdrlen;
  307                 tftp->th_opcode = htons((short)ACK);
  308                 tftp->th_block = htons(trh->trh_block);
  309 
  310 
  311                 /*
  312                  * Check for termination
  313                  */
  314                 if (trh->trh_flags & TRH_FINISHED)
  315                         break;
  316 
  317                 trh->trh_block++;
  318         } while (1/* CONSTCOND */);
  319 
  320         printf("\n");
  321 
  322         /*
  323          * Ack the last block. so_send frees m_outbuf, therefore
  324          * we do not want to free it ourselves.
  325          * Ignore errors, as we already have the whole file.
  326          */
  327         if ((error = (*so->so_send)(so, m_serv, NULL, 
  328             m_outbuf, NULL, 0, l)) != 0)
  329                 DPRINTF(("%s():%d tftproot: sosend returned %d\n", 
  330                     __func__, __LINE__, error));
  331         else
  332                 m_outbuf = NULL;
  333 
  334         /* 
  335          * And use it as the root ramdisk. 
  336          */
  337         DPRINTF(("%s():%d RAMdisk loaded: %ld@%p\n", 
  338             __func__, __LINE__, trh->trh_len, trh->trh_base));
  339         md_root_setconf(trh->trh_base, trh->trh_len);
  340         mdattach(0);
  341 
  342         error = 0;
  343 out:
  344         if (m_serv)
  345                 m_freem(m_serv);
  346 
  347         if (m_outbuf)
  348                 m_freem(m_outbuf);
  349 
  350         if (so)
  351                 soclose(so);
  352 
  353         return error;
  354 }
  355 
  356 static int
  357 tftproot_recv(m, ctx)
  358         struct mbuf *m;
  359         void *ctx;
  360 {
  361         struct tftproot_handle *trh = ctx;
  362         struct tftphdr *tftp;
  363         size_t newlen;
  364         size_t hdrlen = sizeof(*tftp) - sizeof(tftp->th_data);
  365 
  366         /*
  367          * Check for short packet
  368          */
  369         if (m->m_pkthdr.len < hdrlen) {
  370                 DPRINTF(("%s():%d short reply (%d bytes)\n", 
  371                     __func__, __LINE__, m->m_pkthdr.len));
  372                 return -1;
  373         }
  374 
  375         /*
  376          * Check for packet too large for being a TFTP packet
  377          */
  378         if (m->m_pkthdr.len > hdrlen + SEGSIZE) {
  379                 DPRINTF(("%s():%d packet too big (%d bytes)\n", 
  380                     __func__, __LINE__, m->m_pkthdr.len));
  381                 return -1;
  382         }
  383 
  384 
  385         /*
  386          * Examine the TFTP header
  387          */
  388         if (m->m_len > sizeof(*tftp)) {
  389                 if ((m = m_pullup(m, sizeof(*tftp))) == NULL) {
  390                         DPRINTF(("%s():%d m_pullup failed\n",
  391                             __func__, __LINE__));
  392                         return -1;
  393                 }
  394         }
  395         tftp = mtod(m, struct tftphdr *);
  396 
  397         /*
  398          * We handle data or error
  399          */
  400         switch(ntohs(tftp->th_opcode)) {
  401         case DATA:
  402                 break;
  403                 
  404         case ERROR: {
  405                 char errbuf[SEGSIZE + 1];
  406                 int i;
  407 
  408                 for (i = 0; i < SEGSIZE; i++) {
  409                         if ((tftp->th_data[i] < ' ') ||
  410                             (tftp->th_data[i] > '~')) {
  411                                 errbuf[i] = '\0';
  412                                 break;
  413                         }
  414                         errbuf[i] = tftp->th_data[i];
  415                 }
  416                 errbuf[SEGSIZE] = '\0';
  417 
  418                 printf("tftproot: TFTP server returned error %d, %s\n",
  419                     ntohs(tftp->th_code), errbuf);
  420 
  421                 return -1;
  422                 break;
  423         }
  424 
  425         default:
  426                 DPRINTF(("%s():%d unexpected tftp reply opcode %d\n", 
  427                     __func__, __LINE__, ntohs(tftp->th_opcode)));
  428                 return -1;
  429                 break;
  430         }
  431 
  432         /*
  433          * Check for last packet, which does not fill the whole space
  434          */
  435         if (m->m_pkthdr.len < hdrlen + SEGSIZE) {
  436                 DPRINTF(("%s():%d last chunk (%d bytes)\n", 
  437                     __func__, __LINE__, m->m_pkthdr.len));
  438                 trh->trh_flags |= TRH_FINISHED;
  439         }
  440 
  441 
  442         if (ntohs(tftp->th_block) != trh->trh_block) {
  443                 DPRINTF(("%s():%d expected block %d, got block %d\n",
  444                     __func__, __LINE__, trh->trh_block, ntohs(tftp->th_block)));
  445                 return -1;
  446         }
  447 
  448         /* 
  449          * Grow the receiving buffer to accomodate new data
  450          */
  451         newlen = trh->trh_len + (m->m_pkthdr.len - hdrlen);
  452         if ((trh->trh_base = realloc(trh->trh_base, 
  453             newlen, M_TEMP, M_WAITOK)) == NULL) {
  454                 DPRINTF(("%s():%d failed to realloc %ld bytes\n", 
  455                     __func__, __LINE__, (long)newlen));
  456                 return -1;
  457         }
  458 
  459         /*
  460          * Copy the data
  461          */
  462         m_copydata(m, hdrlen, m->m_pkthdr.len - hdrlen,
  463                    (char *)trh->trh_base + trh->trh_len);
  464         trh->trh_len = newlen;
  465 
  466         return 0;
  467 }
  468 

Cache object: e2fc87d7bc5034e8df6e14fe9ac7b8f5


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