466 lines
14 KiB
C
466 lines
14 KiB
C
/*
|
|
* This file impliments TFTP server with the Distress Beacon UDP packet, that will be send
|
|
* as a notification for a system recovery process.
|
|
*/
|
|
|
|
#include "rcvr.h"
|
|
|
|
/* #define DEBUG_RCVR */
|
|
|
|
#ifdef DEBUG_RCVR
|
|
#define debug_rcvr(fmt,args...) printf (fmt ,##args)
|
|
#else
|
|
#define debug_rcvr(fmt,args...)
|
|
#endif
|
|
|
|
#if (CONFIG_COMMANDS & CFG_CMD_RCVR)
|
|
|
|
/* Globals */
|
|
rcvr_state_t rcvr_state = RCVR_INIT;
|
|
ulong myTID = 0; /* Transfer ID used by me as a TFPT server */
|
|
ulong peerTID = 0; /* Transfer ID received from the peer */
|
|
ulong packetNum = 0; /* Packet number expected */
|
|
uchar * imagePtr = NULL; /* Pointer to the location to copy the uImage file */
|
|
|
|
static void
|
|
BeaconSend (void)
|
|
{
|
|
DistressBeaconPacketStruct * bconpkt;
|
|
char *s;
|
|
|
|
bconpkt = (DistressBeaconPacketStruct*) (NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE);
|
|
|
|
/* clear the whole packet to zeros */
|
|
memset(bconpkt, 0x0, sizeof(bconpkt));
|
|
|
|
/* Fill the packet with the information needed */
|
|
bconpkt->Header.PacketType = RECOVERY_MODE;
|
|
bconpkt->Header.Version = BEACON_VERSION;
|
|
bconpkt->Payload.State = (((RECOVERY_STATE_FIRST & 0xFF) << 8) | ((RECOVERY_STATE_FIRST & 0xFF00) >> 8));
|
|
NetCopyIP(&bconpkt->Payload.IPAddr.IPAddress, &NetOurIP);
|
|
memcpy (bconpkt->Payload.LinkAddr.LinkLevelAddr, NetOurEther, LL_ADDR_LEN);
|
|
|
|
if ((s = getenv("deviceName")) != NULL)
|
|
{
|
|
ulong strln = strlen(s);
|
|
if (strln >= MAX_NAME_LEN)
|
|
strncpy(bconpkt->Payload.Name, s, MAX_NAME_LEN-1); /* keep space for null termination */
|
|
else
|
|
strcpy(bconpkt->Payload.Name, s);
|
|
}
|
|
else
|
|
{
|
|
printf("Warning: Missing environment variable \"deviceName\". Assuming default!\n");
|
|
strcpy(bconpkt->Payload.Name, "Unknown S/N");
|
|
}
|
|
|
|
if ((s = getenv("modelNumber")) != NULL)
|
|
{
|
|
ulong strln = strlen(s);
|
|
if (strln >= MAX_MODEL_NUMBER_LEN)
|
|
strncpy(bconpkt->Payload.ModelNumber, s, MAX_MODEL_NUMBER_LEN-1); /* keep space for null termination */
|
|
else
|
|
strcpy(bconpkt->Payload.ModelNumber, s);
|
|
}
|
|
else
|
|
{
|
|
printf("Warning: Missing environment variable \"modelNumber\". Assuming default!\n");
|
|
strcpy(bconpkt->Payload.ModelNumber, "Unknown Model # of NAS system");
|
|
}
|
|
|
|
printf("Broadcasting Distress Beacon Packet.\n");
|
|
debug_rcvr("Sending Beacon packet with IP = 0x%08x and MAC = %02x:%02x:%02x:%02x:%02x:%02x\n", bconpkt->Payload.IPAddr.IPAddress,
|
|
bconpkt->Payload.LinkAddr.LinkLevelAddr[0], bconpkt->Payload.LinkAddr.LinkLevelAddr[1], bconpkt->Payload.LinkAddr.LinkLevelAddr[2],
|
|
bconpkt->Payload.LinkAddr.LinkLevelAddr[3], bconpkt->Payload.LinkAddr.LinkLevelAddr[4], bconpkt->Payload.LinkAddr.LinkLevelAddr[5]);
|
|
|
|
NetSendUDPPacket(NetServerEther, 0, BEACON_UDP_PORT, BEACON_UDP_PORT, sizeof(DistressBeaconPacketStruct));
|
|
}
|
|
|
|
|
|
/*
|
|
* Send an Error Reply.
|
|
*/
|
|
static void
|
|
SendTftpError(ushort error, unsigned src, unsigned dst, uchar * errStr)
|
|
{
|
|
ushort * fld;
|
|
uchar *s;
|
|
|
|
/* write first the opcode */
|
|
fld = (ushort*) (NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE);
|
|
*fld = (((TFTP_OPCODE_ERR & 0xFF) << 8) | ((TFTP_OPCODE_ERR & 0xFF00) >> 8));
|
|
|
|
/* Write the error code */
|
|
fld++;
|
|
*fld = (((error & 0xFF) << 8) | ((error & 0xFF00) >> 8));
|
|
|
|
/* Add a string to explain */
|
|
s = (uchar*) ++fld;
|
|
sprintf(s, "%s", errStr);
|
|
|
|
/* Transmit the error message */
|
|
debug_rcvr("Sendin ERR packet (PeerTID = %d, MyTID = %d).\n", src, dst);
|
|
NetSendUDPPacket(NetServerEther, NetServerIP, src, dst, (strlen(s) + 5));
|
|
}
|
|
|
|
/*
|
|
* Send an Error Reply.
|
|
*/
|
|
static void
|
|
SendTftpAck(ushort block)
|
|
{
|
|
ushort * fld;
|
|
|
|
/* write first the opcode */
|
|
fld = (ushort*) (NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE);
|
|
*fld = (((TFTP_OPCODE_ACK & 0xFF) << 8) | ((TFTP_OPCODE_ACK & 0xFF00) >> 8));
|
|
|
|
/* Write the error code */
|
|
fld++;
|
|
*fld = (((block & 0xFF) << 8) | ((block & 0xFF00) >> 8));
|
|
|
|
/* Transmit the error message */
|
|
debug_rcvr("Sendin ACK packet (PeerTID = %d, MyTID = %d, Block = %d).\n", peerTID, myTID, block);
|
|
NetSendUDPPacket(NetServerEther, NetServerIP, peerTID, myTID, 4);
|
|
}
|
|
|
|
|
|
/*
|
|
* Timeout on Distress Beacon Recovery request.
|
|
*/
|
|
static void
|
|
RecoverTimeout(void)
|
|
{
|
|
switch (rcvr_state)
|
|
{
|
|
/* 5 seconds between Distress Beacon */
|
|
case RCVR_WAIT_4_CNCT:
|
|
NetSetTimeout (RCVR_BEACON_TIMEOUT * CFG_HZ, RecoverTimeout);
|
|
BeaconSend();
|
|
break;
|
|
|
|
/* Timeout between data packets or between uImage and RamDisk files */
|
|
case RCVR_IMAGE_DWNLD:
|
|
debug_rcvr("Timeout, Failing the recovery process!\n");
|
|
SendTftpError(TFTP_ERROR_UNDEFINED, peerTID, myTID, "Data Packet TimeOut!");
|
|
NetSetTimeout(0, (thand_f *)0);
|
|
NetState = NETLOOP_FAIL;
|
|
rcvr_state = RCVR_INIT;
|
|
break;
|
|
|
|
/* Client finished successfully no retransmit requested */
|
|
case RCVR_FINISHED:
|
|
debug_rcvr("Finished successfully.\n");
|
|
NetSetTimeout(0, (thand_f *)0);
|
|
NetState = NETLOOP_SUCCESS;
|
|
rcvr_state = RCVR_INIT;
|
|
break;
|
|
|
|
default:
|
|
debug_rcvr("Invalid state received in the Timeout routine~\n");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle Recovery received packets.
|
|
*/
|
|
static void
|
|
RecoveryHandler(uchar * pkt, unsigned dest, unsigned src, unsigned len)
|
|
{
|
|
ushort opcode;
|
|
ushort * fld;
|
|
uchar * tftpfile = NULL;
|
|
uchar * tftpmode = NULL;
|
|
ushort rxPacketNumber;
|
|
unsigned dlen;
|
|
|
|
fld = (ushort*) pkt;
|
|
opcode = (((*fld & 0xFF) << 8) | ((*fld & 0xFF00) >> 8));
|
|
|
|
switch (rcvr_state)
|
|
{
|
|
case RCVR_INIT:
|
|
break;
|
|
|
|
case RCVR_WAIT_4_CNCT:
|
|
switch (opcode)
|
|
{
|
|
case TFTP_OPCODE_RRQ:
|
|
/* check that the destination port is correct */
|
|
if (dest != TFTP_SERVER_PORT)
|
|
return;
|
|
|
|
/* Copy the server IP and MAC address */
|
|
memcpy (NetServerEther, (uchar*) &NetRxPkt[6], 6);
|
|
NetServerIP = NetRxPkt[26];
|
|
NetServerIP |= (NetRxPkt[27] << 8);
|
|
NetServerIP |= (NetRxPkt[28] << 16);
|
|
NetServerIP |= (NetRxPkt[29] << 24);
|
|
debug_rcvr("New Peer Info (MAC %02x:%02x:%02x:%02x:%02x:%02x, IP %08x\n", NetServerEther[0], NetServerEther[1],
|
|
NetServerEther[2], NetServerEther[3], NetServerEther[4], NetServerEther[5], NetServerIP);
|
|
|
|
printf("Unsupported TFTP GET request received from %d.%d.%d.%d (MAC %02x:%02x:%02x:%02x:%02x:%02x)\n",
|
|
(NetServerIP & 0xFF), ((NetServerIP >> 8) & 0xFF), ((NetServerIP >> 16) & 0xFF), ((NetServerIP >> 24) & 0xFF),
|
|
NetServerEther[0], NetServerEther[1], NetServerEther[2], NetServerEther[3], NetServerEther[4],
|
|
NetServerEther[5]);
|
|
|
|
SendTftpError(TFTP_ERROR_ILLEGAL_OPERATION, src, TFTP_SERVER_PORT, "Request Not Supported");
|
|
break;
|
|
|
|
case TFTP_OPCODE_WRQ:
|
|
/* check that the destination port is correct */
|
|
if (dest != TFTP_SERVER_PORT)
|
|
return;
|
|
|
|
/* Copy the server IP and MAC address */
|
|
memcpy (NetServerEther, (uchar*) &NetRxPkt[6], 6);
|
|
NetServerIP = NetRxPkt[26];
|
|
NetServerIP |= (NetRxPkt[27] << 8);
|
|
NetServerIP |= (NetRxPkt[28] << 16);
|
|
NetServerIP |= (NetRxPkt[29] << 24);
|
|
debug_rcvr("New Peer Info (MAC %02x:%02x:%02x:%02x:%02x:%02x, IP %08x\n", NetServerEther[0], NetServerEther[1],
|
|
NetServerEther[2], NetServerEther[3], NetServerEther[4], NetServerEther[5], NetServerIP);
|
|
|
|
printf("New TFTP PUT request received from %d.%d.%d.%d (MAC %02x:%02x:%02x:%02x:%02x:%02x)\n",
|
|
(NetServerIP & 0xFF), ((NetServerIP >> 8) & 0xFF), ((NetServerIP >> 16) & 0xFF), ((NetServerIP >> 24) & 0xFF),
|
|
NetServerEther[0], NetServerEther[1], NetServerEther[2], NetServerEther[3], NetServerEther[4],
|
|
NetServerEther[5]);
|
|
|
|
tftpfile = pkt +2;
|
|
tftpmode = pkt + 3 + strlen (tftpfile);
|
|
debug_rcvr("TFTP WRQ received (Dest = %d, Src = %d, Len = %d, Opcode = %d, File = %s, Mode = %s, PeerTID = %d\n",
|
|
dest, src, len, opcode, tftpfile, tftpmode, src);
|
|
|
|
/* save the peer port # as the peerTID */
|
|
peerTID = src;
|
|
packetNum = 1; /* reset the packet numbering */
|
|
debug_rcvr("Saving the PeerTID to used throughout the session (PeerTID = %d).\n", peerTID);
|
|
|
|
/* received the request successfully */
|
|
rcvr_state = RCVR_IMAGE_DWNLD;
|
|
NetSetTimeout (RCVR_DATA_TIMEOUT * CFG_HZ, RecoverTimeout);
|
|
|
|
/* Send the ACK */
|
|
SendTftpAck(0);
|
|
break;
|
|
|
|
case TFTP_OPCODE_DATA:
|
|
case TFTP_OPCODE_ACK:
|
|
case TFTP_OPCODE_ERR:
|
|
debug_rcvr("ERROR: Invalid TFTP request while in WAIT_4_CNCT (opcode = %d)!\n",opcode);
|
|
break;
|
|
|
|
default:
|
|
debug_rcvr("ERROR: Invalid TFTP opcode!\n");
|
|
}
|
|
break;
|
|
|
|
case RCVR_IMAGE_DWNLD:
|
|
switch (opcode)
|
|
{
|
|
case TFTP_OPCODE_RRQ:
|
|
debug_rcvr("TFTP GET requestes are not supported. Sendin Error!\n");
|
|
break;
|
|
|
|
case TFTP_OPCODE_WRQ:
|
|
/* check if the WRQ ACK was not received so we are having it again */
|
|
if (packetNum == 1) /* we did not yeat receive any DATA packet */
|
|
{
|
|
/* check that the destination port is correct */
|
|
if (dest != TFTP_SERVER_PORT)
|
|
return;
|
|
|
|
/* Copy the server IP and MAC address */
|
|
memcpy (NetServerEther, (uchar*) &NetRxPkt[6], 6);
|
|
NetServerIP = NetRxPkt[26];
|
|
NetServerIP |= (NetRxPkt[27] << 8);
|
|
NetServerIP |= (NetRxPkt[28] << 16);
|
|
NetServerIP |= (NetRxPkt[29] << 24);
|
|
debug_rcvr("New Peer Info (MAC %02x:%02x:%02x:%02x:%02x:%02x, IP %08x\n", NetServerEther[0], NetServerEther[1],
|
|
NetServerEther[2], NetServerEther[3], NetServerEther[4], NetServerEther[5], NetServerIP);
|
|
|
|
printf("TFTP PUT request received again from %d.%d.%d.%d (MAC %02x:%02x:%02x:%02x:%02x:%02x)\n",
|
|
(NetServerIP & 0xFF), ((NetServerIP >> 8) & 0xFF), ((NetServerIP >> 16) & 0xFF), ((NetServerIP >> 24) & 0xFF),
|
|
NetServerEther[0], NetServerEther[1], NetServerEther[2], NetServerEther[3], NetServerEther[4],
|
|
NetServerEther[5]);
|
|
|
|
tftpfile = pkt +2;
|
|
tftpmode = pkt + 3 + strlen (tftpfile);
|
|
debug_rcvr("TFTP WRQ received again (Dest = %d, Src = %d, Len = %d, Opcode = %d, File = %s, Mode = %s, PeerTID = %d\n",
|
|
dest, src, len, opcode, tftpfile, tftpmode, src);
|
|
|
|
/* save the peer port # as the peerTID */
|
|
peerTID = src;
|
|
debug_rcvr("Saving the PeerTID to used throughout the session (PeerTID = %d).\n", peerTID);
|
|
|
|
/* Send the ACK */
|
|
SendTftpAck(0);
|
|
}
|
|
else
|
|
debug_rcvr("ERROR: Invalid WRQ request while data transfer!\n");
|
|
|
|
break;
|
|
|
|
case TFTP_OPCODE_DATA:
|
|
|
|
/* check that the destination port is correct */
|
|
if (dest != myTID)
|
|
{
|
|
debug_rcvr("ERROR: TFTP data packet not to my TID port (port = %d)!\n", dest);
|
|
return;
|
|
}
|
|
|
|
fld = (ushort*) (pkt+2);
|
|
rxPacketNumber = (((*fld & 0xFF) << 8) | ((*fld & 0xFF00) >> 8));
|
|
if (rxPacketNumber == (ushort)(packetNum & 0xffff))
|
|
{
|
|
/* check the length of data */
|
|
if (len == (TFTP_MAX_DATA_LEN + 4))
|
|
{
|
|
/* print a progress message */
|
|
if ((packetNum % 500) == 0)
|
|
printf("%dKB\r", (packetNum / 2));
|
|
|
|
dlen = TFTP_MAX_DATA_LEN;
|
|
NetSetTimeout (RCVR_DATA_TIMEOUT * CFG_HZ, RecoverTimeout);
|
|
}
|
|
else if (len < (TFTP_MAX_DATA_LEN + 4))
|
|
{
|
|
dlen = (len - 4);
|
|
rcvr_state = RCVR_FINISHED;
|
|
NetSetTimeout (RCVR_FINISH_TIMEOUT * CFG_HZ, RecoverTimeout);
|
|
//debug_rcvr("Received the last packet in the uImage file, changing state to WAIT_4_RAMDISK.\n");
|
|
printf("Recovery Image received (%d bytes).\n",((packetNum * TFTP_MAX_DATA_LEN) + dlen));
|
|
}
|
|
else /* Fatal Error */
|
|
{
|
|
debug_rcvr("ERROR: TFTP data packet larger that 512!\n");
|
|
NetSetTimeout(0, (thand_f *)0);
|
|
NetState = NETLOOP_FAIL;
|
|
rcvr_state = RCVR_INIT;
|
|
return;
|
|
}
|
|
|
|
debug_rcvr("Received uImage Packet #%d (length = %d). Sending Ack.\n", packetNum, dlen);
|
|
|
|
/* copy the data to the RAM */
|
|
memcpy(imagePtr, (pkt+4), dlen);
|
|
imagePtr += dlen;
|
|
NetBootFileXferSize += dlen;
|
|
|
|
/* Send the Ack for the new packet */
|
|
SendTftpAck(packetNum);
|
|
|
|
/* increment the packet number */
|
|
++packetNum;
|
|
}
|
|
else if (rxPacketNumber == ((ushort)(packetNum & 0xffff)-1))
|
|
{
|
|
debug_rcvr("Received Packet #%d AGAIN. Sending Ack.\n");
|
|
|
|
/* Seems that my last ACK was not delivered SO Send the Ack again */
|
|
SendTftpAck(rxPacketNumber);
|
|
}
|
|
else
|
|
debug_rcvr("Invalid Packet #%d received (expecting %d)!\n", rxPacketNumber, packetNum);
|
|
|
|
break;
|
|
|
|
case TFTP_OPCODE_ACK:
|
|
case TFTP_OPCODE_ERR:
|
|
debug_rcvr("ERROR: Invalid TFTP request while in IMAGE_DWNLD (opcode = %d)!\n",opcode);
|
|
break;
|
|
|
|
default:
|
|
debug_rcvr("ERROR: Invalid TFTP opcode!\n");
|
|
}
|
|
break;
|
|
|
|
case RCVR_FINISHED:
|
|
switch (opcode)
|
|
{
|
|
case TFTP_OPCODE_DATA:
|
|
/* check that the destination port is correct */
|
|
if (dest != myTID)
|
|
{
|
|
debug_rcvr("ERROR: TFTP data packet not to my TID port (port = %d)!\n", dest);
|
|
return;
|
|
}
|
|
|
|
fld = (ushort*) (pkt+2);
|
|
rxPacketNumber = (((*fld & 0xFF) << 8) | ((*fld & 0xFF00) >> 8));
|
|
|
|
/* check if the last ACK was not received; so retransmit it */
|
|
if (rxPacketNumber == ((ushort)(packetNum & 0xffff)-1))
|
|
{
|
|
debug_rcvr("Received Packet #%d AGAIN. Sending Ack.\n");
|
|
|
|
/* Seems that my last ACK was not delivered SO Send the Ack again */
|
|
SendTftpAck(rxPacketNumber);
|
|
}
|
|
else
|
|
debug_rcvr("Invalid Packet #%d deceived (expecting %d)!\n", rxPacketNumber, packetNum);
|
|
|
|
break;
|
|
|
|
case TFTP_OPCODE_RRQ:
|
|
case TFTP_OPCODE_WRQ:
|
|
case TFTP_OPCODE_ACK:
|
|
case TFTP_OPCODE_ERR:
|
|
debug_rcvr("ERROR: Invalid TFTP request while in RCVR_FINISHED (opcode = %d)!\n",opcode);
|
|
break;
|
|
|
|
default:
|
|
debug_rcvr("ERROR: Invalid TFTP opcode!\n");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
debug_rcvr("ERROR: Invalid Recovery status!\n");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Start a recovery process - Using Distress Beacon and TFTP server
|
|
*/
|
|
void RecoverRequest(void)
|
|
{
|
|
uchar * s;
|
|
|
|
/* get the uImage locations */
|
|
if ((s = getenv("loadaddr")) != NULL)
|
|
{
|
|
imagePtr = (uchar *)simple_strtoul(s, NULL, 16);
|
|
printf("uImage load address 0x%08x\n", imagePtr);
|
|
}
|
|
else
|
|
{
|
|
printf("ERROR: Missing environment variable for \"loadaddr\"!\n");
|
|
NetState = NETLOOP_FAIL;
|
|
return;
|
|
}
|
|
|
|
/* Caculate the TID to be used */
|
|
myTID = ((NetOurEther[4] << 8) | (NetOurEther[5]));
|
|
peerTID = 0; /* reset the peer TID */
|
|
|
|
/* Change the state for waiting to connect */
|
|
rcvr_state = RCVR_WAIT_4_CNCT;
|
|
|
|
/* Set the handler to the TFTP server */
|
|
NetSetHandler(RecoveryHandler);
|
|
|
|
/* Set the Timeout */
|
|
NetSetTimeout(RCVR_BEACON_TIMEOUT * CFG_HZ, RecoverTimeout);
|
|
|
|
/* Transmit the First Distress Beacon packet */
|
|
BeaconSend();
|
|
}
|
|
|
|
#endif /* (CONFIG_COMMANDS & CFG_CMD_RCVR) */
|
|
|