2 Routines to process Rrq (download).
4 Copyright (c) 2006 - 2009, Intel Corporation<BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php<BR>
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "Mtftp4Impl.h"
20 The packet process callback for MTFTP download.
22 @param UdpPacket The packet received
23 @param EndPoint The local/remote access point of the packet
24 @param IoStatus The status of the receiving
25 @param Context Opaque parameter, which is the MTFTP session
31 IN NET_BUF
*UdpPacket
,
32 IN UDP_END_POINT
*EndPoint
,
33 IN EFI_STATUS IoStatus
,
39 Start the MTFTP session to download.
41 It will first initialize some of the internal states then build and send a RRQ
42 reqeuest packet, at last, it will start receive for the downloading.
44 @param Instance The Mtftp session
45 @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
46 or EFI_MTFTP4_OPCODE_DIR.
48 @retval EFI_SUCCESS The mtftp download session is started.
49 @retval Others Failed to start downloading.
54 IN MTFTP4_PROTOCOL
*Instance
,
61 // The valid block number range are [1, 0xffff]. For example:
62 // the client sends an RRQ request to the server, the server
63 // transfers the DATA1 block. If option negoitation is ongoing,
64 // the server will send back an OACK, then client will send ACK0.
66 Status
= Mtftp4InitBlockRange (&Instance
->Blocks
, 1, 0xffff);
68 if (EFI_ERROR (Status
)) {
72 Status
= Mtftp4SendRequest (Instance
);
74 if (EFI_ERROR (Status
)) {
78 return UdpIoRecvDatagram (Instance
->UnicastPort
, Mtftp4RrqInput
, Instance
, 0);
83 Build and send a ACK packet for the download session.
85 @param Instance The Mtftp session
86 @param BlkNo The BlkNo to ack.
88 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
89 @retval EFI_SUCCESS The ACK has been sent
90 @retval Others Failed to send the ACK.
95 IN MTFTP4_PROTOCOL
*Instance
,
99 EFI_MTFTP4_PACKET
*Ack
;
102 Packet
= NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER
));
103 if (Packet
== NULL
) {
104 return EFI_OUT_OF_RESOURCES
;
107 Ack
= (EFI_MTFTP4_PACKET
*) NetbufAllocSpace (
109 sizeof (EFI_MTFTP4_ACK_HEADER
),
112 ASSERT (Ack
!= NULL
);
114 Ack
->Ack
.OpCode
= HTONS (EFI_MTFTP4_OPCODE_ACK
);
115 Ack
->Ack
.Block
[0] = HTONS (BlkNo
);
117 return Mtftp4SendPacket (Instance
, Packet
);
122 Deliver the received data block to the user, which can be saved
123 in the user provide buffer or through the CheckPacket callback.
125 @param Instance The Mtftp session
126 @param Packet The received data packet
127 @param Len The packet length
129 @retval EFI_SUCCESS The data is saved successfully
130 @retval EFI_ABORTED The user tells to abort by return an error through
132 @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is
133 updated to the actual buffer size needed.
138 IN OUT MTFTP4_PROTOCOL
*Instance
,
139 IN EFI_MTFTP4_PACKET
*Packet
,
143 EFI_MTFTP4_TOKEN
*Token
;
149 Token
= Instance
->Token
;
150 Block
= NTOHS (Packet
->Data
.Block
);
151 DataLen
= Len
- MTFTP4_DATA_HEAD_LEN
;
154 // This is the last block, save the block no
156 if (DataLen
< Instance
->BlkSize
) {
157 Instance
->LastBlock
= Block
;
158 Mtftp4SetLastBlockNum (&Instance
->Blocks
, Block
);
162 // Remove this block number from the file hole. If Mtftp4RemoveBlockNum
163 // returns EFI_NOT_FOUND, the block has been saved, don't save it again.
165 Status
= Mtftp4RemoveBlockNum (&Instance
->Blocks
, Block
);
167 if (Status
== EFI_NOT_FOUND
) {
169 } else if (EFI_ERROR (Status
)) {
173 if (Token
->CheckPacket
!= NULL
) {
174 Status
= Token
->CheckPacket (&Instance
->Mtftp4
, Token
, (UINT16
) Len
, Packet
);
176 if (EFI_ERROR (Status
)) {
179 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
180 (UINT8
*) "User aborted download"
187 if (Token
->Buffer
!= NULL
) {
188 Start
= MultU64x32 (Block
- 1, Instance
->BlkSize
);
190 if (Start
+ DataLen
<= Token
->BufferSize
) {
191 CopyMem ((UINT8
*) Token
->Buffer
+ Start
, Packet
->Data
.Data
, DataLen
);
194 // Update the file size when received the last block
196 if (Instance
->LastBlock
== Block
) {
197 Token
->BufferSize
= Start
+ DataLen
;
200 } else if (Instance
->LastBlock
!= 0) {
202 // Don't save the data if the buffer is too small, return
203 // EFI_BUFFER_TOO_SMALL if received the last packet. This
204 // will give a accurate file length.
206 Token
->BufferSize
= Start
+ DataLen
;
210 EFI_MTFTP4_ERRORCODE_DISK_FULL
,
211 (UINT8
*) "User provided memory block is too small"
214 return EFI_BUFFER_TOO_SMALL
;
223 Function to process the received data packets.
225 It will save the block then send back an ACK if it is active.
227 @param Instance The downloading MTFTP session
228 @param Packet The packet received
229 @param Len The length of the packet
230 @param Multicast Whether this packet is multicast or unicast
231 @param Completed Return whether the download has completed
233 @retval EFI_SUCCESS The data packet is successfully processed
234 @retval EFI_ABORTED The download is aborted by the user
235 @retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small
239 Mtftp4RrqHandleData (
240 IN MTFTP4_PROTOCOL
*Instance
,
241 IN EFI_MTFTP4_PACKET
*Packet
,
243 IN BOOLEAN Multicast
,
244 OUT BOOLEAN
*Completed
252 BlockNum
= NTOHS (Packet
->Data
.Block
);
253 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
255 ASSERT (Expected
>= 0);
258 // If we are active and received an unexpected packet, retransmit
259 // the last ACK then restart receiving. If we are passive, save
262 if (Instance
->Master
&& (Expected
!= BlockNum
)) {
263 Mtftp4Retransmit (Instance
);
267 Status
= Mtftp4RrqSaveBlock (Instance
, Packet
, Len
);
269 if (EFI_ERROR (Status
)) {
274 // Reset the passive client's timer whenever it received a
275 // valid data packet.
277 if (!Instance
->Master
) {
278 Mtftp4SetTimeout (Instance
);
282 // Check whether we have received all the blocks. Send the ACK if we
283 // are active (unicast client or master client for multicast download).
284 // If we have received all the blocks, send an ACK even if we are passive
285 // to tell the server that we are done.
287 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
289 if (Instance
->Master
|| (Expected
< 0)) {
292 // If we are passive client, then the just received Block maybe
293 // isn't the last block. We need to send an ACK to the last block
294 // to inform the server that we are done. If we are active client,
295 // the Block == Instance->LastBlock.
297 BlockNum
= Instance
->LastBlock
;
301 BlockNum
= (UINT16
) (Expected
- 1);
304 Mtftp4RrqSendAck (Instance
, BlockNum
);
312 Validate whether the options received in the server's OACK packet is valid.
314 The options are valid only if:
315 1. The server doesn't include options not requested by us
316 2. The server can only use smaller blksize than that is requested
317 3. The server can only use the same timeout as requested
318 4. The server doesn't change its multicast channel.
320 @param This The downloading Mtftp session
321 @param Reply The options in the OACK packet
322 @param Request The requested options
324 @retval TRUE The options in the OACK is OK.
325 @retval FALSE The options in the OACK is invalid.
330 IN MTFTP4_PROTOCOL
*This
,
331 IN MTFTP4_OPTION
*Reply
,
332 IN MTFTP4_OPTION
*Request
337 // It is invalid for server to return options we don't request
339 if ((Reply
->Exist
&~Request
->Exist
) != 0) {
344 // Server can only specify a smaller block size to be used and
345 // return the timeout matches that requested.
347 if ((((Reply
->Exist
& MTFTP4_BLKSIZE_EXIST
) != 0)&& (Reply
->BlkSize
> Request
->BlkSize
)) ||
348 (((Reply
->Exist
& MTFTP4_TIMEOUT_EXIST
) != 0) && (Reply
->Timeout
!= Request
->Timeout
))) {
353 // The server can send ",,master" to client to change its master
354 // setting. But if it use the specific multicast channel, it can't
355 // change the setting.
357 if (((Reply
->Exist
& MTFTP4_MCAST_EXIST
) != 0) && (This
->McastIp
!= 0)) {
358 if ((Reply
->McastIp
!= 0) && (Reply
->McastIp
!= This
->McastIp
)) {
362 if ((Reply
->McastPort
!= 0) && (Reply
->McastPort
!= This
->McastPort
)) {
372 Configure a UDP IO port to receive the multicast.
374 @param McastIo The UDP IO to configure
375 @param Context The opaque parameter to the function which is the
378 @retval EFI_SUCCESS The UDP child is successfully configured.
379 @retval Others Failed to configure the UDP child.
384 Mtftp4RrqConfigMcastPort (
389 MTFTP4_PROTOCOL
*Instance
;
390 EFI_MTFTP4_CONFIG_DATA
*Config
;
391 EFI_UDP4_CONFIG_DATA UdpConfig
;
392 EFI_IPv4_ADDRESS Group
;
396 Instance
= (MTFTP4_PROTOCOL
*) Context
;
397 Config
= &Instance
->Config
;
399 UdpConfig
.AcceptBroadcast
= FALSE
;
400 UdpConfig
.AcceptPromiscuous
= FALSE
;
401 UdpConfig
.AcceptAnyPort
= FALSE
;
402 UdpConfig
.AllowDuplicatePort
= FALSE
;
403 UdpConfig
.TypeOfService
= 0;
404 UdpConfig
.TimeToLive
= 64;
405 UdpConfig
.DoNotFragment
= FALSE
;
406 UdpConfig
.ReceiveTimeout
= 0;
407 UdpConfig
.TransmitTimeout
= 0;
408 UdpConfig
.UseDefaultAddress
= Config
->UseDefaultSetting
;
409 UdpConfig
.StationAddress
= Config
->StationIp
;
410 UdpConfig
.SubnetMask
= Config
->SubnetMask
;
411 UdpConfig
.StationPort
= Instance
->McastPort
;
412 UdpConfig
.RemotePort
= 0;
414 Ip
= HTONL (Instance
->ServerIp
);
415 CopyMem (&UdpConfig
.RemoteAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
417 Status
= McastIo
->Protocol
.Udp4
->Configure (McastIo
->Protocol
.Udp4
, &UdpConfig
);
419 if (EFI_ERROR (Status
)) {
423 if (!Config
->UseDefaultSetting
&&
424 !EFI_IP4_EQUAL (&mZeroIp4Addr
, &Config
->GatewayIp
)) {
426 // The station IP address is manually configured and the Gateway IP is not 0.
427 // Add the default route for this UDP instance.
429 Status
= McastIo
->Protocol
.Udp4
->Routes (
430 McastIo
->Protocol
.Udp4
,
437 if (EFI_ERROR (Status
)) {
438 McastIo
->Protocol
.Udp4
->Configure (McastIo
->Protocol
.Udp4
, NULL
);
444 // join the multicast group
446 Ip
= HTONL (Instance
->McastIp
);
447 CopyMem (&Group
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
449 return McastIo
->Protocol
.Udp4
->Groups (McastIo
->Protocol
.Udp4
, TRUE
, &Group
);
454 Function to process the OACK.
456 It will first validate the OACK packet, then update the various negotiated parameters.
458 @param Instance The download MTFTP session
459 @param Packet The packet received
460 @param Len The packet length
461 @param Multicast Whether this packet is received as a multicast
462 @param Completed Returns whether the download has completed. NOT
463 used by this function.
465 @retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child
466 @retval EFI_TFTP_ERROR Some error happened during the process
467 @retval EFI_SUCCESS The OACK is successfully processed.
471 Mtftp4RrqHandleOack (
472 IN OUT MTFTP4_PROTOCOL
*Instance
,
473 IN EFI_MTFTP4_PACKET
*Packet
,
475 IN BOOLEAN Multicast
,
476 OUT BOOLEAN
*Completed
486 // If already started the master download, don't change the
487 // setting. Master download always succeeds.
489 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
490 ASSERT (Expected
!= -1);
492 if (Instance
->Master
&& (Expected
!= 1)) {
497 // Parse and validate the options from server
499 ZeroMem (&Reply
, sizeof (MTFTP4_OPTION
));
501 Status
= Mtftp4ParseOptionOack (Packet
, Len
, &Reply
);
503 if (EFI_ERROR (Status
) ||
504 !Mtftp4RrqOackValid (Instance
, &Reply
, &Instance
->RequestOption
)) {
506 // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
508 if (Status
!= EFI_OUT_OF_RESOURCES
) {
511 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
512 (UINT8
*) "Mal-formated OACK packet"
516 return EFI_TFTP_ERROR
;
519 if ((Reply
.Exist
& MTFTP4_MCAST_EXIST
) != 0) {
522 // Save the multicast info. Always update the Master, only update the
523 // multicast IP address, block size, timeoute at the first time. If IP
524 // address is updated, create a UDP child to receive the multicast.
526 Instance
->Master
= Reply
.Master
;
528 if (Instance
->McastIp
== 0) {
529 if ((Reply
.McastIp
== 0) || (Reply
.McastPort
== 0)) {
532 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
533 (UINT8
*) "Illegal multicast setting"
536 return EFI_TFTP_ERROR
;
540 // Create a UDP child then start receive the multicast from it.
542 Instance
->McastIp
= Reply
.McastIp
;
543 Instance
->McastPort
= Reply
.McastPort
;
544 Instance
->McastUdpPort
= UdpIoCreateIo (
545 Instance
->Service
->Controller
,
546 Instance
->Service
->Image
,
547 Mtftp4RrqConfigMcastPort
,
552 if (Instance
->McastUdpPort
== NULL
) {
553 return EFI_DEVICE_ERROR
;
556 Status
= UdpIoRecvDatagram (Instance
->McastUdpPort
, Mtftp4RrqInput
, Instance
, 0);
558 if (EFI_ERROR (Status
)) {
561 EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION
,
562 (UINT8
*) "Failed to create socket to receive multicast packet"
569 // Update the parameters used.
571 if (Reply
.BlkSize
!= 0) {
572 Instance
->BlkSize
= Reply
.BlkSize
;
575 if (Reply
.Timeout
!= 0) {
576 Instance
->Timeout
= Reply
.Timeout
;
581 Instance
->Master
= TRUE
;
583 if (Reply
.BlkSize
!= 0) {
584 Instance
->BlkSize
= Reply
.BlkSize
;
587 if (Reply
.Timeout
!= 0) {
588 Instance
->Timeout
= Reply
.Timeout
;
593 // Send an ACK to (Expected - 1) which is 0 for unicast download,
594 // or tell the server we want to receive the Expected block.
596 return Mtftp4RrqSendAck (Instance
, (UINT16
) (Expected
- 1));
601 The packet process callback for MTFTP download.
603 @param UdpPacket The packet received
604 @param EndPoint The local/remote access point of the packet
605 @param IoStatus The status of the receiving
606 @param Context Opaque parameter, which is the MTFTP session
612 IN NET_BUF
*UdpPacket
,
613 IN UDP_END_POINT
*EndPoint
,
614 IN EFI_STATUS IoStatus
,
618 MTFTP4_PROTOCOL
*Instance
;
619 EFI_MTFTP4_PACKET
*Packet
;
626 Instance
= (MTFTP4_PROTOCOL
*) Context
;
627 NET_CHECK_SIGNATURE (Instance
, MTFTP4_PROTOCOL_SIGNATURE
);
629 Status
= EFI_SUCCESS
;
634 if (EFI_ERROR (IoStatus
)) {
639 ASSERT (UdpPacket
!= NULL
);
642 // Find the port this packet is from to restart receive correctly.
644 Multicast
= (BOOLEAN
) (EndPoint
->LocalAddr
.Addr
[0] == Instance
->McastIp
);
646 if (UdpPacket
->TotalSize
< MTFTP4_OPCODE_LEN
) {
651 // Client send initial request to server's listening port. Server
652 // will select a UDP port to communicate with the client. The server
653 // is required to use the same port as RemotePort to multicast the
656 if (EndPoint
->RemotePort
!= Instance
->ConnectedPort
) {
657 if (Instance
->ConnectedPort
!= 0) {
660 Instance
->ConnectedPort
= EndPoint
->RemotePort
;
665 // Copy the MTFTP packet to a continuous buffer if it isn't already so.
667 Len
= UdpPacket
->TotalSize
;
669 if (UdpPacket
->BlockOpNum
> 1) {
670 Packet
= AllocatePool (Len
);
672 if (Packet
== NULL
) {
673 Status
= EFI_OUT_OF_RESOURCES
;
677 NetbufCopy (UdpPacket
, 0, Len
, (UINT8
*) Packet
);
680 Packet
= (EFI_MTFTP4_PACKET
*) NetbufGetByte (UdpPacket
, 0, NULL
);
683 Opcode
= NTOHS (Packet
->OpCode
);
686 // Call the user's CheckPacket if provided. Abort the transmission
687 // if CheckPacket returns an EFI_ERROR code.
689 if ((Instance
->Token
->CheckPacket
!= NULL
) &&
690 ((Opcode
== EFI_MTFTP4_OPCODE_OACK
) || (Opcode
== EFI_MTFTP4_OPCODE_ERROR
))) {
692 Status
= Instance
->Token
->CheckPacket (
699 if (EFI_ERROR (Status
)) {
701 // Send an error message to the server to inform it
703 if (Opcode
!= EFI_MTFTP4_OPCODE_ERROR
) {
706 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED
,
707 (UINT8
*) "User aborted the transfer"
711 Status
= EFI_ABORTED
;
717 case EFI_MTFTP4_OPCODE_DATA
:
718 if ((Len
> (UINT32
) (MTFTP4_DATA_HEAD_LEN
+ Instance
->BlkSize
)) ||
719 (Len
< (UINT32
) MTFTP4_DATA_HEAD_LEN
)) {
723 Status
= Mtftp4RrqHandleData (Instance
, Packet
, Len
, Multicast
, &Completed
);
726 case EFI_MTFTP4_OPCODE_OACK
:
727 if (Multicast
|| (Len
<= MTFTP4_OPCODE_LEN
)) {
731 Status
= Mtftp4RrqHandleOack (Instance
, Packet
, Len
, Multicast
, &Completed
);
734 case EFI_MTFTP4_OPCODE_ERROR
:
735 Status
= EFI_TFTP_ERROR
;
745 // Free the resources, then if !EFI_ERROR (Status), restart the
746 // receive, otherwise end the session.
748 if ((Packet
!= NULL
) && (UdpPacket
->BlockOpNum
> 1)) {
752 if (UdpPacket
!= NULL
) {
753 NetbufFree (UdpPacket
);
756 if (!EFI_ERROR (Status
) && !Completed
) {
758 Status
= UdpIoRecvDatagram (Instance
->McastUdpPort
, Mtftp4RrqInput
, Instance
, 0);
760 Status
= UdpIoRecvDatagram (Instance
->UnicastPort
, Mtftp4RrqInput
, Instance
, 0);
764 if (EFI_ERROR (Status
) || Completed
) {
765 Mtftp4CleanOperation (Instance
, Status
);