summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
diff options
context:
space:
mode:
Diffstat (limited to 'NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c')
-rw-r--r--NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c602
1 files changed, 602 insertions, 0 deletions
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
new file mode 100644
index 0000000000..69ef4d2093
--- /dev/null
+++ b/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
@@ -0,0 +1,602 @@
+/** @file
+ Mtftp6 Wrq process functions implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Mtftp6Impl.h"
+
+
+
+/**
+ Build and send a Mtftp6 data packet for upload.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] BlockNum The block num to be sent.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet.
+ @retval EFI_SUCCESS The data packet was sent.
+ @retval EFI_ABORTED The user aborted this process.
+
+**/
+EFI_STATUS
+Mtftp6WrqSendBlock (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 BlockNum
+ )
+{
+ EFI_MTFTP6_PACKET *Packet;
+ EFI_MTFTP6_TOKEN *Token;
+ NET_BUF *UdpPacket;
+ EFI_STATUS Status;
+ UINT16 DataLen;
+ UINT8 *DataBuf;
+ UINT64 Start;
+
+ //
+ // Allocate net buffer to create data packet.
+ //
+ UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN);
+
+ if (UdpPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (
+ UdpPacket,
+ MTFTP6_DATA_HEAD_LEN,
+ FALSE
+ );
+ ASSERT (Packet != NULL);
+
+ Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA);
+ Packet->Data.Block = HTONS (BlockNum);
+
+ //
+ // Read the block from either the buffer or PacketNeeded callback
+ //
+ Token = Instance->Token;
+ DataLen = Instance->BlkSize;
+
+ if (Token->Buffer != NULL) {
+ Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);
+
+ if (Token->BufferSize < Start + Instance->BlkSize) {
+ DataLen = (UINT16) (Token->BufferSize - Start);
+ Instance->LastBlk = BlockNum;
+ Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);
+ }
+
+ } else {
+ //
+ // Get data from PacketNeeded
+ //
+ DataBuf = NULL;
+ Status = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf);
+
+ if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {
+ if (DataBuf != NULL) {
+ gBS->FreePool (DataBuf);
+ }
+ //
+ // The received packet has already been freed.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+
+ return EFI_ABORTED;
+ }
+
+ if (DataLen < Instance->BlkSize) {
+ Instance->LastBlk = BlockNum;
+ Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ CopyMem (Packet->Data.Data, DataBuf, DataLen);
+ gBS->FreePool (DataBuf);
+ }
+ }
+
+ //
+ // Reset current retry count of the instance.
+ //
+ Instance->CurRetry = 0;
+
+ return Mtftp6TransmitPacket (Instance, UdpPacket);
+}
+
+
+/**
+ Function to handle received ACK packet. If the ACK number matches the
+ expected block number, with more data pending, send the next
+ block. Otherwise, tell the caller that we are done.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the received packet.
+ @param[in] Len The length of the packet.
+ @param[out] UdpPacket The net buf of received packet.
+ @param[out] IsCompleted If TRUE, the upload has been completed.
+ Otherwise, the upload has not been completed.
+
+ @retval EFI_SUCCESS The ACK packet successfully processed.
+ @retval EFI_TFTP_ERROR The block number loops back.
+ @retval Others Failed to transmit the next data packet.
+
+**/
+EFI_STATUS
+Mtftp6WrqHandleAck (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 Len,
+ OUT NET_BUF **UdpPacket,
+ OUT BOOLEAN *IsCompleted
+ )
+{
+ UINT16 AckNum;
+ INTN Expected;
+ UINT64 TotalBlock;
+
+ *IsCompleted = FALSE;
+ AckNum = NTOHS (Packet->Ack.Block[0]);
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput
+ // restart receive.
+ //
+ if (Expected != AckNum) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Remove the acked block number, if this is the last block number,
+ // tell the Mtftp6WrqInput to finish the transfer. This is the last
+ // block number if the block range are empty..
+ //
+ Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock);
+
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+
+ if (Expected < 0) {
+ //
+ // The block range is empty. It may either because the the last
+ // block has been ACKed, or the sequence number just looped back,
+ // that is, there is more than 0xffff blocks.
+ //
+ if (Instance->LastBlk == AckNum) {
+ ASSERT (Instance->LastBlk >= 1);
+ *IsCompleted = TRUE;
+ return EFI_SUCCESS;
+
+ } else {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if block number rolls back.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "Block number rolls back, not supported, try blksize option"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+ }
+
+ //
+ // Free the receive buffer before send new packet since it might need
+ // reconfigure udpio.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+
+ return Mtftp6WrqSendBlock (Instance, (UINT16) Expected);
+}
+
+
+/**
+ Check whether the received OACK is valid. The OACK is valid
+ only if:
+ 1. It only include options requested by us.
+ 2. It can only include a smaller block size.
+ 3. It can't change the proposed time out value.
+ 4. Other requirements of the individal MTFTP6 options as required.
+
+ @param[in] ReplyInfo The pointer to options information in reply packet.
+ @param[in] RequestInfo The pointer to requested options information.
+
+ @retval TRUE If the option in OACK is valid.
+ @retval FALSE If the option is invalid.
+
+**/
+BOOLEAN
+Mtftp6WrqOackValid (
+ IN MTFTP6_EXT_OPTION_INFO *ReplyInfo,
+ IN MTFTP6_EXT_OPTION_INFO *RequestInfo
+ )
+{
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size to be used and
+ // return the timeout matches that requested.
+ //
+ if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||
+ (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))
+ ) {
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Process the OACK packet for Wrq.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Packet The pointer to the received packet.
+ @param[in] Len The length of the packet.
+ @param[out] UdpPacket The net buf of received packet.
+ @param[out] IsCompleted If TRUE, the upload has been completed.
+ Otherwise, the upload has not been completed.
+
+ @retval EFI_SUCCESS The OACK packet successfully processed.
+ @retval EFI_TFTP_ERROR An TFTP communication error happened.
+ @retval Others Failed to process the OACK packet.
+
+**/
+EFI_STATUS
+Mtftp6WrqHandleOack (
+ IN MTFTP6_INSTANCE *Instance,
+ IN EFI_MTFTP6_PACKET *Packet,
+ IN UINT32 Len,
+ OUT NET_BUF **UdpPacket,
+ OUT BOOLEAN *IsCompleted
+ )
+{
+ EFI_MTFTP6_OPTION *Options;
+ UINT32 Count;
+ MTFTP6_EXT_OPTION_INFO ExtInfo;
+ EFI_MTFTP6_PACKET Dummy;
+ EFI_STATUS Status;
+ INTN Expected;
+
+ *IsCompleted = FALSE;
+
+ //
+ // Ignore the OACK if already started the upload
+ //
+ Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);
+
+ if (Expected != 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Parse and validate the options from server
+ //
+ ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));
+
+ Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);
+
+ if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) {
+ //
+ // Don't send a MTFTP error packet when out of resource, it can
+ // only make it worse.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (*UdpPacket);
+ *UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if invalid Oack packet received.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Mal-formated OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if (ExtInfo.BlkSize != 0) {
+ Instance->BlkSize = ExtInfo.BlkSize;
+ }
+
+ if (ExtInfo.Timeout != 0) {
+ Instance->Timeout = ExtInfo.Timeout;
+ }
+
+ //
+ // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck,
+ // which will start the transmission of the first data block.
+ //
+ Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK);
+ Dummy.Ack.Block[0] = 0;
+
+ return Mtftp6WrqHandleAck (
+ Instance,
+ &Dummy,
+ sizeof (EFI_MTFTP6_ACK_HEADER),
+ UdpPacket,
+ IsCompleted
+ );
+}
+
+
+/**
+ The packet process callback for Mtftp6 upload.
+
+ @param[in] UdpPacket The pointer to the packet received.
+ @param[in] UdpEpt The pointer to the Udp6 access point.
+ @param[in] IoStatus The status from Udp6 instance.
+ @param[in] Context The pointer to the context.
+
+**/
+VOID
+EFIAPI
+Mtftp6WrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *UdpEpt,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP6_INSTANCE *Instance;
+ EFI_MTFTP6_PACKET *Packet;
+ BOOLEAN IsCompleted;
+ EFI_STATUS Status;
+ UINT32 TotalNum;
+ UINT32 Len;
+ UINT16 Opcode;
+
+ Instance = (MTFTP6_INSTANCE *) Context;
+
+ NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);
+
+ IsCompleted = FALSE;
+ Packet = NULL;
+ Status = EFI_SUCCESS;
+ TotalNum = 0;
+
+ //
+ // Return error status if Udp6 instance failed to receive.
+ //
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client.
+ //
+ if (UdpEpt->RemotePort != Instance->ServerDataPort) {
+ if (Instance->ServerDataPort != 0) {
+ goto ON_EXIT;
+ } else {
+ Instance->ServerDataPort = UdpEpt->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+ TotalNum = UdpPacket->BlockOpNum;
+
+ if (TotalNum > 1) {
+ Packet = AllocateZeroPool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ ASSERT (Packet != NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Callback to the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if (Instance->Token->CheckPacket != NULL &&
+ (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)
+ ) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp6,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {
+ //
+ // Free the received packet before send new packet in ReceiveNotify,
+ // since the udpio might need to be reconfigured.
+ //
+ NetbufFree (UdpPacket);
+ UdpPacket = NULL;
+ //
+ // Send the Mtftp6 error message if user aborted the current session.
+ //
+ Mtftp6SendError (
+ Instance,
+ EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Switch the process routines by the operation code.
+ //
+ switch (Opcode) {
+ case EFI_MTFTP6_OPCODE_ACK:
+ if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) {
+ goto ON_EXIT;
+ }
+ //
+ // Handle the Ack packet of Wrq.
+ //
+ Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted);
+ break;
+
+ case EFI_MTFTP6_OPCODE_OACK:
+ if (Len <= MTFTP6_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+ //
+ // Handle the Oack packet of Wrq.
+ //
+ Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted);
+ break;
+
+ default:
+ //
+ // Drop and return eror if received error message.
+ //
+ Status = EFI_TFTP_ERROR;
+ break;
+ }
+
+ON_EXIT:
+ //
+ // Free the resources, then if !EFI_ERROR (Status) and not completed,
+ // restart the receive, otherwise end the session.
+ //
+ if (Packet != NULL && TotalNum > 1) {
+ FreePool (Packet);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ if (!EFI_ERROR (Status) && !IsCompleted) {
+ Status = UdpIoRecvDatagram (
+ Instance->UdpIo,
+ Mtftp6WrqInput,
+ Instance,
+ 0
+ );
+ }
+ //
+ // Clean up the current session if failed to continue.
+ //
+ if (EFI_ERROR (Status) || IsCompleted) {
+ Mtftp6OperationClean (Instance, Status);
+ }
+}
+
+
+/**
+ Start the Mtftp6 instance to upload. It will first init some states,
+ then send the WRQ request packet, and start to receive the packet.
+
+ @param[in] Instance The pointer to the Mtftp6 instance.
+ @param[in] Operation The operation code of the current packet.
+
+ @retval EFI_SUCCESS The Mtftp6 was started to upload.
+ @retval Others Failed to start to upload.
+
+**/
+EFI_STATUS
+Mtftp6WrqStart (
+ IN MTFTP6_INSTANCE *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [0, 0xffff]. For example:
+ // the client sends an WRQ request to the server, the server
+ // ACK with an ACK0 to let client start transfer the first
+ // packet.
+ //
+ Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp6SendRequest (Instance, Operation);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (
+ Instance->UdpIo,
+ Mtftp6WrqInput,
+ Instance,
+ 0
+ );
+}
+