📄 tftp.c
字号:
/* tftp.c:
* This code supports the monitor's TFTP server.
* TFTP is covered under RFC 783.
*
* General notice:
* This code is part of a boot-monitor package developed as a generic base
* platform for embedded system designs. As such, it is likely to be
* distributed to various projects beyond the control of the original
* author. Please notify the author of any enhancements made or bugs found
* so that all may benefit from the changes. In addition, notification back
* to the author will allow the new user to pick up changes that may have
* been made by other users after this version of the code was distributed.
*
* Note1: the majority of this code was edited with 4-space tabs.
* Note2: as more and more contributions are accepted, the term "author"
* is becoming a mis-representation of credit.
*
* Original author: Ed Sutter
* Email: esutter@lucent.com
* Phone: 908-582-2351
*/
#include "config.h"
#if INCLUDE_TFTP
#include "endian.h"
#include "genlib.h"
#include "cpuio.h"
#include "ether.h"
#include "tfs.h"
#include "tfsprivate.h"
#include "monflags.h"
#include "stddefs.h"
#include "flash.h"
#include "cli.h"
#include "timer.h"
#define MODE_NULL 0
#define MODE_NETASCII 1
#define MODE_OCTET 2
void ShowTftpStats();
int SendTFTPData(struct ether_header *,ushort,uchar *,int);
int SendTFTPErr(struct ether_header *,short,char *,int);
int SendTFTPAck(struct ether_header *,ushort);
int SendTFTPRRQ(uchar *,uchar *,char *,char *,uchar *);
static struct elapsed_tmr tftpTmr;
static ushort tftpPrevBlock; /* Keeps the value of the block number of
* the previous TFTP transaction.
*/
static int TftpWrqMode; /* Set to MODE_NETASCII, MODE_OCTET or
* MODE_NULL based on the incoming WRQ
* request
*/
static int TftpChopCount; /* Number of characters chopped from the
* incoming file because of NETASCII
* conversion (remove 0x0d).
*/
static int TftpLastPktSize;
static uchar TftpLastPkt[TFTP_PKTOVERHEAD+TFTP_DATAMAX+4];
/* Storage of last packet sent. This is
* used if it is determined that the packet
* most recently sent must be sent again.
*/
static long TftpRetryTimeout; /* Count used during a TFTP transfer to
* kick off a retransmission of a packet.
*/
static ushort TftpRmtPort; /* Remote port of tftp transfer */
static uchar *TftpAddr; /* Current destination address used for tftp
* file transfer into local memory.
*/
static char *TftpLTMptr; /* List-to-mem pointer used when filename
* for RRQ is ".".
*/
static int TftpCount; /* Running total number of bytes transferred
* for a particular tftp transaction.
*/
static int TftpState; /* Used to keep track of the current state
* of a tftp transaction.
*/
static int TftpTurnedOff; /* If non-zero, then tftp is disabled. */
static char TftpErrString[32]; /* Used to post a tftp error message. */
static char TftpTfsFname[TFSNAMESIZE+64]; /* Store name of WRQ destination
* file (plus flags & info).
*/
/* ls_to_mem():
* Turn the list of files into a string buffer that contains
* each filename followed by a newline. Each filename is the "full"
* name, meaning that it will contain commas to delimit the flags and
* info fields of the file.
* Return a pointer to the allocated space.
*/
char *
ls_to_mem(void)
{
#if INCLUDE_TFS
int size;
TFILE *tfp;
char *base, *bp, *info, *flags, fbuf[16];
/* First determine how much memory will be needed to store
* the list of files... Each filename in the list will consist
* of the string formatted as "name,flags,info\n"...
*/
size = 0;
tfp = (TFILE *)0;
while((tfp = tfsnext(tfp))) {
/* Increment size to include filename, 2 commas and a newline...
*/
size += (strlen(TFS_NAME(tfp)) + 3);
/* Determine if flags and/or info field is to be present in
* the name, and add that to the size appropriately...
*/
flags = tfsflagsbtoa(TFS_FLAGS(tfp),fbuf);
if (!fbuf[0])
flags = 0;
if (flags)
size += strlen(flags);
if (TFS_INFO(tfp))
size += strlen(TFS_INFO(tfp));
}
if (size == 0)
return(0);
/* Allocate the space. Add one additional byte to the size to
* account for the NULL termination at the end of the string...
*/
base = bp = malloc(size+1);
if (bp == (char *)0)
return(0);
/* Load the buffer with the list of names with their
* TFS extensions...
*/
tfp = (TFILE *)0;
while((tfp = tfsnext(tfp))) {
flags = tfsflagsbtoa(TFS_FLAGS(tfp),fbuf);
if (!flags || !fbuf[0])
flags = "";
if (TFS_INFO(tfp))
info = TFS_INFO(tfp);
else
info = "";
bp += sprintf(bp,"%s,%s,%s\n",TFS_NAME(tfp),flags,info);
}
return(base);
#else
return(0);
#endif
}
char *
tftpStringState(int state)
{
switch(state) {
case TFTPOFF:
return("OFF");
case TFTPIDLE:
return("IDLE");
case TFTPACTIVE:
return("ACTIVE");
case TFTPERROR:
return("ERROR");
case TFTPSENTRRQ:
return("SENTRRQ");
case TFTPTIMEOUT:
return("TIMEOUT");
default:
return("???");
}
}
int
tftpGotoState(int state)
{
int ostate;
ostate = TftpState;
TftpState = state;
if ((EtherVerbose & SHOW_TFTP_STATE) && (state != ostate))
printf(" TFTP State change %s -> %s\n",
tftpStringState(ostate), tftpStringState(state));
return(ostate);
}
/* tftpGet():
* Return size of file if successful; else 0.
*/
int
tftpGet(ulong addr,char *tftpsrvr,char *mode, char *hostfile,char *tfsfile,
char *tfsflags,char *tfsinfo)
{
int done;
char buf[32];
uchar binip[8], binenet[8], *enetaddr;
setenv("TFTPGET",0);
/* Convert IP address to binary: */
if (IpToBin(tftpsrvr,binip) < 0)
return(0);
/* Get the ethernet address for the IP: */
/* Give ARP the same verbosity (if any) set up for TFTP: */
if (EtherVerbose & SHOW_TFTP_STATE)
EtherVerbose |= SHOW_ARP;
enetaddr = ArpEther(binip,binenet,0);
EtherVerbose &= ~SHOW_ARP;
if (!enetaddr) {
printf("ARP failed for %s\n",tftpsrvr);
return(0);
}
printf("Retrieving %s from %s...\n",hostfile,tftpsrvr);
/* Send the TFTP RRQ to initiate the transfer. */
if (SendTFTPRRQ(binip,binenet,hostfile,mode,(uchar *)addr) < 0) {
printf("RRQ failed\n");
return(0);
}
/* Wait for TftpState to indicate that the transaction has completed... */
done = 0;
while(!done) {
pollethernet();
switch(TftpState) {
case TFTPIDLE:
//stone debug
//printf("Rcvd %d bytes",TftpCount);
done = 1;
break;
case TFTPERROR:
printf("Host error: %s\n",TftpErrString);
done = 2;
break;
case TFTPTIMEOUT:
printf("Timing out (%d bytes rcvd)\n",TftpCount);
done = 2;
break;
default:
break;
}
}
if (done == 2)
return(0);
/* stone debug ,dont use tfs .
if (tfsfile) {
int err, filesize;
filesize = TftpCount - TftpChopCount;
printf("\nAdding %s (size=%d) to TFS...",tfsfile,filesize);
err = tfsadd(tfsfile,tfsinfo,tfsflags,(uchar *)addr,filesize);
if (err != TFS_OKAY)
printf("%s: %s\n",tfsfile,(char *)tfsctrl(TFS_ERRMSG,err,0));
}
*/
sprintf(buf,"%d",TftpCount);
setenv("TFTPGET",buf);
printf("Rcvd %d bytes\n",TftpCount);
return(TftpCount);
}
/* tftpInit():
* Called by the ethenet initialization to initialize state variables.
*/
void
tftpInit()
{
TftpCount = -1;
TftpRmtPort = 0;
TftpTurnedOff = 0;
tftpGotoState(TFTPIDLE);
TftpAddr = (uchar *)0;
}
/* storePktAndSend():
* The final stage in sending a TFTP packet...
* 1. Compute IP and UDP checksums;
* 2. Copy ethernet packet to a buffer so that it can be resent
* if necessary (by tftpStateCheck()).
* 3. Store the size of the packet;
* 4. Send the packet out the interface.
* 5. Reset the timeout count and re-transmission delay variables.
*/
void
storePktAndSend(struct ip *ipp, struct ether_header *epkt,int size)
{
ipChksum(ipp); /* Compute csum of ip hdr */
udpChksum(ipp); /* Compute UDP checksum */
/* Copy packet to static buffer */
memcpy((char *)TftpLastPkt,(char *)epkt,size);
TftpLastPktSize = size; /* Copy size to static location */
sendBuffer(size); /* Send buffer out ethernet i*/
/* Re-initialize the re-transmission delay variables.
*/
TftpRetryTimeout = RetransmitDelay(DELAY_INIT_TFTP);
startElapsedTimer(&tftpTmr,TftpRetryTimeout * 1000);
}
/* getTftpSrcPort():
* Each time a TFTP RRQ goes out, use a new source port number.
* Cycle through a block of 256 port numbers...
*/
ushort
getTftpSrcPort(void)
{
if (TftpSrcPort < (IPPORT_TFTPSRC+256))
TftpSrcPort++;
else
TftpSrcPort = IPPORT_TFTPSRC;
return(TftpSrcPort);
}
/* tftpStateCheck():
* Called by the pollethernet function to support the ability to retry
* on a TFTP transmission that appears to have terminated prematurely
* due to a lost packet. If a packet is sent and the response is not
* received within about 1-2 seconds, the packet is re-sent. The retry
* will repeat 8 times; then give up and set the TFTP state to idle.
*
* Taken from RFC 1350 section 2 "Overview of the Protocol"...
* ... If a packet gets lost in the network, the intended recipient will
* timeout and may retransmit his last packet (which may be data or an
* acknowledgement), thus causing the sender of the lost packet to retransmit
* the lost packet.
*
* Taken from RFC 1123 section 4.2.3.2 "Timeout Algorithms"...
* ... a TFTP implementation MUST use an adaptive timeout ...
*/
void
tftpStateCheck(void)
{
uchar *buf;
long delay;
ushort tftp_opcode;
struct ip *ihdr;
struct Udphdr *uhdr;
struct ether_header *ehdr;
switch(TftpState) {
case TFTPIDLE:
case TFTPTIMEOUT:
case TFTPERROR:
return;
default:
break;
}
/* If timeout occurs, re-transmit the packet...
*/
if (!msecElapsed(&tftpTmr))
return;
delay = RetransmitDelay(DELAY_INCREMENT);
if (delay == RETRANSMISSION_TIMEOUT) {
if (EtherVerbose & SHOW_TFTP_STATE)
printf(" TFTP_RETRY giving up\n");
tftpGotoState(TFTPTIMEOUT);
enableBroadcastReception();
return;
}
/* Get a transmit buffer and copy the packet that was last sent.
* Insert a new IP ID, recalculate the checksums and send it again...
* If the opcode of the packet to be re-transmitted is RRQ, then
* use a new port number.
*/
buf = (uchar *)getXmitBuffer();
memcpy((char *)buf,(char *)TftpLastPkt,TftpLastPktSize);
ehdr = (struct ether_header *)buf;
ihdr = (struct ip *)(ehdr + 1);
uhdr = (struct Udphdr *)(ihdr + 1);
tftp_opcode = *(ushort *)(uhdr + 1);
ihdr->ip_id = ipId();
if (tftp_opcode == ecs(TFTP_RRQ)) {
uhdr->uh_sport = getTftpSrcPort();
self_ecs(uhdr->uh_sport);
}
ipChksum(ihdr);
udpChksum(ihdr);
sendBuffer(TftpLastPktSize);
if (EtherVerbose & SHOW_TFTP_STATE)
printf(" TFTP_RETRY (%ld secs)\n",TftpRetryTimeout);
TftpRetryTimeout = delay;
startElapsedTimer(&tftpTmr,TftpRetryTimeout * 1000);
return;
}
/* tftpStartSrvrFilter():
* Called when either a TFTP_RRQ or TFTP_WRQ is received indicating that
* a client wants to start up a transfer.
* If TftpState is IDLE, ERROR or TIMEOUT, this means it is safe to start
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -