/** @file | |
Routines to process Rrq (download). | |
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR> | |
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Mtftp4Impl.h" | |
/** | |
The packet process callback for MTFTP download. | |
@param UdpPacket The packet received | |
@param EndPoint The local/remote access point of the packet | |
@param IoStatus The status of the receiving | |
@param Context Opaque parameter, which is the MTFTP session | |
**/ | |
VOID | |
EFIAPI | |
Mtftp4RrqInput ( | |
IN NET_BUF *UdpPacket, | |
IN UDP_END_POINT *EndPoint, | |
IN EFI_STATUS IoStatus, | |
IN VOID *Context | |
); | |
/** | |
Start the MTFTP session to download. | |
It will first initialize some of the internal states then build and send a RRQ | |
request packet, at last, it will start receive for the downloading. | |
@param Instance The Mtftp session | |
@param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ | |
or EFI_MTFTP4_OPCODE_DIR. | |
@retval EFI_SUCCESS The mtftp download session is started. | |
@retval Others Failed to start downloading. | |
**/ | |
EFI_STATUS | |
Mtftp4RrqStart ( | |
IN MTFTP4_PROTOCOL *Instance, | |
IN UINT16 Operation | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// The valid block number range are [1, 0xffff]. For example: | |
// the client sends an RRQ request to the server, the server | |
// transfers the DATA1 block. If option negotiation is ongoing, | |
// the server will send back an OACK, then client will send ACK0. | |
// | |
Status = Mtftp4InitBlockRange (&Instance->Blocks, 1, 0xffff); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = Mtftp4SendRequest (Instance); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0); | |
} | |
/** | |
Build and send a ACK packet for the download session. | |
@param Instance The Mtftp session | |
@param BlkNo The BlkNo to ack. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet | |
@retval EFI_SUCCESS The ACK has been sent | |
@retval Others Failed to send the ACK. | |
**/ | |
EFI_STATUS | |
Mtftp4RrqSendAck ( | |
IN MTFTP4_PROTOCOL *Instance, | |
IN UINT16 BlkNo | |
) | |
{ | |
EFI_MTFTP4_PACKET *Ack; | |
NET_BUF *Packet; | |
EFI_STATUS Status; | |
Status = EFI_SUCCESS; | |
Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER)); | |
if (Packet == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Ack = (EFI_MTFTP4_PACKET *)NetbufAllocSpace ( | |
Packet, | |
sizeof (EFI_MTFTP4_ACK_HEADER), | |
FALSE | |
); | |
ASSERT (Ack != NULL); | |
Ack->Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK); | |
Ack->Ack.Block[0] = HTONS (BlkNo); | |
Status = Mtftp4SendPacket (Instance, Packet); | |
if (!EFI_ERROR (Status)) { | |
Instance->AckedBlock = Instance->TotalBlock; | |
} | |
return Status; | |
} | |
/** | |
Deliver the received data block to the user, which can be saved | |
in the user provide buffer or through the CheckPacket callback. | |
@param Instance The Mtftp session | |
@param Packet The received data packet | |
@param Len The packet length | |
@retval EFI_SUCCESS The data is saved successfully | |
@retval EFI_ABORTED The user tells to abort by return an error through | |
CheckPacket | |
@retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is | |
updated to the actual buffer size needed. | |
**/ | |
EFI_STATUS | |
Mtftp4RrqSaveBlock ( | |
IN OUT MTFTP4_PROTOCOL *Instance, | |
IN EFI_MTFTP4_PACKET *Packet, | |
IN UINT32 Len | |
) | |
{ | |
EFI_MTFTP4_TOKEN *Token; | |
EFI_STATUS Status; | |
UINT16 Block; | |
UINT64 Start; | |
UINT32 DataLen; | |
UINT64 BlockCounter; | |
BOOLEAN Completed; | |
Completed = FALSE; | |
Token = Instance->Token; | |
Block = NTOHS (Packet->Data.Block); | |
DataLen = Len - MTFTP4_DATA_HEAD_LEN; | |
// | |
// This is the last block, save the block no | |
// | |
if (DataLen < Instance->BlkSize) { | |
Completed = TRUE; | |
Instance->LastBlock = Block; | |
Mtftp4SetLastBlockNum (&Instance->Blocks, Block); | |
} | |
// | |
// Remove this block number from the file hole. If Mtftp4RemoveBlockNum | |
// returns EFI_NOT_FOUND, the block has been saved, don't save it again. | |
// Note that : For bigger files, allowing the block counter to roll over | |
// to accept transfers of unlimited size. So BlockCounter is memorised as | |
// continuous block counter. | |
// | |
Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &BlockCounter); | |
if (Status == EFI_NOT_FOUND) { | |
return EFI_SUCCESS; | |
} else if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Token->CheckPacket != NULL) { | |
Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16)Len, Packet); | |
if (EFI_ERROR (Status)) { | |
Mtftp4SendError ( | |
Instance, | |
EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, | |
(UINT8 *)"User aborted download" | |
); | |
return EFI_ABORTED; | |
} | |
} | |
if (Token->Buffer != NULL) { | |
Start = MultU64x32 (BlockCounter - 1, Instance->BlkSize); | |
if (Start + DataLen <= Token->BufferSize) { | |
CopyMem ((UINT8 *)Token->Buffer + Start, Packet->Data.Data, DataLen); | |
// | |
// Update the file size when received the last block | |
// | |
if ((Instance->LastBlock == Block) && Completed) { | |
Token->BufferSize = Start + DataLen; | |
} | |
} else if (Instance->LastBlock != 0) { | |
// | |
// Don't save the data if the buffer is too small, return | |
// EFI_BUFFER_TOO_SMALL if received the last packet. This | |
// will give a accurate file length. | |
// | |
Token->BufferSize = Start + DataLen; | |
Mtftp4SendError ( | |
Instance, | |
EFI_MTFTP4_ERRORCODE_DISK_FULL, | |
(UINT8 *)"User provided memory block is too small" | |
); | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Function to process the received data packets. | |
It will save the block then send back an ACK if it is active. | |
@param Instance The downloading MTFTP session | |
@param Packet The packet received | |
@param Len The length of the packet | |
@param Multicast Whether this packet is multicast or unicast | |
@param Completed Return whether the download has completed | |
@retval EFI_SUCCESS The data packet is successfully processed | |
@retval EFI_ABORTED The download is aborted by the user | |
@retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small | |
**/ | |
EFI_STATUS | |
Mtftp4RrqHandleData ( | |
IN MTFTP4_PROTOCOL *Instance, | |
IN EFI_MTFTP4_PACKET *Packet, | |
IN UINT32 Len, | |
IN BOOLEAN Multicast, | |
OUT BOOLEAN *Completed | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 BlockNum; | |
INTN Expected; | |
*Completed = FALSE; | |
Status = EFI_SUCCESS; | |
BlockNum = NTOHS (Packet->Data.Block); | |
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); | |
ASSERT (Expected >= 0); | |
// | |
// If we are active (Master) and received an unexpected packet, transmit | |
// the ACK for the block we received, then restart receiving the | |
// expected one. If we are passive (Slave), save the block. | |
// | |
if (Instance->Master && (Expected != BlockNum)) { | |
// | |
// If Expected is 0, (UINT16) (Expected - 1) is also the expected Ack number (65535). | |
// | |
return Mtftp4RrqSendAck (Instance, (UINT16)(Expected - 1)); | |
} | |
Status = Mtftp4RrqSaveBlock (Instance, Packet, Len); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Record the total received and saved block number. | |
// | |
Instance->TotalBlock++; | |
// | |
// Reset the passive client's timer whenever it received a | |
// valid data packet. | |
// | |
if (!Instance->Master) { | |
Mtftp4SetTimeout (Instance); | |
} | |
// | |
// Check whether we have received all the blocks. Send the ACK if we | |
// are active (unicast client or master client for multicast download). | |
// If we have received all the blocks, send an ACK even if we are passive | |
// to tell the server that we are done. | |
// | |
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); | |
if (Instance->Master || (Expected < 0)) { | |
if (Expected < 0) { | |
// | |
// If we are passive client, then the just received Block maybe | |
// isn't the last block. We need to send an ACK to the last block | |
// to inform the server that we are done. If we are active client, | |
// the Block == Instance->LastBlock. | |
// | |
BlockNum = Instance->LastBlock; | |
*Completed = TRUE; | |
} else { | |
BlockNum = (UINT16)(Expected - 1); | |
} | |
if ((Instance->WindowSize == (Instance->TotalBlock - Instance->AckedBlock)) || (Expected < 0)) { | |
Status = Mtftp4RrqSendAck (Instance, BlockNum); | |
} | |
} | |
return Status; | |
} | |
/** | |
Validate whether the options received in the server's OACK packet is valid. | |
The options are valid only if: | |
1. The server doesn't include options not requested by us | |
2. The server can only use smaller blksize than that is requested | |
3. The server can only use the same timeout as requested | |
4. The server doesn't change its multicast channel. | |
@param This The downloading Mtftp session | |
@param Reply The options in the OACK packet | |
@param Request The requested options | |
@retval TRUE The options in the OACK is OK. | |
@retval FALSE The options in the OACK is invalid. | |
**/ | |
BOOLEAN | |
Mtftp4RrqOackValid ( | |
IN MTFTP4_PROTOCOL *This, | |
IN MTFTP4_OPTION *Reply, | |
IN MTFTP4_OPTION *Request | |
) | |
{ | |
// | |
// It is invalid for server to return options we don't request | |
// | |
if ((Reply->Exist &~Request->Exist) != 0) { | |
return FALSE; | |
} | |
// | |
// Server can only specify a smaller block size and window size to be used and | |
// return the timeout matches that requested. | |
// | |
if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) || | |
(((Reply->Exist & MTFTP4_WINDOWSIZE_EXIST) != 0) && (Reply->WindowSize > Request->WindowSize)) || | |
(((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout)) | |
) | |
{ | |
return FALSE; | |
} | |
// | |
// The server can send ",,master" to client to change its master | |
// setting. But if it use the specific multicast channel, it can't | |
// change the setting. | |
// | |
if (((Reply->Exist & MTFTP4_MCAST_EXIST) != 0) && (This->McastIp != 0)) { | |
if ((Reply->McastIp != 0) && (Reply->McastIp != This->McastIp)) { | |
return FALSE; | |
} | |
if ((Reply->McastPort != 0) && (Reply->McastPort != This->McastPort)) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
/** | |
Configure a UDP IO port to receive the multicast. | |
@param McastIo The UDP IO to configure | |
@param Context The opaque parameter to the function which is the | |
MTFTP session. | |
@retval EFI_SUCCESS The UDP child is successfully configured. | |
@retval Others Failed to configure the UDP child. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Mtftp4RrqConfigMcastPort ( | |
IN UDP_IO *McastIo, | |
IN VOID *Context | |
) | |
{ | |
MTFTP4_PROTOCOL *Instance; | |
EFI_MTFTP4_CONFIG_DATA *Config; | |
EFI_UDP4_CONFIG_DATA UdpConfig; | |
EFI_IPv4_ADDRESS Group; | |
EFI_STATUS Status; | |
IP4_ADDR Ip; | |
Instance = (MTFTP4_PROTOCOL *)Context; | |
Config = &Instance->Config; | |
UdpConfig.AcceptBroadcast = FALSE; | |
UdpConfig.AcceptPromiscuous = FALSE; | |
UdpConfig.AcceptAnyPort = FALSE; | |
UdpConfig.AllowDuplicatePort = FALSE; | |
UdpConfig.TypeOfService = 0; | |
UdpConfig.TimeToLive = 64; | |
UdpConfig.DoNotFragment = FALSE; | |
UdpConfig.ReceiveTimeout = 0; | |
UdpConfig.TransmitTimeout = 0; | |
UdpConfig.UseDefaultAddress = Config->UseDefaultSetting; | |
IP4_COPY_ADDRESS (&UdpConfig.StationAddress, &Config->StationIp); | |
IP4_COPY_ADDRESS (&UdpConfig.SubnetMask, &Config->SubnetMask); | |
UdpConfig.StationPort = Instance->McastPort; | |
UdpConfig.RemotePort = 0; | |
Ip = HTONL (Instance->ServerIp); | |
IP4_COPY_ADDRESS (&UdpConfig.RemoteAddress, &Ip); | |
Status = McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, &UdpConfig); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (!Config->UseDefaultSetting && | |
!EFI_IP4_EQUAL (&mZeroIp4Addr, &Config->GatewayIp)) | |
{ | |
// | |
// The station IP address is manually configured and the Gateway IP is not 0. | |
// Add the default route for this UDP instance. | |
// | |
Status = McastIo->Protocol.Udp4->Routes ( | |
McastIo->Protocol.Udp4, | |
FALSE, | |
&mZeroIp4Addr, | |
&mZeroIp4Addr, | |
&Config->GatewayIp | |
); | |
if (EFI_ERROR (Status)) { | |
McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, NULL); | |
return Status; | |
} | |
} | |
// | |
// join the multicast group | |
// | |
Ip = HTONL (Instance->McastIp); | |
IP4_COPY_ADDRESS (&Group, &Ip); | |
return McastIo->Protocol.Udp4->Groups (McastIo->Protocol.Udp4, TRUE, &Group); | |
} | |
/** | |
Function to process the OACK. | |
It will first validate the OACK packet, then update the various negotiated parameters. | |
@param Instance The download MTFTP session | |
@param Packet The packet received | |
@param Len The packet length | |
@param Multicast Whether this packet is received as a multicast | |
@param Completed Returns whether the download has completed. NOT | |
used by this function. | |
@retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child | |
@retval EFI_TFTP_ERROR Some error happened during the process | |
@retval EFI_SUCCESS The OACK is successfully processed. | |
**/ | |
EFI_STATUS | |
Mtftp4RrqHandleOack ( | |
IN OUT MTFTP4_PROTOCOL *Instance, | |
IN EFI_MTFTP4_PACKET *Packet, | |
IN UINT32 Len, | |
IN BOOLEAN Multicast, | |
OUT BOOLEAN *Completed | |
) | |
{ | |
MTFTP4_OPTION Reply; | |
EFI_STATUS Status; | |
INTN Expected; | |
EFI_UDP4_PROTOCOL *Udp4; | |
*Completed = FALSE; | |
// | |
// If already started the master download, don't change the | |
// setting. Master download always succeeds. | |
// | |
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks); | |
ASSERT (Expected != -1); | |
if (Instance->Master && (Expected != 1)) { | |
return EFI_SUCCESS; | |
} | |
// | |
// Parse and validate the options from server | |
// | |
ZeroMem (&Reply, sizeof (MTFTP4_OPTION)); | |
Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply); | |
if (EFI_ERROR (Status) || | |
!Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) | |
{ | |
// | |
// Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES. | |
// | |
if (Status != EFI_OUT_OF_RESOURCES) { | |
Mtftp4SendError ( | |
Instance, | |
EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, | |
(UINT8 *)"Malformatted OACK packet" | |
); | |
} | |
return EFI_TFTP_ERROR; | |
} | |
if ((Reply.Exist & MTFTP4_MCAST_EXIST) != 0) { | |
// | |
// Save the multicast info. Always update the Master, only update the | |
// multicast IP address, block size, window size, timeout at the first time. | |
// If IP address is updated, create a UDP child to receive the multicast. | |
// | |
Instance->Master = Reply.Master; | |
if (Instance->McastIp == 0) { | |
if ((Reply.McastIp == 0) || (Reply.McastPort == 0)) { | |
Mtftp4SendError ( | |
Instance, | |
EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, | |
(UINT8 *)"Illegal multicast setting" | |
); | |
return EFI_TFTP_ERROR; | |
} | |
// | |
// Create a UDP child then start receive the multicast from it. | |
// | |
Instance->McastIp = Reply.McastIp; | |
Instance->McastPort = Reply.McastPort; | |
if (Instance->McastUdpPort == NULL) { | |
Instance->McastUdpPort = UdpIoCreateIo ( | |
Instance->Service->Controller, | |
Instance->Service->Image, | |
Mtftp4RrqConfigMcastPort, | |
UDP_IO_UDP4_VERSION, | |
Instance | |
); | |
if (Instance->McastUdpPort != NULL) { | |
Status = gBS->OpenProtocol ( | |
Instance->McastUdpPort->UdpHandle, | |
&gEfiUdp4ProtocolGuid, | |
(VOID **)&Udp4, | |
Instance->Service->Image, | |
Instance->Handle, | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
if (EFI_ERROR (Status)) { | |
UdpIoFreeIo (Instance->McastUdpPort); | |
Instance->McastUdpPort = NULL; | |
return EFI_DEVICE_ERROR; | |
} | |
} | |
} | |
if (Instance->McastUdpPort == NULL) { | |
return EFI_DEVICE_ERROR; | |
} | |
Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0); | |
if (EFI_ERROR (Status)) { | |
Mtftp4SendError ( | |
Instance, | |
EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION, | |
(UINT8 *)"Failed to create socket to receive multicast packet" | |
); | |
return Status; | |
} | |
// | |
// Update the parameters used. | |
// | |
if (Reply.BlkSize != 0) { | |
Instance->BlkSize = Reply.BlkSize; | |
} | |
if (Reply.WindowSize != 0) { | |
Instance->WindowSize = Reply.WindowSize; | |
} | |
if (Reply.Timeout != 0) { | |
Instance->Timeout = Reply.Timeout; | |
} | |
} | |
} else { | |
Instance->Master = TRUE; | |
if (Reply.BlkSize != 0) { | |
Instance->BlkSize = Reply.BlkSize; | |
} | |
if (Reply.WindowSize != 0) { | |
Instance->WindowSize = Reply.WindowSize; | |
} | |
if (Reply.Timeout != 0) { | |
Instance->Timeout = Reply.Timeout; | |
} | |
} | |
// | |
// Send an ACK to (Expected - 1) which is 0 for unicast download, | |
// or tell the server we want to receive the Expected block. | |
// | |
return Mtftp4RrqSendAck (Instance, (UINT16)(Expected - 1)); | |
} | |
/** | |
The packet process callback for MTFTP download. | |
@param UdpPacket The packet received | |
@param EndPoint The local/remote access point of the packet | |
@param IoStatus The status of the receiving | |
@param Context Opaque parameter, which is the MTFTP session | |
**/ | |
VOID | |
EFIAPI | |
Mtftp4RrqInput ( | |
IN NET_BUF *UdpPacket, | |
IN UDP_END_POINT *EndPoint, | |
IN EFI_STATUS IoStatus, | |
IN VOID *Context | |
) | |
{ | |
MTFTP4_PROTOCOL *Instance; | |
EFI_MTFTP4_PACKET *Packet; | |
BOOLEAN Completed; | |
BOOLEAN Multicast; | |
EFI_STATUS Status; | |
UINT16 Opcode; | |
UINT32 Len; | |
Instance = (MTFTP4_PROTOCOL *)Context; | |
NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE); | |
Status = EFI_SUCCESS; | |
Packet = NULL; | |
Completed = FALSE; | |
Multicast = FALSE; | |
if (EFI_ERROR (IoStatus)) { | |
Status = IoStatus; | |
goto ON_EXIT; | |
} | |
ASSERT (UdpPacket != NULL); | |
// | |
// Find the port this packet is from to restart receive correctly. | |
// | |
Multicast = (BOOLEAN)(EndPoint->LocalAddr.Addr[0] == Instance->McastIp); | |
if (UdpPacket->TotalSize < MTFTP4_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. The server | |
// is required to use the same port as RemotePort to multicast the | |
// data. | |
// | |
if (EndPoint->RemotePort != Instance->ConnectedPort) { | |
if (Instance->ConnectedPort != 0) { | |
goto ON_EXIT; | |
} else { | |
Instance->ConnectedPort = EndPoint->RemotePort; | |
} | |
} | |
// | |
// Copy the MTFTP packet to a continuous buffer if it isn't already so. | |
// | |
Len = UdpPacket->TotalSize; | |
if (UdpPacket->BlockOpNum > 1) { | |
Packet = AllocatePool (Len); | |
if (Packet == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
NetbufCopy (UdpPacket, 0, Len, (UINT8 *)Packet); | |
} else { | |
Packet = (EFI_MTFTP4_PACKET *)NetbufGetByte (UdpPacket, 0, NULL); | |
ASSERT (Packet != NULL); | |
} | |
Opcode = NTOHS (Packet->OpCode); | |
// | |
// Call the user's CheckPacket if provided. Abort the transmission | |
// if CheckPacket returns an EFI_ERROR code. | |
// | |
if ((Instance->Token->CheckPacket != NULL) && | |
((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) | |
{ | |
Status = Instance->Token->CheckPacket ( | |
&Instance->Mtftp4, | |
Instance->Token, | |
(UINT16)Len, | |
Packet | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// Send an error message to the server to inform it | |
// | |
if (Opcode != EFI_MTFTP4_OPCODE_ERROR) { | |
Mtftp4SendError ( | |
Instance, | |
EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, | |
(UINT8 *)"User aborted the transfer" | |
); | |
} | |
Status = EFI_ABORTED; | |
goto ON_EXIT; | |
} | |
} | |
switch (Opcode) { | |
case EFI_MTFTP4_OPCODE_DATA: | |
if ((Len > (UINT32)(MTFTP4_DATA_HEAD_LEN + Instance->BlkSize)) || | |
(Len < (UINT32)MTFTP4_DATA_HEAD_LEN)) | |
{ | |
goto ON_EXIT; | |
} | |
Status = Mtftp4RrqHandleData (Instance, Packet, Len, Multicast, &Completed); | |
break; | |
case EFI_MTFTP4_OPCODE_OACK: | |
if (Multicast || (Len <= MTFTP4_OPCODE_LEN)) { | |
goto ON_EXIT; | |
} | |
Status = Mtftp4RrqHandleOack (Instance, Packet, Len, Multicast, &Completed); | |
break; | |
case EFI_MTFTP4_OPCODE_ERROR: | |
Status = EFI_TFTP_ERROR; | |
break; | |
default: | |
break; | |
} | |
ON_EXIT: | |
// | |
// Free the resources, then if !EFI_ERROR (Status), restart the | |
// receive, otherwise end the session. | |
// | |
if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) { | |
FreePool (Packet); | |
} | |
if (UdpPacket != NULL) { | |
NetbufFree (UdpPacket); | |
} | |
if (!EFI_ERROR (Status) && !Completed) { | |
if (Multicast) { | |
Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0); | |
} else { | |
Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0); | |
} | |
} | |
if (EFI_ERROR (Status) || Completed) { | |
Mtftp4CleanOperation (Instance, Status); | |
} | |
} |