📄 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 "cli.h"#define MODE_NULL 0#define MODE_NETASCII 1#define MODE_OCTET 2void 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 ushort tftpLastblock; /* Keeps the value of the block number of * the last TFTP transaction. */static int tftpRetry_delayloops;/* Number of times tftpStateCheck() must be * called to reach tftpRetry_delaysecs. */static int tftpRetry_delaysecs; /* Number of seconds to wait prior to retry. */static int tftpRetry_lastchance;/* Set if we are on the last retry and are * about to give up. */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. * It is cleared each time a valid packet * is sent, and incremented by the * tftpStateCheck() function. */static ushort TftpRmtPort; /* Remote port of tftp transfer */static uchar *TftpAddr; /* Current destination address used for tftp * file transfer into local memory. */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). */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("???"); }}inttftpGotoState(int state){ int ostate; ostate = TftpState; TftpState = state; if ((EtherVerbose & SHOW_TFTP) && (state != ostate)) printf(" TFTP State change %s -> %s\n", tftpStringState(ostate), tftpStringState(state)); return(ostate);}/* tftpGet(): * Return size of file if successful; else 0. */inttftpGet(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) EtherVerbose |= SHOW_ARP; enetaddr = ArpEther(binip,binenet,0); EtherVerbose &= ~SHOW_ARP; if (!enetaddr) { printf("ARP failed for %s\n",tftpsrvr); return(0); } /* Initialize the retransmission delay calculator: */ RetransmitDelay(DELAY_INIT_TFTP); printf("Retrieving %s from %s...",hostfile,tftpsrvr); if (EtherVerbose & SHOW_TFTP) printf("\n"); /* 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: printf(" %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); 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)); } printf("\n"); sprintf(buf,"%d",TftpCount); setenv("TFTPGET",buf); return(TftpCount);}/* tftpInit(): * Called by the ethenet initialization to initialize state variables. */voidtftpInit(){ 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. */voidstorePktAndSend(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*/ /* Clear the retry timeout counters and re-initialize the */ /* re-transmission delay variables. */ TftpRetryTimeout = 0; tftpRetry_lastchance = 0; tftpRetry_delayloops = 0; RetransmitDelay(DELAY_INIT_TFTP);}/* getTftpSrcPort(): * Each time a TFTP RRQ goes out, use a new source port number. * Cycle through a block of 256 port numbers... */ushortgetTftpSrcPort(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 ... */voidtftpStateCheck(void){ uchar *buf; ushort tftp_opcode; struct ip *ihdr; struct Udphdr *uhdr; struct ether_header *ehdr; switch(TftpState) { case TFTPIDLE: case TFTPTIMEOUT: case TFTPERROR: tftpRetry_lastchance = tftpRetry_delayloops = 0; return; default: break; } if (!tftpRetry_delayloops) { tftpRetry_delaysecs = RetransmitDelay(DELAY_OR_TIMEOUT_RETURN); tftpRetry_delayloops = tftpRetry_delaysecs * LoopsPerSecond; } /* If the value in TftpRetryTimeout reaches the tftpRetry_delayloops */ /* value, then assume it is time to re-transmit a packet... */ if (++TftpRetryTimeout < tftpRetry_delayloops) return; if (tftpRetry_lastchance) { if (EtherVerbose & SHOW_TFTP) printf(" TFTP_RETRY giving up\n"); tftpGotoState(TFTPTIMEOUT); enableBroadcastReception(); TftpRetryTimeout = 0; 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) printf(" TFTP_RETRY (%d secs)\n",tftpRetry_delaysecs); tftpRetry_delayloops = 0; if (RetransmitDelay(DELAY_INCREMENT) == RETRANSMISSION_TIMEOUT) tftpRetry_lastchance = 1; TftpRetryTimeout = 0; return;}/* tftpStartSrvrFilter(): * Called when either a TFTP_RRQ or TFTP_WRQ is received indicating that
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -