Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=886
This patch is to support the TFTP windowsize option described in RFC 7440.
The feature allows the client and server to negotiate a window size of
consecutive blocks to send as an alternative for replacing the single-block
lockstep schema.
Currently, the windowsize for write request operation is not supported since
there is no real use cases.
Cc: Ye Ting <ting.ye@intel.com>
Cc: Fu Siyuan <siyuan.fu@intel.com>
Cc: Shao Ming <ming.shao@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Wu Jiaxin <jiaxin.wu@intel.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Fu Siyuan <siyuan.fu@intel.com>
Reviewed-by: Ye Ting <ting.ye@intel.com>
Instance->Operation = 0;\r
\r
Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;\r
Instance->Operation = 0;\r
\r
Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;\r
+ Instance->WindowSize = 1;\r
+ Instance->TotalBlock = 0;\r
+ Instance->AckedBlock = 0;\r
Instance->LastBlock = 0;\r
Instance->ServerIp = 0;\r
Instance->ListeningPort = 0;\r
Instance->LastBlock = 0;\r
Instance->ServerIp = 0;\r
Instance->ListeningPort = 0;\r
Token->OptionList,\r
Token->OptionCount,\r
TRUE,\r
Token->OptionList,\r
Token->OptionCount,\r
TRUE,\r
&Instance->RequestOption\r
);\r
\r
&Instance->RequestOption\r
);\r
\r
Config = &Instance->Config;\r
Instance->Token = Token;\r
Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;\r
Config = &Instance->Config;\r
Instance->Token = Token;\r
Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;\r
+ Instance->WindowSize = MTFTP4_DEFAULT_WINDOWSIZE;\r
\r
CopyMem (&Instance->ServerIp, &Config->ServerIp, sizeof (IP4_ADDR));\r
Instance->ServerIp = NTOHL (Instance->ServerIp);\r
\r
CopyMem (&Instance->ServerIp, &Config->ServerIp, sizeof (IP4_ADDR));\r
Instance->ServerIp = NTOHL (Instance->ServerIp);\r
RFC2347 - TFTP Option Extension\r
RFC2348 - TFTP Blocksize Option\r
RFC2349 - TFTP Timeout Interval and Transfer Size Options\r
RFC2347 - TFTP Option Extension\r
RFC2348 - TFTP Blocksize Option\r
RFC2349 - TFTP Timeout Interval and Transfer Size Options\r
+ RFC7440 - TFTP Windowsize Option\r
\r
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
This program and the accompanying materials\r
\r
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
This program and the accompanying materials\r
#define MTFTP4_DEFAULT_TIMEOUT 3\r
#define MTFTP4_DEFAULT_RETRY 5\r
#define MTFTP4_DEFAULT_BLKSIZE 512\r
#define MTFTP4_DEFAULT_TIMEOUT 3\r
#define MTFTP4_DEFAULT_RETRY 5\r
#define MTFTP4_DEFAULT_BLKSIZE 512\r
+#define MTFTP4_DEFAULT_WINDOWSIZE 1\r
#define MTFTP4_TIME_TO_GETMAP 5\r
\r
#define MTFTP4_STATE_UNCONFIGED 0\r
#define MTFTP4_TIME_TO_GETMAP 5\r
\r
#define MTFTP4_STATE_UNCONFIGED 0\r
UINT16 LastBlock;\r
LIST_ENTRY Blocks;\r
\r
UINT16 LastBlock;\r
LIST_ENTRY Blocks;\r
\r
+ UINT16 WindowSize;\r
+\r
+ //\r
+ // Record the total received block number and the already acked block number.\r
+ //\r
+ UINT64 TotalBlock;\r
+ UINT64 AckedBlock;\r
+\r
//\r
// The server's communication end point: IP and two ports. one for\r
// initial request, one for its selected port.\r
//\r
// The server's communication end point: IP and two ports. one for\r
// initial request, one for its selected port.\r
/** @file\r
Routines to process MTFTP4 options.\r
\r
/** @file\r
Routines to process MTFTP4 options.\r
\r
-Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
which accompanies this distribution. The full text of the license may be found at\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
which accompanies this distribution. The full text of the license may be found at\r
\r
CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = {\r
"blksize",\r
\r
CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = {\r
"blksize",\r
"timeout",\r
"tsize",\r
"multicast"\r
"timeout",\r
"tsize",\r
"multicast"\r
@param Count The number of options in the Options\r
@param Request Whether this is a request or OACK. The format of\r
multicast is different according to this setting.\r
@param Count The number of options in the Options\r
@param Request Whether this is a request or OACK. The format of\r
multicast is different according to this setting.\r
+ @param Operation The current performed operation.\r
@param MtftpOption The MTFTP4_OPTION for easy access.\r
\r
@retval EFI_INVALID_PARAMETER The option is mal-formated\r
@param MtftpOption The MTFTP4_OPTION for easy access.\r
\r
@retval EFI_INVALID_PARAMETER The option is mal-formated\r
IN EFI_MTFTP4_OPTION *Options,\r
IN UINT32 Count,\r
IN BOOLEAN Request,\r
IN EFI_MTFTP4_OPTION *Options,\r
IN UINT32 Count,\r
IN BOOLEAN Request,\r
OUT MTFTP4_OPTION *MtftpOption\r
)\r
{\r
OUT MTFTP4_OPTION *MtftpOption\r
)\r
{\r
\r
MtftpOption->Exist |= MTFTP4_MCAST_EXIST;\r
\r
\r
MtftpOption->Exist |= MTFTP4_MCAST_EXIST;\r
\r
+ } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "windowsize")) {\r
+ if (Operation == EFI_MTFTP4_OPCODE_WRQ) {\r
+ //\r
+ // Currently, windowsize is not supported in the write operation.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Value = NetStringToU32 (This->ValueStr);\r
+\r
+ if (Value < 1) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ MtftpOption->WindowSize = (UINT16) Value;\r
+ MtftpOption->Exist |= MTFTP4_WINDOWSIZE_EXIST;\r
} else if (Request) {\r
//\r
// Ignore the unsupported option if it is a reply, and return\r
} else if (Request) {\r
//\r
// Ignore the unsupported option if it is a reply, and return\r
\r
@param Packet The OACK packet to parse\r
@param PacketLen The length of the packet\r
\r
@param Packet The OACK packet to parse\r
@param PacketLen The length of the packet\r
+ @param Operation The current performed operation.\r
@param MtftpOption The MTFTP_OPTION for easy access.\r
\r
@retval EFI_INVALID_PARAMETER The packet option is mal-formated\r
@param MtftpOption The MTFTP_OPTION for easy access.\r
\r
@retval EFI_INVALID_PARAMETER The packet option is mal-formated\r
Mtftp4ParseOptionOack (\r
IN EFI_MTFTP4_PACKET *Packet,\r
IN UINT32 PacketLen,\r
Mtftp4ParseOptionOack (\r
IN EFI_MTFTP4_PACKET *Packet,\r
IN UINT32 PacketLen,\r
OUT MTFTP4_OPTION *MtftpOption\r
)\r
{\r
OUT MTFTP4_OPTION *MtftpOption\r
)\r
{\r
}\r
ASSERT (OptionList != NULL);\r
\r
}\r
ASSERT (OptionList != NULL);\r
\r
- Status = Mtftp4ParseOption (OptionList, Count, FALSE, MtftpOption);\r
+ Status = Mtftp4ParseOption (OptionList, Count, FALSE, Operation, MtftpOption);\r
\r
FreePool (OptionList);\r
return Status;\r
\r
FreePool (OptionList);\r
return Status;\r
#ifndef __EFI_MTFTP4_OPTION_H__\r
#define __EFI_MTFTP4_OPTION_H__\r
\r
#ifndef __EFI_MTFTP4_OPTION_H__\r
#define __EFI_MTFTP4_OPTION_H__\r
\r
-#define MTFTP4_SUPPORTED_OPTIONS 4\r
+#define MTFTP4_SUPPORTED_OPTIONS 5\r
#define MTFTP4_OPCODE_LEN 2\r
#define MTFTP4_ERRCODE_LEN 2\r
#define MTFTP4_BLKNO_LEN 2\r
#define MTFTP4_OPCODE_LEN 2\r
#define MTFTP4_ERRCODE_LEN 2\r
#define MTFTP4_BLKNO_LEN 2\r
#define MTFTP4_TIMEOUT_EXIST 0x02\r
#define MTFTP4_TSIZE_EXIST 0x04\r
#define MTFTP4_MCAST_EXIST 0x08\r
#define MTFTP4_TIMEOUT_EXIST 0x02\r
#define MTFTP4_TSIZE_EXIST 0x04\r
#define MTFTP4_MCAST_EXIST 0x08\r
+#define MTFTP4_WINDOWSIZE_EXIST 0x10\r
\r
typedef struct {\r
UINT16 BlkSize;\r
\r
typedef struct {\r
UINT16 BlkSize;\r
UINT8 Timeout;\r
UINT32 Tsize;\r
IP4_ADDR McastIp;\r
UINT8 Timeout;\r
UINT32 Tsize;\r
IP4_ADDR McastIp;\r
@param Count The number of options in the Options\r
@param Request Whether this is a request or OACK. The format of\r
multicast is different according to this setting.\r
@param Count The number of options in the Options\r
@param Request Whether this is a request or OACK. The format of\r
multicast is different according to this setting.\r
+ @param Operation The current performed operation.\r
@param MtftpOption The MTFTP4_OPTION for easy access.\r
\r
@retval EFI_INVALID_PARAMETER The option is mal-formated\r
@param MtftpOption The MTFTP4_OPTION for easy access.\r
\r
@retval EFI_INVALID_PARAMETER The option is mal-formated\r
IN EFI_MTFTP4_OPTION *Options,\r
IN UINT32 Count,\r
IN BOOLEAN Request,\r
IN EFI_MTFTP4_OPTION *Options,\r
IN UINT32 Count,\r
IN BOOLEAN Request,\r
OUT MTFTP4_OPTION *MtftpOption\r
);\r
\r
OUT MTFTP4_OPTION *MtftpOption\r
);\r
\r
\r
@param Packet The OACK packet to parse\r
@param PacketLen The length of the packet\r
\r
@param Packet The OACK packet to parse\r
@param PacketLen The length of the packet\r
+ @param Operation The current performed operation.\r
@param MtftpOption The MTFTP_OPTION for easy access.\r
\r
@retval EFI_INVALID_PARAMETER The packet option is mal-formated\r
@param MtftpOption The MTFTP_OPTION for easy access.\r
\r
@retval EFI_INVALID_PARAMETER The packet option is mal-formated\r
Mtftp4ParseOptionOack (\r
IN EFI_MTFTP4_PACKET *Packet,\r
IN UINT32 PacketLen,\r
Mtftp4ParseOptionOack (\r
IN EFI_MTFTP4_PACKET *Packet,\r
IN UINT32 PacketLen,\r
OUT MTFTP4_OPTION *MtftpOption\r
);\r
\r
OUT MTFTP4_OPTION *MtftpOption\r
);\r
\r
{\r
EFI_MTFTP4_PACKET *Ack;\r
NET_BUF *Packet;\r
{\r
EFI_MTFTP4_PACKET *Ack;\r
NET_BUF *Packet;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
\r
Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));\r
if (Packet == NULL) {\r
\r
Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));\r
if (Packet == NULL) {\r
Ack->Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);\r
Ack->Ack.Block[0] = HTONS (BlkNo);\r
\r
Ack->Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);\r
Ack->Ack.Block[0] = HTONS (BlkNo);\r
\r
- return Mtftp4SendPacket (Instance, Packet);\r
+ Status = Mtftp4SendPacket (Instance, Packet);\r
+ if (!EFI_ERROR (Status)) {\r
+ Instance->AckedBlock = Instance->TotalBlock;\r
+ }\r
+\r
+ return Status;\r
UINT16 Block;\r
UINT64 Start;\r
UINT32 DataLen;\r
UINT16 Block;\r
UINT64 Start;\r
UINT32 DataLen;\r
BOOLEAN Completed;\r
\r
Completed = FALSE;\r
BOOLEAN Completed;\r
\r
Completed = FALSE;\r
// This is the last block, save the block no\r
//\r
if (DataLen < Instance->BlkSize) {\r
// This is the last block, save the block no\r
//\r
if (DataLen < Instance->BlkSize) {\r
Instance->LastBlock = Block;\r
Mtftp4SetLastBlockNum (&Instance->Blocks, Block);\r
}\r
Instance->LastBlock = Block;\r
Mtftp4SetLastBlockNum (&Instance->Blocks, Block);\r
}\r
// to accept transfers of unlimited size. So TotalBlock is memorised as\r
// continuous block counter.\r
//\r
// to accept transfers of unlimited size. So TotalBlock is memorised as\r
// continuous block counter.\r
//\r
- Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &TotalBlock);\r
+ Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &Instance->TotalBlock);\r
\r
if (Status == EFI_NOT_FOUND) {\r
return EFI_SUCCESS;\r
\r
if (Status == EFI_NOT_FOUND) {\r
return EFI_SUCCESS;\r
}\r
\r
if (Token->Buffer != NULL) {\r
}\r
\r
if (Token->Buffer != NULL) {\r
- Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize);\r
+ Start = MultU64x32 (Instance->TotalBlock - 1, Instance->BlkSize);\r
\r
if (Start + DataLen <= Token->BufferSize) {\r
CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);\r
\r
if (Start + DataLen <= Token->BufferSize) {\r
CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);\r
INTN Expected;\r
\r
*Completed = FALSE;\r
INTN Expected;\r
\r
*Completed = FALSE;\r
+ Status = EFI_SUCCESS;\r
BlockNum = NTOHS (Packet->Data.Block);\r
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
\r
ASSERT (Expected >= 0);\r
\r
//\r
BlockNum = NTOHS (Packet->Data.Block);\r
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
\r
ASSERT (Expected >= 0);\r
\r
//\r
- // If we are active and received an unexpected packet, retransmit\r
- // the last ACK then restart receiving. If we are passive, save\r
- // the block.\r
+ // If we are active and received an unexpected packet, transmit\r
+ // the ACK for the block we received, then restart receiving the\r
+ // expected one. If we are passive, save the block.\r
//\r
if (Instance->Master && (Expected != BlockNum)) {\r
//\r
if (Instance->Master && (Expected != BlockNum)) {\r
- Mtftp4Retransmit (Instance);\r
- return EFI_SUCCESS;\r
+ //\r
+ // If Expected is 0, (UINT16) (Expected - 1) is also the expected Ack number (65535).\r
+ //\r
+ return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));\r
}\r
\r
Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);\r
}\r
\r
Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);\r
BlockNum = (UINT16) (Expected - 1);\r
}\r
\r
BlockNum = (UINT16) (Expected - 1);\r
}\r
\r
- Mtftp4RrqSendAck (Instance, BlockNum);\r
+ if (Instance->WindowSize == (Instance->TotalBlock - Instance->AckedBlock) || Expected < 0) {\r
+ Status = Mtftp4RrqSendAck (Instance, BlockNum);\r
+ }\r
+\r
- // Server can only specify a smaller block size to be used and\r
+ // Server can only specify a smaller block size and window size to be used and\r
// return the timeout matches that requested.\r
//\r
if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0)&& (Reply->BlkSize > Request->BlkSize)) ||\r
// return the timeout matches that requested.\r
//\r
if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0)&& (Reply->BlkSize > Request->BlkSize)) ||\r
- (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {\r
+ (((Reply->Exist & MTFTP4_WINDOWSIZE_EXIST) != 0)&& (Reply->WindowSize > Request->WindowSize)) ||\r
+ (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))\r
+ ) {\r
//\r
ZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
\r
//\r
ZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
\r
- Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);\r
+ Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);\r
\r
if (EFI_ERROR (Status) ||\r
!Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {\r
\r
if (EFI_ERROR (Status) ||\r
!Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {\r
\r
//\r
// Save the multicast info. Always update the Master, only update the\r
\r
//\r
// Save the multicast info. Always update the Master, only update the\r
- // multicast IP address, block size, timeoute at the first time. If IP\r
+ // multicast IP address, block size, window size, timeoute at the first time. If IP\r
// address is updated, create a UDP child to receive the multicast.\r
//\r
Instance->Master = Reply.Master;\r
// address is updated, create a UDP child to receive the multicast.\r
//\r
Instance->Master = Reply.Master;\r
Instance->BlkSize = Reply.BlkSize;\r
}\r
\r
Instance->BlkSize = Reply.BlkSize;\r
}\r
\r
+ if (Reply.WindowSize != 0) {\r
+ Instance->WindowSize = Reply.WindowSize;\r
+ }\r
+\r
if (Reply.Timeout != 0) {\r
Instance->Timeout = Reply.Timeout;\r
}\r
if (Reply.Timeout != 0) {\r
Instance->Timeout = Reply.Timeout;\r
}\r
Instance->BlkSize = Reply.BlkSize;\r
}\r
\r
Instance->BlkSize = Reply.BlkSize;\r
}\r
\r
+ if (Reply.WindowSize != 0) {\r
+ Instance->WindowSize = Reply.WindowSize;\r
+ }\r
+\r
if (Reply.Timeout != 0) {\r
Instance->Timeout = Reply.Timeout;\r
}\r
if (Reply.Timeout != 0) {\r
Instance->Timeout = Reply.Timeout;\r
}\r
// wrap to zero, because this is the simplest to implement. Here we choose\r
// this solution.\r
//\r
// wrap to zero, because this is the simplest to implement. Here we choose\r
// this solution.\r
//\r
\r
if (Range->Round > 0) {\r
\r
if (Range->Round > 0) {\r
- *TotalBlock += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;\r
- }\r
+ *TotalBlock += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;\r
+ }\r
\r
if (Range->Start > Range->Bound) {\r
Range->Start = 0;\r
\r
if (Range->Start > Range->Bound) {\r
Range->Start = 0;\r
}\r
\r
if ((Range->Start > Range->End) || Completed) {\r
}\r
\r
if ((Range->Start > Range->End) || Completed) {\r
IN UINT8 *ErrInfo\r
);\r
\r
IN UINT8 *ErrInfo\r
);\r
\r
-/**\r
- Retransmit the last packet for the instance.\r
-\r
- @param Instance The Mtftp instance\r
-\r
- @retval EFI_SUCCESS The last packet is retransmitted.\r
- @retval Others Failed to retransmit.\r
-\r
-**/\r
-EFI_STATUS\r
-Mtftp4Retransmit (\r
- IN MTFTP4_PROTOCOL *Instance\r
- );\r
\r
/**\r
The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.\r
\r
/**\r
The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.\r
// Parse and validate the options from server\r
//\r
ZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
// Parse and validate the options from server\r
//\r
ZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
- Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);\r
+ Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);\r
\r
if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {\r
//\r
\r
if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {\r
//\r