Home | History | Annotate | Download | only in netboot
      1 /*
      2  *  GRUB  --  GRand Unified Bootloader
      3  *  Copyright (C) 2000,2001,2002,2004  Free Software Foundation, Inc.
      4  *
      5  *  This program is free software; you can redistribute it and/or modify
      6  *  it under the terms of the GNU General Public License as published by
      7  *  the Free Software Foundation; either version 2 of the License, or
      8  *  (at your option) any later version.
      9  *
     10  *  This program is distributed in the hope that it will be useful,
     11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *  GNU General Public License for more details.
     14  *
     15  *  You should have received a copy of the GNU General Public License
     16  *  along with this program; if not, write to the Free Software
     17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18  */
     19 
     20 /* Based on "src/main.c" in etherboot-4.5.8.  */
     21 /**************************************************************************
     22 ETHERBOOT -  BOOTP/TFTP Bootstrap Program
     23 
     24 Author: Martin Renters
     25   Date: Dec/93
     26 
     27 **************************************************************************/
     28 
     29 /* #define TFTP_DEBUG	1 */
     30 
     31 #include <filesys.h>
     32 
     33 #define GRUB	1
     34 #include <etherboot.h>
     35 #include <nic.h>
     36 
     37 static int retry;
     38 static unsigned short iport = 2000;
     39 static unsigned short oport;
     40 static unsigned short block, prevblock;
     41 static int bcounter;
     42 static struct tftp_t tp, saved_tp;
     43 static int packetsize;
     44 static int buf_eof, buf_read;
     45 static int saved_filepos;
     46 static unsigned short len, saved_len;
     47 static char *buf;
     48 
     49 /* Fill the buffer by receiving the data via the TFTP protocol.  */
     50 static int
     51 buf_fill (int abort)
     52 {
     53 #ifdef TFTP_DEBUG
     54   grub_printf ("buf_fill (%d)\n", abort);
     55 #endif
     56 
     57   while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN))
     58     {
     59       struct tftp_t *tr;
     60       long timeout;
     61 
     62 #ifdef CONGESTED
     63       timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry);
     64 #else
     65       timeout = rfc2131_sleep_interval (TIMEOUT, retry);
     66 #endif
     67 
     68       if (! await_reply (AWAIT_TFTP, iport, NULL, timeout))
     69 	{
     70 	  if (ip_abort)
     71 	    return 0;
     72 
     73 	  if (! block && retry++ < MAX_TFTP_RETRIES)
     74 	    {
     75 	      /* Maybe initial request was lost.  */
     76 #ifdef TFTP_DEBUG
     77 	      grub_printf ("Maybe initial request was lost.\n");
     78 #endif
     79 	      if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
     80 				  ++iport, TFTP_PORT, len, &tp))
     81 		return 0;
     82 
     83 	      continue;
     84 	    }
     85 
     86 #ifdef CONGESTED
     87 	  if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
     88 	    {
     89 	      /* We resend our last ack.  */
     90 # ifdef TFTP_DEBUG
     91 	      grub_printf ("<REXMT>\n");
     92 # endif
     93 	      udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
     94 			    iport, oport,
     95 			    TFTP_MIN_PACKET, &tp);
     96 	      continue;
     97 	    }
     98 #endif
     99 	  /* Timeout.  */
    100 	  return 0;
    101 	}
    102 
    103       tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
    104       if (tr->opcode == ntohs (TFTP_ERROR))
    105 	{
    106 	  grub_printf ("TFTP error %d (%s)\n",
    107 		       ntohs (tr->u.err.errcode),
    108 		       tr->u.err.errmsg);
    109 	  return 0;
    110 	}
    111 
    112       if (tr->opcode == ntohs (TFTP_OACK))
    113 	{
    114 	  char *p = tr->u.oack.data, *e;
    115 
    116 #ifdef TFTP_DEBUG
    117 	  grub_printf ("OACK ");
    118 #endif
    119 	  /* Shouldn't happen.  */
    120 	  if (prevblock)
    121 	    {
    122 	      /* Ignore it.  */
    123 	      grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
    124 			   __FILE__, __LINE__, prevblock);
    125 	      continue;
    126 	    }
    127 
    128 	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 2;
    129 	  if (len > TFTP_MAX_PACKET)
    130 	    goto noak;
    131 
    132 	  e = p + len;
    133 	  while (*p != '\000' && p < e)
    134 	    {
    135 	      if (! grub_strcmp ("blksize", p))
    136 		{
    137 		  p += 8;
    138 		  if ((packetsize = getdec (&p)) < TFTP_DEFAULTSIZE_PACKET)
    139 		    goto noak;
    140 #ifdef TFTP_DEBUG
    141 		  grub_printf ("blksize = %d\n", packetsize);
    142 #endif
    143 		}
    144 	      else if (! grub_strcmp ("tsize", p))
    145 		{
    146 		  p += 6;
    147 		  if ((filemax = getdec (&p)) < 0)
    148 		    {
    149 		      filemax = -1;
    150 		      goto noak;
    151 		    }
    152 #ifdef TFTP_DEBUG
    153 		  grub_printf ("tsize = %d\n", filemax);
    154 #endif
    155 		}
    156 	      else
    157 		{
    158 		noak:
    159 #ifdef TFTP_DEBUG
    160 		  grub_printf ("NOAK\n");
    161 #endif
    162 		  tp.opcode = htons (TFTP_ERROR);
    163 		  tp.u.err.errcode = 8;
    164 		  len = (grub_sprintf ((char *) tp.u.err.errmsg,
    165 				       "RFC1782 error")
    166 			 + sizeof (tp.ip) + sizeof (tp.udp)
    167 			 + sizeof (tp.opcode) + sizeof (tp.u.err.errcode)
    168 			 + 1);
    169 		  udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
    170 				iport, ntohs (tr->udp.src),
    171 				len, &tp);
    172 		  return 0;
    173 		}
    174 
    175 	      while (p < e && *p)
    176 		p++;
    177 
    178 	      if (p < e)
    179 		p++;
    180 	    }
    181 
    182 	  if (p > e)
    183 	    goto noak;
    184 
    185 	  /* This ensures that the packet does not get processed as
    186 	     data!  */
    187 	  block = tp.u.ack.block = 0;
    188 	}
    189       else if (tr->opcode == ntohs (TFTP_DATA))
    190 	{
    191 #ifdef TFTP_DEBUG
    192 	  grub_printf ("DATA ");
    193 #endif
    194 	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4;
    195 
    196 	  /* Shouldn't happen.  */
    197 	  if (len > packetsize)
    198 	    {
    199 	      /* Ignore it.  */
    200 	      grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
    201 			   __FILE__, __LINE__, len, packetsize);
    202 	      continue;
    203 	    }
    204 
    205 	  block = ntohs (tp.u.ack.block = tr->u.data.block);
    206 	}
    207       else
    208 	/* Neither TFTP_OACK nor TFTP_DATA.  */
    209 	break;
    210 
    211       if ((block || bcounter) && (block != prevblock + (unsigned short) 1))
    212 	/* Block order should be continuous */
    213 	tp.u.ack.block = htons (block = prevblock);
    214 
    215       /* Should be continuous.  */
    216       tp.opcode = abort ? htons (TFTP_ERROR) : htons (TFTP_ACK);
    217       oport = ntohs (tr->udp.src);
    218 
    219 #ifdef TFTP_DEBUG
    220       grub_printf ("ACK\n");
    221 #endif
    222       /* Ack.  */
    223       udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport,
    224 		    oport, TFTP_MIN_PACKET, &tp);
    225 
    226       if (abort)
    227 	{
    228 	  buf_eof = 1;
    229 	  break;
    230 	}
    231 
    232       /* Retransmission or OACK.  */
    233       if ((unsigned short) (block - prevblock) != 1)
    234 	/* Don't process.  */
    235 	continue;
    236 
    237       prevblock = block;
    238       /* Is it the right place to zero the timer?  */
    239       retry = 0;
    240 
    241       /* In GRUB, this variable doesn't play any important role at all,
    242 	 but use it for consistency with Etherboot.  */
    243       bcounter++;
    244 
    245       /* Copy the downloaded data to the buffer.  */
    246       grub_memmove (buf + buf_read, tr->u.data.download, len);
    247       buf_read += len;
    248 
    249       /* End of data.  */
    250       if (len < packetsize)
    251 	buf_eof = 1;
    252     }
    253 
    254   return 1;
    255 }
    256 
    257 /* Send the RRQ whose length is LEN.  */
    258 static int
    259 send_rrq (void)
    260 {
    261   /* Initialize some variables.  */
    262   retry = 0;
    263   block = 0;
    264   prevblock = 0;
    265   packetsize = TFTP_DEFAULTSIZE_PACKET;
    266   bcounter = 0;
    267 
    268   buf = (char *) FSYS_BUF;
    269   buf_eof = 0;
    270   buf_read = 0;
    271   saved_filepos = 0;
    272 
    273   /* Clear out the Rx queue first.  It contains nothing of interest,
    274    * except possibly ARP requests from the DHCP/TFTP server.  We use
    275    * polling throughout Etherboot, so some time may have passed since we
    276    * last polled the receive queue, which may now be filled with
    277    * broadcast packets.  This will cause the reply to the packets we are
    278    * about to send to be lost immediately.  Not very clever.  */
    279   await_reply (AWAIT_QDRAIN, 0, NULL, 0);
    280 
    281 #ifdef TFTP_DEBUG
    282   grub_printf ("send_rrq ()\n");
    283   {
    284     int i;
    285     char *p;
    286 
    287     for (i = 0, p = (char *) &tp; i < len; i++)
    288       if (p[i] >= ' ' && p[i] <= '~')
    289 	grub_putchar (p[i]);
    290       else
    291 	grub_printf ("\\%x", (unsigned) p[i]);
    292 
    293     grub_putchar ('\n');
    294   }
    295 #endif
    296   /* Send the packet.  */
    297   return udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
    298 		       TFTP_PORT, len, &tp);
    299 }
    300 
    301 /* Mount the network drive. If the drive is ready, return one, otherwise
    302    return zero.  */
    303 int
    304 tftp_mount (void)
    305 {
    306   /* Check if the current drive is the network drive.  */
    307   if (current_drive != NETWORK_DRIVE)
    308     return 0;
    309 
    310   /* If the drive is not initialized yet, abort.  */
    311   if (! network_ready)
    312     return 0;
    313 
    314   return 1;
    315 }
    316 
    317 /* Read up to SIZE bytes, returned in ADDR.  */
    318 int
    319 tftp_read (char *addr, int size)
    320 {
    321   /* How many bytes is read?  */
    322   int ret = 0;
    323 
    324 #ifdef TFTP_DEBUG
    325   grub_printf ("tftp_read (0x%x, %d)\n", (int) addr, size);
    326 #endif
    327 
    328   if (filepos < saved_filepos)
    329     {
    330       /* Uggh.. FILEPOS has been moved backwards. So reopen the file.  */
    331       buf_read = 0;
    332       buf_fill (1);
    333       grub_memmove ((char *) &tp, (char *) &saved_tp, saved_len);
    334       len = saved_len;
    335 #ifdef TFTP_DEBUG
    336       {
    337 	int i;
    338 	grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp.opcode);
    339 	for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
    340 	  {
    341 	    if (tp.u.rrq[i] >= ' ' && tp.u.rrq[i] <= '~')
    342 	      grub_putchar (tp.u.rrq[i]);
    343 	    else
    344 	      grub_putchar ('*');
    345 	  }
    346 	grub_putchar ('\n');
    347       }
    348 #endif
    349 
    350       if (! send_rrq ())
    351 	{
    352 	  errnum = ERR_WRITE;
    353 	  return 0;
    354 	}
    355     }
    356 
    357   while (size > 0)
    358     {
    359       int amt = buf_read + saved_filepos - filepos;
    360 
    361       /* If the length that can be copied from the buffer is over the
    362 	 requested size, cut it down.  */
    363       if (amt > size)
    364 	amt = size;
    365 
    366       if (amt > 0)
    367 	{
    368 	  /* Copy the buffer to the supplied memory space.  */
    369 	  grub_memmove (addr, buf + filepos - saved_filepos, amt);
    370 	  size -= amt;
    371 	  addr += amt;
    372 	  filepos += amt;
    373 	  ret += amt;
    374 
    375 	  /* If the size of the empty space becomes small, move the unused
    376 	     data forwards.  */
    377 	  if (filepos - saved_filepos > FSYS_BUFLEN / 2)
    378 	    {
    379 	      grub_memmove (buf, buf + FSYS_BUFLEN / 2, FSYS_BUFLEN / 2);
    380 	      buf_read -= FSYS_BUFLEN / 2;
    381 	      saved_filepos += FSYS_BUFLEN / 2;
    382 	    }
    383 	}
    384       else
    385 	{
    386 	  /* Skip the whole buffer.  */
    387 	  saved_filepos += buf_read;
    388 	  buf_read = 0;
    389 	}
    390 
    391       /* Read the data.  */
    392       if (size > 0 && ! buf_fill (0))
    393 	{
    394 	  errnum = ERR_READ;
    395 	  return 0;
    396 	}
    397 
    398       /* Sanity check.  */
    399       if (size > 0 && buf_read == 0)
    400 	{
    401 	  errnum = ERR_READ;
    402 	  return 0;
    403 	}
    404     }
    405 
    406   return ret;
    407 }
    408 
    409 /* Check if the file DIRNAME really exists. Get the size and save it in
    410    FILEMAX.  */
    411 int
    412 tftp_dir (char *dirname)
    413 {
    414   int ch;
    415 
    416 #ifdef TFTP_DEBUG
    417   grub_printf ("tftp_dir (%s)\n", dirname);
    418 #endif
    419 
    420   /* In TFTP, there is no way to know what files exist.  */
    421   if (print_possibilities)
    422     return 1;
    423 
    424   /* Don't know the size yet.  */
    425   filemax = -1;
    426 
    427  reopen:
    428   /* Construct the TFTP request packet.  */
    429   tp.opcode = htons (TFTP_RRQ);
    430   /* Terminate the filename.  */
    431   ch = nul_terminate (dirname);
    432   /* Make the request string (octet, blksize and tsize).  */
    433   len = (grub_sprintf ((char *) tp.u.rrq,
    434 		       "%s%coctet%cblksize%c%d%ctsize%c0",
    435 		       dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
    436 	 + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1);
    437   /* Restore the original DIRNAME.  */
    438   dirname[grub_strlen (dirname)] = ch;
    439   /* Save the TFTP packet so that we can reopen the file later.  */
    440   grub_memmove ((char *) &saved_tp, (char *) &tp, len);
    441   saved_len = len;
    442   if (! send_rrq ())
    443     {
    444       errnum = ERR_WRITE;
    445       return 0;
    446     }
    447 
    448   /* Read the data.  */
    449   if (! buf_fill (0))
    450     {
    451       errnum = ERR_FILE_NOT_FOUND;
    452       return 0;
    453     }
    454 
    455   if (filemax == -1)
    456     {
    457       /* The server doesn't support the "tsize" option, so we must read
    458 	 the file twice...  */
    459 
    460       /* Zero the size of the file.  */
    461       filemax = 0;
    462       do
    463 	{
    464 	  /* Add the length of the downloaded data.  */
    465 	  filemax += buf_read;
    466 	  /* Reset the offset. Just discard the contents of the buffer.  */
    467 	  buf_read = 0;
    468 	  /* Read the data.  */
    469 	  if (! buf_fill (0))
    470 	    {
    471 	      errnum = ERR_READ;
    472 	      return 0;
    473 	    }
    474 	}
    475       while (! buf_eof);
    476 
    477       /* Maybe a few amounts of data remains.  */
    478       filemax += buf_read;
    479 
    480       /* Retry the open instruction.  */
    481       goto reopen;
    482     }
    483 
    484   return 1;
    485 }
    486 
    487 /* Close the file.  */
    488 void
    489 tftp_close (void)
    490 {
    491 #ifdef TFTP_DEBUG
    492   grub_printf ("tftp_close ()\n");
    493 #endif
    494 
    495   buf_read = 0;
    496   buf_fill (1);
    497 }
    498