2 Routines to process Rrq (download).
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php<BR>
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include "Mtftp4Impl.h"
21 The packet process callback for MTFTP download.
23 @param UdpPacket The packet received
24 @param EndPoint The local/remote access point of the packet
25 @param IoStatus The status of the receiving
26 @param Context Opaque parameter, which is the MTFTP session
32 IN NET_BUF
*UdpPacket
,
33 IN UDP_END_POINT
*EndPoint
,
34 IN EFI_STATUS IoStatus
,
40 Start the MTFTP session to download.
42 It will first initialize some of the internal states then build and send a RRQ
43 reqeuest packet, at last, it will start receive for the downloading.
45 @param Instance The Mtftp session
46 @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
47 or EFI_MTFTP4_OPCODE_DIR.
49 @retval EFI_SUCCESS The mtftp download session is started.
50 @retval Others Failed to start downloading.
55 IN MTFTP4_PROTOCOL
*Instance
,
62 // The valid block number range are [1, 0xffff]. For example:
63 // the client sends an RRQ request to the server, the server
64 // transfers the DATA1 block. If option negoitation is ongoing,
65 // the server will send back an OACK, then client will send ACK0.
67 Status
= Mtftp4InitBlockRange (&Instance
->Blocks
, 1, 0xffff);
69 if (EFI_ERROR (Status
)) {
73 Status
= Mtftp4SendRequest (Instance
);
75 if (EFI_ERROR (Status
)) {
79 return UdpIoRecvDatagram (Instance
->UnicastPort
, Mtftp4RrqInput
, Instance
, 0);
84 Build and send a ACK packet for the download session.
86 @param Instance The Mtftp session
87 @param BlkNo The BlkNo to ack.
89 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
90 @retval EFI_SUCCESS The ACK has been sent
91 @retval Others Failed to send the ACK.
96 IN MTFTP4_PROTOCOL
*Instance
,
100 EFI_MTFTP4_PACKET
*Ack
;
104 Status
= EFI_SUCCESS
;
106 Packet
= NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER
));
107 if (Packet
== NULL
) {
108 return EFI_OUT_OF_RESOURCES
;
111 Ack
= (EFI_MTFTP4_PACKET
*) NetbufAllocSpace (
113 sizeof (EFI_MTFTP4_ACK_HEADER
),
116 ASSERT (Ack
!= NULL
);
118 Ack
->Ack
.OpCode
= HTONS (EFI_MTFTP4_OPCODE_ACK
);
119 Ack
->Ack
.Block
[0] = HTONS (BlkNo
);
121 Status
= Mtftp4SendPacket (Instance
, Packet
);
122 if (!EFI_ERROR (Status
)) {
123 Instance
->AckedBlock
= Instance
->TotalBlock
;
131 Deliver the received data block to the user, which can be saved
132 in the user provide buffer or through the CheckPacket callback.
134 @param Instance The Mtftp session
135 @param Packet The received data packet
136 @param Len The packet length
138 @retval EFI_SUCCESS The data is saved successfully
139 @retval EFI_ABORTED The user tells to abort by return an error through
141 @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is
142 updated to the actual buffer size needed.
147 IN OUT MTFTP4_PROTOCOL
*Instance
,
148 IN EFI_MTFTP4_PACKET
*Packet
,
152 EFI_MTFTP4_TOKEN
*Token
;
161 Token
= Instance
->Token
;
162 Block
= NTOHS (Packet
->Data
.Block
);
163 DataLen
= Len
- MTFTP4_DATA_HEAD_LEN
;
166 // This is the last block, save the block no
168 if (DataLen
< Instance
->BlkSize
) {
170 Instance
->LastBlock
= Block
;
171 Mtftp4SetLastBlockNum (&Instance
->Blocks
, Block
);
175 // Remove this block number from the file hole. If Mtftp4RemoveBlockNum
176 // returns EFI_NOT_FOUND, the block has been saved, don't save it again.
177 // Note that : For bigger files, allowing the block counter to roll over
178 // to accept transfers of unlimited size. So BlockCounter is memorised as
179 // continuous block counter.
181 Status
= Mtftp4RemoveBlockNum (&Instance
->Blocks
, Block
, Completed
, &BlockCounter
);
183 if (Status
== EFI_NOT_FOUND
) {
185 } else if (EFI_ERROR (Status
)) {
189 if (Token
->CheckPacket
!= NULL
) {
190 Status
= Token
->CheckPacket (&Instance
->Mtftp4
, Token
, (UINT16
) Len
, Packet
);
192 if (EFI_ERROR (Status
)) {
195 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
196 (UINT8
*) "User aborted download"
203 if (Token
->Buffer
!= NULL
) {
204 Start
= MultU64x32 (BlockCounter
- 1, Instance
->BlkSize
);
206 if (Start
+ DataLen
<= Token
->BufferSize
) {
207 CopyMem ((UINT8
*) Token
->Buffer
+ Start
, Packet
->Data
.Data
, DataLen
);
210 // Update the file size when received the last block
212 if ((Instance
->LastBlock
== Block
) && Completed
) {
213 Token
->BufferSize
= Start
+ DataLen
;
216 } else if (Instance
->LastBlock
!= 0) {
218 // Don't save the data if the buffer is too small, return
219 // EFI_BUFFER_TOO_SMALL if received the last packet. This
220 // will give a accurate file length.
222 Token
->BufferSize
= Start
+ DataLen
;
226 EFI_MTFTP4_ERRORCODE_DISK_FULL
,
227 (UINT8
*) "User provided memory block is too small"
230 return EFI_BUFFER_TOO_SMALL
;
239 Function to process the received data packets.
241 It will save the block then send back an ACK if it is active.
243 @param Instance The downloading MTFTP session
244 @param Packet The packet received
245 @param Len The length of the packet
246 @param Multicast Whether this packet is multicast or unicast
247 @param Completed Return whether the download has completed
249 @retval EFI_SUCCESS The data packet is successfully processed
250 @retval EFI_ABORTED The download is aborted by the user
251 @retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small
255 Mtftp4RrqHandleData (
256 IN MTFTP4_PROTOCOL
*Instance
,
257 IN EFI_MTFTP4_PACKET
*Packet
,
259 IN BOOLEAN Multicast
,
260 OUT BOOLEAN
*Completed
268 Status
= EFI_SUCCESS
;
269 BlockNum
= NTOHS (Packet
->Data
.Block
);
270 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
272 ASSERT (Expected
>= 0);
275 // If we are active (Master) and received an unexpected packet, transmit
276 // the ACK for the block we received, then restart receiving the
277 // expected one. If we are passive (Slave), save the block.
279 if (Instance
->Master
&& (Expected
!= BlockNum
)) {
281 // If Expected is 0, (UINT16) (Expected - 1) is also the expected Ack number (65535).
283 return Mtftp4RrqSendAck (Instance
, (UINT16
) (Expected
- 1));
286 Status
= Mtftp4RrqSaveBlock (Instance
, Packet
, Len
);
288 if (EFI_ERROR (Status
)) {
293 // Record the total received and saved block number.
295 Instance
->TotalBlock
++;
298 // Reset the passive client's timer whenever it received a
299 // valid data packet.
301 if (!Instance
->Master
) {
302 Mtftp4SetTimeout (Instance
);
306 // Check whether we have received all the blocks. Send the ACK if we
307 // are active (unicast client or master client for multicast download).
308 // If we have received all the blocks, send an ACK even if we are passive
309 // to tell the server that we are done.
311 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
313 if (Instance
->Master
|| (Expected
< 0)) {
316 // If we are passive client, then the just received Block maybe
317 // isn't the last block. We need to send an ACK to the last block
318 // to inform the server that we are done. If we are active client,
319 // the Block == Instance->LastBlock.
321 BlockNum
= Instance
->LastBlock
;
325 BlockNum
= (UINT16
) (Expected
- 1);
328 if (Instance
->WindowSize
== (Instance
->TotalBlock
- Instance
->AckedBlock
) || Expected
< 0) {
329 Status
= Mtftp4RrqSendAck (Instance
, BlockNum
);
339 Validate whether the options received in the server's OACK packet is valid.
341 The options are valid only if:
342 1. The server doesn't include options not requested by us
343 2. The server can only use smaller blksize than that is requested
344 3. The server can only use the same timeout as requested
345 4. The server doesn't change its multicast channel.
347 @param This The downloading Mtftp session
348 @param Reply The options in the OACK packet
349 @param Request The requested options
351 @retval TRUE The options in the OACK is OK.
352 @retval FALSE The options in the OACK is invalid.
357 IN MTFTP4_PROTOCOL
*This
,
358 IN MTFTP4_OPTION
*Reply
,
359 IN MTFTP4_OPTION
*Request
364 // It is invalid for server to return options we don't request
366 if ((Reply
->Exist
&~Request
->Exist
) != 0) {
371 // Server can only specify a smaller block size and window size to be used and
372 // return the timeout matches that requested.
374 if ((((Reply
->Exist
& MTFTP4_BLKSIZE_EXIST
) != 0)&& (Reply
->BlkSize
> Request
->BlkSize
)) ||
375 (((Reply
->Exist
& MTFTP4_WINDOWSIZE_EXIST
) != 0)&& (Reply
->WindowSize
> Request
->WindowSize
)) ||
376 (((Reply
->Exist
& MTFTP4_TIMEOUT_EXIST
) != 0) && (Reply
->Timeout
!= Request
->Timeout
))
382 // The server can send ",,master" to client to change its master
383 // setting. But if it use the specific multicast channel, it can't
384 // change the setting.
386 if (((Reply
->Exist
& MTFTP4_MCAST_EXIST
) != 0) && (This
->McastIp
!= 0)) {
387 if ((Reply
->McastIp
!= 0) && (Reply
->McastIp
!= This
->McastIp
)) {
391 if ((Reply
->McastPort
!= 0) && (Reply
->McastPort
!= This
->McastPort
)) {
401 Configure a UDP IO port to receive the multicast.
403 @param McastIo The UDP IO to configure
404 @param Context The opaque parameter to the function which is the
407 @retval EFI_SUCCESS The UDP child is successfully configured.
408 @retval Others Failed to configure the UDP child.
413 Mtftp4RrqConfigMcastPort (
418 MTFTP4_PROTOCOL
*Instance
;
419 EFI_MTFTP4_CONFIG_DATA
*Config
;
420 EFI_UDP4_CONFIG_DATA UdpConfig
;
421 EFI_IPv4_ADDRESS Group
;
425 Instance
= (MTFTP4_PROTOCOL
*) Context
;
426 Config
= &Instance
->Config
;
428 UdpConfig
.AcceptBroadcast
= FALSE
;
429 UdpConfig
.AcceptPromiscuous
= FALSE
;
430 UdpConfig
.AcceptAnyPort
= FALSE
;
431 UdpConfig
.AllowDuplicatePort
= FALSE
;
432 UdpConfig
.TypeOfService
= 0;
433 UdpConfig
.TimeToLive
= 64;
434 UdpConfig
.DoNotFragment
= FALSE
;
435 UdpConfig
.ReceiveTimeout
= 0;
436 UdpConfig
.TransmitTimeout
= 0;
437 UdpConfig
.UseDefaultAddress
= Config
->UseDefaultSetting
;
438 IP4_COPY_ADDRESS (&UdpConfig
.StationAddress
, &Config
->StationIp
);
439 IP4_COPY_ADDRESS (&UdpConfig
.SubnetMask
, &Config
->SubnetMask
);
440 UdpConfig
.StationPort
= Instance
->McastPort
;
441 UdpConfig
.RemotePort
= 0;
443 Ip
= HTONL (Instance
->ServerIp
);
444 IP4_COPY_ADDRESS (&UdpConfig
.RemoteAddress
, &Ip
);
446 Status
= McastIo
->Protocol
.Udp4
->Configure (McastIo
->Protocol
.Udp4
, &UdpConfig
);
448 if (EFI_ERROR (Status
)) {
452 if (!Config
->UseDefaultSetting
&&
453 !EFI_IP4_EQUAL (&mZeroIp4Addr
, &Config
->GatewayIp
)) {
455 // The station IP address is manually configured and the Gateway IP is not 0.
456 // Add the default route for this UDP instance.
458 Status
= McastIo
->Protocol
.Udp4
->Routes (
459 McastIo
->Protocol
.Udp4
,
466 if (EFI_ERROR (Status
)) {
467 McastIo
->Protocol
.Udp4
->Configure (McastIo
->Protocol
.Udp4
, NULL
);
473 // join the multicast group
475 Ip
= HTONL (Instance
->McastIp
);
476 IP4_COPY_ADDRESS (&Group
, &Ip
);
478 return McastIo
->Protocol
.Udp4
->Groups (McastIo
->Protocol
.Udp4
, TRUE
, &Group
);
483 Function to process the OACK.
485 It will first validate the OACK packet, then update the various negotiated parameters.
487 @param Instance The download MTFTP session
488 @param Packet The packet received
489 @param Len The packet length
490 @param Multicast Whether this packet is received as a multicast
491 @param Completed Returns whether the download has completed. NOT
492 used by this function.
494 @retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child
495 @retval EFI_TFTP_ERROR Some error happened during the process
496 @retval EFI_SUCCESS The OACK is successfully processed.
500 Mtftp4RrqHandleOack (
501 IN OUT MTFTP4_PROTOCOL
*Instance
,
502 IN EFI_MTFTP4_PACKET
*Packet
,
504 IN BOOLEAN Multicast
,
505 OUT BOOLEAN
*Completed
511 EFI_UDP4_PROTOCOL
*Udp4
;
516 // If already started the master download, don't change the
517 // setting. Master download always succeeds.
519 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
520 ASSERT (Expected
!= -1);
522 if (Instance
->Master
&& (Expected
!= 1)) {
527 // Parse and validate the options from server
529 ZeroMem (&Reply
, sizeof (MTFTP4_OPTION
));
531 Status
= Mtftp4ParseOptionOack (Packet
, Len
, Instance
->Operation
, &Reply
);
533 if (EFI_ERROR (Status
) ||
534 !Mtftp4RrqOackValid (Instance
, &Reply
, &Instance
->RequestOption
)) {
536 // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
538 if (Status
!= EFI_OUT_OF_RESOURCES
) {
541 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
542 (UINT8
*) "Mal-formated OACK packet"
546 return EFI_TFTP_ERROR
;
549 if ((Reply
.Exist
& MTFTP4_MCAST_EXIST
) != 0) {
552 // Save the multicast info. Always update the Master, only update the
553 // multicast IP address, block size, window size, timeoute at the first time. If IP
554 // address is updated, create a UDP child to receive the multicast.
556 Instance
->Master
= Reply
.Master
;
558 if (Instance
->McastIp
== 0) {
559 if ((Reply
.McastIp
== 0) || (Reply
.McastPort
== 0)) {
562 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
563 (UINT8
*) "Illegal multicast setting"
566 return EFI_TFTP_ERROR
;
570 // Create a UDP child then start receive the multicast from it.
572 Instance
->McastIp
= Reply
.McastIp
;
573 Instance
->McastPort
= Reply
.McastPort
;
574 if (Instance
->McastUdpPort
== NULL
) {
575 Instance
->McastUdpPort
= UdpIoCreateIo (
576 Instance
->Service
->Controller
,
577 Instance
->Service
->Image
,
578 Mtftp4RrqConfigMcastPort
,
582 if (Instance
->McastUdpPort
!= NULL
) {
583 Status
= gBS
->OpenProtocol (
584 Instance
->McastUdpPort
->UdpHandle
,
585 &gEfiUdp4ProtocolGuid
,
587 Instance
->Service
->Image
,
589 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
591 if (EFI_ERROR (Status
)) {
592 UdpIoFreeIo (Instance
->McastUdpPort
);
593 Instance
->McastUdpPort
= NULL
;
594 return EFI_DEVICE_ERROR
;
600 if (Instance
->McastUdpPort
== NULL
) {
601 return EFI_DEVICE_ERROR
;
604 Status
= UdpIoRecvDatagram (Instance
->McastUdpPort
, Mtftp4RrqInput
, Instance
, 0);
606 if (EFI_ERROR (Status
)) {
609 EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION
,
610 (UINT8
*) "Failed to create socket to receive multicast packet"
617 // Update the parameters used.
619 if (Reply
.BlkSize
!= 0) {
620 Instance
->BlkSize
= Reply
.BlkSize
;
623 if (Reply
.WindowSize
!= 0) {
624 Instance
->WindowSize
= Reply
.WindowSize
;
627 if (Reply
.Timeout
!= 0) {
628 Instance
->Timeout
= Reply
.Timeout
;
633 Instance
->Master
= TRUE
;
635 if (Reply
.BlkSize
!= 0) {
636 Instance
->BlkSize
= Reply
.BlkSize
;
639 if (Reply
.WindowSize
!= 0) {
640 Instance
->WindowSize
= Reply
.WindowSize
;
643 if (Reply
.Timeout
!= 0) {
644 Instance
->Timeout
= Reply
.Timeout
;
649 // Send an ACK to (Expected - 1) which is 0 for unicast download,
650 // or tell the server we want to receive the Expected block.
652 return Mtftp4RrqSendAck (Instance
, (UINT16
) (Expected
- 1));
657 The packet process callback for MTFTP download.
659 @param UdpPacket The packet received
660 @param EndPoint The local/remote access point of the packet
661 @param IoStatus The status of the receiving
662 @param Context Opaque parameter, which is the MTFTP session
668 IN NET_BUF
*UdpPacket
,
669 IN UDP_END_POINT
*EndPoint
,
670 IN EFI_STATUS IoStatus
,
674 MTFTP4_PROTOCOL
*Instance
;
675 EFI_MTFTP4_PACKET
*Packet
;
682 Instance
= (MTFTP4_PROTOCOL
*) Context
;
683 NET_CHECK_SIGNATURE (Instance
, MTFTP4_PROTOCOL_SIGNATURE
);
685 Status
= EFI_SUCCESS
;
690 if (EFI_ERROR (IoStatus
)) {
695 ASSERT (UdpPacket
!= NULL
);
698 // Find the port this packet is from to restart receive correctly.
700 Multicast
= (BOOLEAN
) (EndPoint
->LocalAddr
.Addr
[0] == Instance
->McastIp
);
702 if (UdpPacket
->TotalSize
< MTFTP4_OPCODE_LEN
) {
707 // Client send initial request to server's listening port. Server
708 // will select a UDP port to communicate with the client. The server
709 // is required to use the same port as RemotePort to multicast the
712 if (EndPoint
->RemotePort
!= Instance
->ConnectedPort
) {
713 if (Instance
->ConnectedPort
!= 0) {
716 Instance
->ConnectedPort
= EndPoint
->RemotePort
;
721 // Copy the MTFTP packet to a continuous buffer if it isn't already so.
723 Len
= UdpPacket
->TotalSize
;
725 if (UdpPacket
->BlockOpNum
> 1) {
726 Packet
= AllocatePool (Len
);
728 if (Packet
== NULL
) {
729 Status
= EFI_OUT_OF_RESOURCES
;
733 NetbufCopy (UdpPacket
, 0, Len
, (UINT8
*) Packet
);
736 Packet
= (EFI_MTFTP4_PACKET
*) NetbufGetByte (UdpPacket
, 0, NULL
);
737 ASSERT (Packet
!= NULL
);
740 Opcode
= NTOHS (Packet
->OpCode
);
743 // Call the user's CheckPacket if provided. Abort the transmission
744 // if CheckPacket returns an EFI_ERROR code.
746 if ((Instance
->Token
->CheckPacket
!= NULL
) &&
747 ((Opcode
== EFI_MTFTP4_OPCODE_OACK
) || (Opcode
== EFI_MTFTP4_OPCODE_ERROR
))) {
749 Status
= Instance
->Token
->CheckPacket (
756 if (EFI_ERROR (Status
)) {
758 // Send an error message to the server to inform it
760 if (Opcode
!= EFI_MTFTP4_OPCODE_ERROR
) {
763 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED
,
764 (UINT8
*) "User aborted the transfer"
768 Status
= EFI_ABORTED
;
774 case EFI_MTFTP4_OPCODE_DATA
:
775 if ((Len
> (UINT32
) (MTFTP4_DATA_HEAD_LEN
+ Instance
->BlkSize
)) ||
776 (Len
< (UINT32
) MTFTP4_DATA_HEAD_LEN
)) {
780 Status
= Mtftp4RrqHandleData (Instance
, Packet
, Len
, Multicast
, &Completed
);
783 case EFI_MTFTP4_OPCODE_OACK
:
784 if (Multicast
|| (Len
<= MTFTP4_OPCODE_LEN
)) {
788 Status
= Mtftp4RrqHandleOack (Instance
, Packet
, Len
, Multicast
, &Completed
);
791 case EFI_MTFTP4_OPCODE_ERROR
:
792 Status
= EFI_TFTP_ERROR
;
802 // Free the resources, then if !EFI_ERROR (Status), restart the
803 // receive, otherwise end the session.
805 if ((Packet
!= NULL
) && (UdpPacket
->BlockOpNum
> 1)) {
809 if (UdpPacket
!= NULL
) {
810 NetbufFree (UdpPacket
);
813 if (!EFI_ERROR (Status
) && !Completed
) {
815 Status
= UdpIoRecvDatagram (Instance
->McastUdpPort
, Mtftp4RrqInput
, Instance
, 0);
817 Status
= UdpIoRecvDatagram (Instance
->UnicastPort
, Mtftp4RrqInput
, Instance
, 0);
821 if (EFI_ERROR (Status
) || Completed
) {
822 Mtftp4CleanOperation (Instance
, Status
);