3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Routines to process Rrq (download)
23 #include "Mtftp4Impl.h"
27 IN NET_BUF
*UdpPacket
,
28 IN UDP_POINTS
*Points
,
29 IN EFI_STATUS IoStatus
,
35 Start the MTFTP session to download. It will first initialize some
36 of the internal states then build and send a RRQ reqeuest packet, at
37 last, it will start receive for the downloading.
39 @param Instance The Mtftp session
40 @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
41 or EFI_MTFTP4_OPCODE_DIR.
43 @retval EFI_SUCCESS The mtftp download session is started.
44 @retval Others Failed to start downloading.
49 IN MTFTP4_PROTOCOL
*Instance
,
56 // The valid block number range are [1, 0xffff]. For example:
57 // the client sends an RRQ request to the server, the server
58 // transfers the DATA1 block. If option negoitation is ongoing,
59 // the server will send back an OACK, then client will send ACK0.
61 Status
= Mtftp4InitBlockRange (&Instance
->Blocks
, 1, 0xffff);
63 if (EFI_ERROR (Status
)) {
67 Status
= Mtftp4SendRequest (Instance
);
69 if (EFI_ERROR (Status
)) {
73 return UdpIoRecvDatagram (Instance
->UnicastPort
, Mtftp4RrqInput
, Instance
, 0);
78 Build and send a ACK packet for the download session.
80 @param Instance The Mtftp session
81 @param BlkNo The BlkNo to ack.
83 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
84 @retval EFI_SUCCESS The ACK has been sent
85 @retval Others Failed to send the ACK.
90 IN MTFTP4_PROTOCOL
*Instance
,
94 EFI_MTFTP4_PACKET
*Ack
;
97 Packet
= NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER
));
100 return EFI_OUT_OF_RESOURCES
;
103 Ack
= (EFI_MTFTP4_PACKET
*) NetbufAllocSpace (
105 sizeof (EFI_MTFTP4_ACK_HEADER
),
109 Ack
->Ack
.OpCode
= HTONS (EFI_MTFTP4_OPCODE_ACK
);
110 Ack
->Ack
.Block
[0] = HTONS (BlkNo
);
112 return Mtftp4SendPacket (Instance
, Packet
);
117 Deliver the received data block to the user, which can be saved
118 in the user provide buffer or through the CheckPacket callback.
120 @param Instance The Mtftp session
121 @param Packet The received data packet
122 @param Len The packet length
124 @retval EFI_SUCCESS The data is saved successfully
125 @retval EFI_ABORTED The user tells to abort by return an error through
127 @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is
128 updated to the actual buffer size needed.
133 IN MTFTP4_PROTOCOL
*Instance
,
134 IN EFI_MTFTP4_PACKET
*Packet
,
138 EFI_MTFTP4_TOKEN
*Token
;
144 Token
= Instance
->Token
;
145 Block
= NTOHS (Packet
->Data
.Block
);
146 DataLen
= Len
- MTFTP4_DATA_HEAD_LEN
;
149 // This is the last block, save the block no
151 if (DataLen
< Instance
->BlkSize
) {
152 Instance
->LastBlock
= Block
;
153 Mtftp4SetLastBlockNum (&Instance
->Blocks
, Block
);
157 // Remove this block number from the file hole. If Mtftp4RemoveBlockNum
158 // returns EFI_NOT_FOUND, the block has been saved, don't save it again.
160 Status
= Mtftp4RemoveBlockNum (&Instance
->Blocks
, Block
);
162 if (Status
== EFI_NOT_FOUND
) {
164 } else if (EFI_ERROR (Status
)) {
168 if (Token
->CheckPacket
!= NULL
) {
169 Status
= Token
->CheckPacket (&Instance
->Mtftp4
, Token
, (UINT16
) Len
, Packet
);
171 if (EFI_ERROR (Status
)) {
174 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
175 (UINT8
*) "User aborted download"
182 if (Token
->Buffer
!= NULL
) {
183 Start
= MultU64x32 (Block
- 1, Instance
->BlkSize
);
185 if (Start
+ DataLen
<= Token
->BufferSize
) {
186 NetCopyMem ((UINT8
*) Token
->Buffer
+ Start
, Packet
->Data
.Data
, DataLen
);
189 // Update the file size when received the last block
191 if (Instance
->LastBlock
== Block
) {
192 Token
->BufferSize
= Start
+ DataLen
;
195 } else if (Instance
->LastBlock
!= 0) {
197 // Don't save the data if the buffer is too small, return
198 // EFI_BUFFER_TOO_SMALL if received the last packet. This
199 // will give a accurate file length.
201 Token
->BufferSize
= Start
+ DataLen
;
205 EFI_MTFTP4_ERRORCODE_DISK_FULL
,
206 (UINT8
*) "User provided memory block is too small"
209 return EFI_BUFFER_TOO_SMALL
;
218 Function to process the received data packets. It will save the block
219 then send back an ACK if it is active.
221 @param Instance The downloading MTFTP session
222 @param Packet The packet received
223 @param Len The length of the packet
224 @param Multicast Whether this packet is multicast or unicast
225 @param Completed Return whether the download has completed
227 @retval EFI_SUCCESS The data packet is successfully processed
228 @retval EFI_ABORTED The download is aborted by the user
229 @retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small
233 Mtftp4RrqHandleData (
234 IN MTFTP4_PROTOCOL
*Instance
,
235 IN EFI_MTFTP4_PACKET
*Packet
,
237 IN BOOLEAN Multicast
,
238 OUT BOOLEAN
*Completed
246 BlockNum
= NTOHS (Packet
->Data
.Block
);
247 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
249 ASSERT (Expected
>= 0);
252 // If we are active and received an unexpected packet, retransmit
253 // the last ACK then restart receiving. If we are passive, save
256 if (Instance
->Master
&& (Expected
!= BlockNum
)) {
257 Mtftp4Retransmit (Instance
);
261 Status
= Mtftp4RrqSaveBlock (Instance
, Packet
, Len
);
263 if (EFI_ERROR (Status
)) {
268 // Reset the passive client's timer whenever it received a
269 // valid data packet.
271 if (!Instance
->Master
) {
272 Mtftp4SetTimeout (Instance
);
276 // Check whether we have received all the blocks. Send the ACK if we
277 // are active (unicast client or master client for multicast download).
278 // If we have received all the blocks, send an ACK even if we are passive
279 // to tell the server that we are done.
281 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
283 if (Instance
->Master
|| (Expected
< 0)) {
286 // If we are passive client, then the just received Block maybe
287 // isn't the last block. We need to send an ACK to the last block
288 // to inform the server that we are done. If we are active client,
289 // the Block == Instance->LastBlock.
291 BlockNum
= Instance
->LastBlock
;
295 BlockNum
= (UINT16
) (Expected
- 1);
298 Mtftp4RrqSendAck (Instance
, BlockNum
);
306 Validate whether the options received in the server's OACK packet is valid.
307 The options are valid only if:
308 1. The server doesn't include options not requested by us
309 2. The server can only use smaller blksize than that is requested
310 3. The server can only use the same timeout as requested
311 4. The server doesn't change its multicast channel.
313 @param This The downloading Mtftp session
314 @param Reply The options in the OACK packet
315 @param Request The requested options
317 @return TRUE if the options in the OACK is OK, otherwise FALSE.
322 IN MTFTP4_PROTOCOL
*This
,
323 IN MTFTP4_OPTION
*Reply
,
324 IN MTFTP4_OPTION
*Request
329 // It is invalid for server to return options we don't request
331 if ((Reply
->Exist
&~Request
->Exist
) != 0) {
336 // Server can only specify a smaller block size to be used and
337 // return the timeout matches that requested.
339 if (((Reply
->Exist
& MTFTP4_BLKSIZE_EXIST
) && (Reply
->BlkSize
> Request
->BlkSize
)) ||
340 ((Reply
->Exist
& MTFTP4_TIMEOUT_EXIST
) && (Reply
->Timeout
!= Request
->Timeout
))) {
345 // The server can send ",,master" to client to change its master
346 // setting. But if it use the specific multicast channel, it can't
347 // change the setting.
349 if ((Reply
->Exist
& MTFTP4_MCAST_EXIST
) && (This
->McastIp
!= 0)) {
350 if ((Reply
->McastIp
!= 0) && (Reply
->McastIp
!= This
->McastIp
)) {
354 if ((Reply
->McastPort
!= 0) && (Reply
->McastPort
!= This
->McastPort
)) {
364 Configure a UDP IO port to receive the multicast.
366 @param McastIo The UDP IO port to configure
367 @param Context The opaque parameter to the function which is the
370 @retval EFI_SUCCESS The udp child is successfully configured.
371 @retval Others Failed to configure the UDP child.
376 Mtftp4RrqConfigMcastPort (
377 IN UDP_IO_PORT
*McastIo
,
381 MTFTP4_PROTOCOL
*Instance
;
382 EFI_MTFTP4_CONFIG_DATA
*Config
;
383 EFI_UDP4_CONFIG_DATA UdpConfig
;
384 EFI_IPv4_ADDRESS Group
;
388 Instance
= (MTFTP4_PROTOCOL
*) Context
;
389 Config
= &Instance
->Config
;
391 UdpConfig
.AcceptBroadcast
= FALSE
;
392 UdpConfig
.AcceptPromiscuous
= FALSE
;
393 UdpConfig
.AcceptAnyPort
= FALSE
;
394 UdpConfig
.AllowDuplicatePort
= FALSE
;
395 UdpConfig
.TypeOfService
= 0;
396 UdpConfig
.TimeToLive
= 64;
397 UdpConfig
.DoNotFragment
= FALSE
;
398 UdpConfig
.ReceiveTimeout
= 0;
399 UdpConfig
.TransmitTimeout
= 0;
400 UdpConfig
.UseDefaultAddress
= Config
->UseDefaultSetting
;
401 UdpConfig
.StationAddress
= Config
->StationIp
;
402 UdpConfig
.SubnetMask
= Config
->SubnetMask
;
403 UdpConfig
.StationPort
= Instance
->McastPort
;
404 UdpConfig
.RemotePort
= 0;
406 Ip
= HTONL (Instance
->ServerIp
);
407 NetCopyMem (&UdpConfig
.RemoteAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
409 Status
= McastIo
->Udp
->Configure (McastIo
->Udp
, &UdpConfig
);
411 if (EFI_ERROR (Status
)) {
415 if (!Config
->UseDefaultSetting
&& !EFI_IP4_EQUAL (&mZeroIp4Addr
, &Config
->GatewayIp
)) {
417 // The station IP address is manually configured and the Gateway IP is not 0.
418 // Add the default route for this UDP instance.
420 Status
= McastIo
->Udp
->Routes (McastIo
->Udp
, FALSE
, &mZeroIp4Addr
, &mZeroIp4Addr
, &Config
->GatewayIp
);
421 if (EFI_ERROR (Status
)) {
422 McastIo
->Udp
->Configure (McastIo
->Udp
, NULL
);
428 // join the multicast group
430 Ip
= HTONL (Instance
->McastIp
);
431 NetCopyMem (&Group
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
433 return McastIo
->Udp
->Groups (McastIo
->Udp
, TRUE
, &Group
);
438 Function to process the OACK. It will first validate the OACK
439 packet, then update the various negotiated parameters.
441 @param Instance The download MTFTP session
442 @param Packet The packet received
443 @param Len The packet length
444 @param Multicast Whether this packet is received as a multicast
445 @param Completed Returns whether the download has completed. NOT
446 used by this function.
448 @retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child
449 @retval EFI_TFTP_ERROR Some error happened during the process
450 @retval EFI_SUCCESS The OACK is successfully processed.
454 Mtftp4RrqHandleOack (
455 IN MTFTP4_PROTOCOL
*Instance
,
456 IN EFI_MTFTP4_PACKET
*Packet
,
458 IN BOOLEAN Multicast
,
459 OUT BOOLEAN
*Completed
469 // If already started the master download, don't change the
470 // setting. Master download always succeeds.
472 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
473 ASSERT (Expected
!= -1);
475 if (Instance
->Master
&& (Expected
!= 1)) {
480 // Parse and validate the options from server
482 NetZeroMem (&Reply
, sizeof (MTFTP4_OPTION
));
484 Status
= Mtftp4ParseOptionOack (Packet
, Len
, &Reply
);
486 if (EFI_ERROR (Status
) ||
487 !Mtftp4RrqOackValid (Instance
, &Reply
, &Instance
->RequestOption
)) {
489 // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
491 if (Status
!= EFI_OUT_OF_RESOURCES
) {
494 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
495 (UINT8
*) "Mal-formated OACK packet"
499 return EFI_TFTP_ERROR
;
502 if (Reply
.Exist
& MTFTP4_MCAST_EXIST
) {
505 // Save the multicast info. Always update the Master, only update the
506 // multicast IP address, block size, timeoute at the first time. If IP
507 // address is updated, create a UDP child to receive the multicast.
509 Instance
->Master
= Reply
.Master
;
511 if (Instance
->McastIp
== 0) {
512 if ((Reply
.McastIp
== 0) || (Reply
.McastPort
== 0)) {
515 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
516 (UINT8
*) "Illegal multicast setting"
519 return EFI_TFTP_ERROR
;
523 // Create a UDP child then start receive the multicast from it.
525 Instance
->McastIp
= Reply
.McastIp
;
526 Instance
->McastPort
= Reply
.McastPort
;
527 Instance
->McastUdpPort
= UdpIoCreatePort (
528 Instance
->Service
->Controller
,
529 Instance
->Service
->Image
,
530 Mtftp4RrqConfigMcastPort
,
534 if (Instance
->McastUdpPort
== NULL
) {
535 return EFI_DEVICE_ERROR
;
538 Status
= UdpIoRecvDatagram (Instance
->McastUdpPort
, Mtftp4RrqInput
, Instance
, 0);
540 if (EFI_ERROR (Status
)) {
543 EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION
,
544 (UINT8
*) "Failed to create socket to receive multicast packet"
551 // Update the parameters used.
553 if (Reply
.BlkSize
!= 0) {
554 Instance
->BlkSize
= Reply
.BlkSize
;
557 if (Reply
.Timeout
!= 0) {
558 Instance
->Timeout
= Reply
.Timeout
;
563 Instance
->Master
= TRUE
;
565 if (Reply
.BlkSize
!= 0) {
566 Instance
->BlkSize
= Reply
.BlkSize
;
569 if (Reply
.Timeout
!= 0) {
570 Instance
->Timeout
= Reply
.Timeout
;
575 // Send an ACK to (Expected - 1) which is 0 for unicast download,
576 // or tell the server we want to receive the Expected block.
578 return Mtftp4RrqSendAck (Instance
, (UINT16
) (Expected
- 1));
583 The packet process callback for MTFTP download.
585 @param UdpPacket The packet received
586 @param Points The local/remote access point of the packet
587 @param IoStatus The status of the receiving
588 @param Context Opaque parameter, which is the MTFTP session
595 IN NET_BUF
*UdpPacket
,
596 IN UDP_POINTS
*Points
,
597 IN EFI_STATUS IoStatus
,
601 MTFTP4_PROTOCOL
*Instance
;
602 EFI_MTFTP4_PACKET
*Packet
;
609 Instance
= (MTFTP4_PROTOCOL
*) Context
;
610 NET_CHECK_SIGNATURE (Instance
, MTFTP4_PROTOCOL_SIGNATURE
);
612 Status
= EFI_SUCCESS
;
617 if (EFI_ERROR (IoStatus
)) {
622 ASSERT (UdpPacket
!= NULL
);
625 // Find the port this packet is from to restart receive correctly.
627 Multicast
= (BOOLEAN
) (Points
->LocalAddr
== Instance
->McastIp
);
629 if (UdpPacket
->TotalSize
< MTFTP4_OPCODE_LEN
) {
634 // Client send initial request to server's listening port. Server
635 // will select a UDP port to communicate with the client. The server
636 // is required to use the same port as RemotePort to multicast the
639 if (Points
->RemotePort
!= Instance
->ConnectedPort
) {
640 if (Instance
->ConnectedPort
!= 0) {
643 Instance
->ConnectedPort
= Points
->RemotePort
;
648 // Copy the MTFTP packet to a continuous buffer if it isn't already so.
650 Len
= UdpPacket
->TotalSize
;
652 if (UdpPacket
->BlockOpNum
> 1) {
653 Packet
= NetAllocatePool (Len
);
655 if (Packet
== NULL
) {
656 Status
= EFI_OUT_OF_RESOURCES
;
660 NetbufCopy (UdpPacket
, 0, Len
, (UINT8
*) Packet
);
663 Packet
= (EFI_MTFTP4_PACKET
*) NetbufGetByte (UdpPacket
, 0, NULL
);
666 Opcode
= NTOHS (Packet
->OpCode
);
669 // Call the user's CheckPacket if provided. Abort the transmission
670 // if CheckPacket returns an EFI_ERROR code.
672 if ((Instance
->Token
->CheckPacket
!= NULL
) &&
673 ((Opcode
== EFI_MTFTP4_OPCODE_OACK
) || (Opcode
== EFI_MTFTP4_OPCODE_ERROR
))) {
675 Status
= Instance
->Token
->CheckPacket (
682 if (EFI_ERROR (Status
)) {
684 // Send an error message to the server to inform it
686 if (Opcode
!= EFI_MTFTP4_OPCODE_ERROR
) {
689 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED
,
690 (UINT8
*) "User aborted the transfer"
694 Status
= EFI_ABORTED
;
700 case EFI_MTFTP4_OPCODE_DATA
:
701 if ((Len
> (UINT32
) (MTFTP4_DATA_HEAD_LEN
+ Instance
->BlkSize
)) ||
702 (Len
< (UINT32
) MTFTP4_DATA_HEAD_LEN
)) {
706 Status
= Mtftp4RrqHandleData (Instance
, Packet
, Len
, Multicast
, &Completed
);
709 case EFI_MTFTP4_OPCODE_OACK
:
710 if (Multicast
|| (Len
<= MTFTP4_OPCODE_LEN
)) {
714 Status
= Mtftp4RrqHandleOack (Instance
, Packet
, Len
, Multicast
, &Completed
);
717 case EFI_MTFTP4_OPCODE_ERROR
:
718 Status
= EFI_TFTP_ERROR
;
725 // Free the resources, then if !EFI_ERROR (Status), restart the
726 // receive, otherwise end the session.
728 if ((Packet
!= NULL
) && (UdpPacket
->BlockOpNum
> 1)) {
729 NetFreePool (Packet
);
732 if (UdpPacket
!= NULL
) {
733 NetbufFree (UdpPacket
);
736 if (!EFI_ERROR (Status
) && !Completed
) {
738 Status
= UdpIoRecvDatagram (Instance
->McastUdpPort
, Mtftp4RrqInput
, Instance
, 0);
740 Status
= UdpIoRecvDatagram (Instance
->UnicastPort
, Mtftp4RrqInput
, Instance
, 0);
744 if (EFI_ERROR (Status
) || Completed
) {
745 Mtftp4CleanOperation (Instance
, Status
);