2 Routines to process Wrq (upload).
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "Mtftp4Impl.h"
12 Build then send a MTFTP data packet for the MTFTP upload session.
14 @param Instance The MTFTP upload session.
15 @param BlockNum The block number to send.
17 @retval EFI_OUT_OF_RESOURCES Failed to build the packet.
18 @retval EFI_ABORTED The consumer of this child directs to abort the
19 transmission by return an error through PacketNeeded.
20 @retval EFI_SUCCESS The data is sent.
25 IN OUT MTFTP4_PROTOCOL
*Instance
,
29 EFI_MTFTP4_PACKET
*Packet
;
30 EFI_MTFTP4_TOKEN
*Token
;
38 // Allocate a buffer to hold the user data
40 UdpPacket
= NetbufAlloc (Instance
->BlkSize
+ MTFTP4_DATA_HEAD_LEN
);
42 if (UdpPacket
== NULL
) {
43 return EFI_OUT_OF_RESOURCES
;
46 Packet
= (EFI_MTFTP4_PACKET
*)NetbufAllocSpace (UdpPacket
, MTFTP4_DATA_HEAD_LEN
, FALSE
);
47 ASSERT (Packet
!= NULL
);
49 Packet
->Data
.OpCode
= HTONS (EFI_MTFTP4_OPCODE_DATA
);
50 Packet
->Data
.Block
= HTONS (BlockNum
);
53 // Read the block from either the buffer or PacketNeeded callback
55 Token
= Instance
->Token
;
56 DataLen
= Instance
->BlkSize
;
58 if (Token
->Buffer
!= NULL
) {
59 Start
= MultU64x32 (BlockNum
- 1, Instance
->BlkSize
);
61 if (Token
->BufferSize
< Start
+ Instance
->BlkSize
) {
62 DataLen
= (UINT16
)(Token
->BufferSize
- Start
);
63 Instance
->LastBlock
= BlockNum
;
64 Mtftp4SetLastBlockNum (&Instance
->Blocks
, BlockNum
);
68 NetbufAllocSpace (UdpPacket
, DataLen
, FALSE
);
69 CopyMem (Packet
->Data
.Data
, (UINT8
*)Token
->Buffer
+ Start
, DataLen
);
73 // Get data from PacketNeeded
76 Status
= Token
->PacketNeeded (
83 if (EFI_ERROR (Status
) || (DataLen
> Instance
->BlkSize
)) {
84 if (DataBuf
!= NULL
) {
88 if (UdpPacket
!= NULL
) {
89 NetbufFree (UdpPacket
);
94 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED
,
95 (UINT8
*)"User aborted the transfer"
101 if (DataLen
< Instance
->BlkSize
) {
102 Instance
->LastBlock
= BlockNum
;
103 Mtftp4SetLastBlockNum (&Instance
->Blocks
, BlockNum
);
107 NetbufAllocSpace (UdpPacket
, DataLen
, FALSE
);
108 CopyMem (Packet
->Data
.Data
, DataBuf
, DataLen
);
113 return Mtftp4SendPacket (Instance
, UdpPacket
);
117 Function to handle received ACK packet.
119 If the ACK number matches the expected block number, and there are more
120 data pending, send the next block. Otherwise tell the caller that we are done.
122 @param Instance The MTFTP upload session
123 @param Packet The MTFTP packet received
124 @param Len The packet length
125 @param Completed Return whether the upload has finished.
127 @retval EFI_SUCCESS The ACK is successfully processed.
128 @retval EFI_TFTP_ERROR The block number loops back.
129 @retval Others Failed to transmit the next data packet.
134 IN MTFTP4_PROTOCOL
*Instance
,
135 IN EFI_MTFTP4_PACKET
*Packet
,
137 OUT BOOLEAN
*Completed
145 AckNum
= NTOHS (Packet
->Ack
.Block
[0]);
146 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
148 ASSERT (Expected
>= 0);
151 // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput
154 if (Expected
!= AckNum
) {
159 // Remove the acked block number, if this is the last block number,
160 // tell the Mtftp4WrqInput to finish the transfer. This is the last
161 // block number if the block range are empty.
163 Mtftp4RemoveBlockNum (&Instance
->Blocks
, AckNum
, *Completed
, &BlockCounter
);
165 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
169 // The block range is empty. It may either because the last
170 // block has been ACKed, or the sequence number just looped back,
171 // that is, there is more than 0xffff blocks.
173 if (Instance
->LastBlock
== AckNum
) {
174 ASSERT (Instance
->LastBlock
>= 1);
180 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED
,
181 (UINT8
*)"Block number rolls back, not supported, try blksize option"
184 return EFI_TFTP_ERROR
;
188 return Mtftp4WrqSendBlock (Instance
, (UINT16
)Expected
);
192 Check whether the received OACK is valid.
194 The OACK is valid only if:
195 1. It only include options requested by us
196 2. It can only include a smaller block size
197 3. It can't change the proposed time out value.
198 4. Other requirements of the individal MTFTP options as required.
200 @param Reply The options included in the OACK
201 @param Request The options we requested
203 @retval TRUE The options included in OACK is valid.
204 @retval FALSE The options included in OACK is invalid.
209 IN MTFTP4_OPTION
*Reply
,
210 IN MTFTP4_OPTION
*Request
214 // It is invalid for server to return options we don't request
216 if ((Reply
->Exist
& ~Request
->Exist
) != 0) {
221 // Server can only specify a smaller block size to be used and
222 // return the timeout matches that requested.
224 if ((((Reply
->Exist
& MTFTP4_BLKSIZE_EXIST
) != 0) && (Reply
->BlkSize
> Request
->BlkSize
)) ||
225 (((Reply
->Exist
& MTFTP4_TIMEOUT_EXIST
) != 0) && (Reply
->Timeout
!= Request
->Timeout
)))
234 Function to handle the MTFTP OACK packet.
236 It parses the packet's options, and update the internal states of the session.
238 @param Instance The MTFTP session
239 @param Packet The received OACK packet
240 @param Len The length of the packet
241 @param Completed Whether the transmission has completed. NOT used by
244 @retval EFI_SUCCESS The OACK process is OK
245 @retval EFI_TFTP_ERROR Some error occurred, and the session reset.
249 Mtftp4WrqHandleOack (
250 IN OUT MTFTP4_PROTOCOL
*Instance
,
251 IN EFI_MTFTP4_PACKET
*Packet
,
253 OUT BOOLEAN
*Completed
257 EFI_MTFTP4_PACKET Bogus
;
264 // Ignore the OACK if already started the upload
266 Expected
= Mtftp4GetNextBlockNum (&Instance
->Blocks
);
273 // Parse and validate the options from server
275 ZeroMem (&Reply
, sizeof (MTFTP4_OPTION
));
276 Status
= Mtftp4ParseOptionOack (Packet
, Len
, Instance
->Operation
, &Reply
);
278 if (EFI_ERROR (Status
) || !Mtftp4WrqOackValid (&Reply
, &Instance
->RequestOption
)) {
280 // Don't send a MTFTP error packet when out of resource, it can
281 // only make it worse.
283 if (Status
!= EFI_OUT_OF_RESOURCES
) {
286 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION
,
287 (UINT8
*)"Malformatted OACK packet"
291 return EFI_TFTP_ERROR
;
294 if (Reply
.BlkSize
!= 0) {
295 Instance
->BlkSize
= Reply
.BlkSize
;
298 if (Reply
.Timeout
!= 0) {
299 Instance
->Timeout
= Reply
.Timeout
;
303 // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,
304 // which will start the transmission of the first data block.
306 Bogus
.Ack
.OpCode
= HTONS (EFI_MTFTP4_OPCODE_ACK
);
307 Bogus
.Ack
.Block
[0] = 0;
309 Status
= Mtftp4WrqHandleAck (
312 sizeof (EFI_MTFTP4_ACK_HEADER
),
320 The input process routine for MTFTP upload.
322 @param UdpPacket The received MTFTP packet.
323 @param EndPoint The local/remote access point
324 @param IoStatus The result of the packet receiving
325 @param Context Opaque parameter for the callback, which is the
331 IN NET_BUF
*UdpPacket
,
332 IN UDP_END_POINT
*EndPoint
,
333 IN EFI_STATUS IoStatus
,
337 MTFTP4_PROTOCOL
*Instance
;
338 EFI_MTFTP4_PACKET
*Packet
;
344 Instance
= (MTFTP4_PROTOCOL
*)Context
;
345 NET_CHECK_SIGNATURE (Instance
, MTFTP4_PROTOCOL_SIGNATURE
);
349 Status
= EFI_SUCCESS
;
351 if (EFI_ERROR (IoStatus
)) {
356 ASSERT (UdpPacket
!= NULL
);
358 if (UdpPacket
->TotalSize
< MTFTP4_OPCODE_LEN
) {
363 // Client send initial request to server's listening port. Server
364 // will select a UDP port to communicate with the client.
366 if (EndPoint
->RemotePort
!= Instance
->ConnectedPort
) {
367 if (Instance
->ConnectedPort
!= 0) {
370 Instance
->ConnectedPort
= EndPoint
->RemotePort
;
375 // Copy the MTFTP packet to a continuous buffer if it isn't already so.
377 Len
= UdpPacket
->TotalSize
;
379 if (UdpPacket
->BlockOpNum
> 1) {
380 Packet
= AllocatePool (Len
);
382 if (Packet
== NULL
) {
383 Status
= EFI_OUT_OF_RESOURCES
;
387 NetbufCopy (UdpPacket
, 0, Len
, (UINT8
*)Packet
);
389 Packet
= (EFI_MTFTP4_PACKET
*)NetbufGetByte (UdpPacket
, 0, NULL
);
390 ASSERT (Packet
!= NULL
);
393 Opcode
= NTOHS (Packet
->OpCode
);
396 // Call the user's CheckPacket if provided. Abort the transmission
397 // if CheckPacket returns an EFI_ERROR code.
399 if ((Instance
->Token
->CheckPacket
!= NULL
) &&
400 ((Opcode
== EFI_MTFTP4_OPCODE_OACK
) || (Opcode
== EFI_MTFTP4_OPCODE_ERROR
)))
402 Status
= Instance
->Token
->CheckPacket (
409 if (EFI_ERROR (Status
)) {
411 // Send an error message to the server to inform it
413 if (Opcode
!= EFI_MTFTP4_OPCODE_ERROR
) {
416 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED
,
417 (UINT8
*)"User aborted the transfer"
421 Status
= EFI_ABORTED
;
427 case EFI_MTFTP4_OPCODE_ACK
:
428 if (Len
!= MTFTP4_OPCODE_LEN
+ MTFTP4_BLKNO_LEN
) {
432 Status
= Mtftp4WrqHandleAck (Instance
, Packet
, Len
, &Completed
);
435 case EFI_MTFTP4_OPCODE_OACK
:
436 if (Len
<= MTFTP4_OPCODE_LEN
) {
440 Status
= Mtftp4WrqHandleOack (Instance
, Packet
, Len
, &Completed
);
443 case EFI_MTFTP4_OPCODE_ERROR
:
444 Status
= EFI_TFTP_ERROR
;
453 // Free the resources, then if !EFI_ERROR (Status) and not completed,
454 // restart the receive, otherwise end the session.
456 if ((Packet
!= NULL
) && (UdpPacket
->BlockOpNum
> 1)) {
460 if (UdpPacket
!= NULL
) {
461 NetbufFree (UdpPacket
);
464 if (!EFI_ERROR (Status
) && !Completed
) {
465 Status
= UdpIoRecvDatagram (Instance
->UnicastPort
, Mtftp4WrqInput
, Instance
, 0);
469 // Status may have been updated by UdpIoRecvDatagram
471 if (EFI_ERROR (Status
) || Completed
) {
472 Mtftp4CleanOperation (Instance
, Status
);
477 Start the MTFTP session for upload.
479 It will first init some states, then send the WRQ request packet,
480 and start receiving the packet.
482 @param Instance The MTFTP session
483 @param Operation Redundant parameter, which is always
484 EFI_MTFTP4_OPCODE_WRQ here.
486 @retval EFI_SUCCESS The upload process has been started.
487 @retval Others Failed to start the upload.
492 IN MTFTP4_PROTOCOL
*Instance
,
499 // The valid block number range are [0, 0xffff]. For example:
500 // the client sends an WRQ request to the server, the server
501 // ACK with an ACK0 to let client start transfer the first
504 Status
= Mtftp4InitBlockRange (&Instance
->Blocks
, 0, 0xffff);
506 if (EFI_ERROR (Status
)) {
510 Status
= Mtftp4SendRequest (Instance
);
512 if (EFI_ERROR (Status
)) {
516 return UdpIoRecvDatagram (Instance
->UnicastPort
, Mtftp4WrqInput
, Instance
, 0);