2 Support routines for Mtftp.
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "Mtftp4Impl.h"
13 Allocate a MTFTP4 block range, then init it to the range of [Start, End]
15 @param Start The start block number
16 @param End The last block number in the range
18 @return Pointer to the created block range, NULL if failed to allocate memory.
27 MTFTP4_BLOCK_RANGE
*Range
;
29 Range
= AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE
));
35 InitializeListHead (&Range
->Link
);
45 Initialize the block range for either RRQ or WRQ.
47 RRQ and WRQ have different requirements for Start and End.
48 For example, during start up, WRQ initializes its whole valid block range
49 to [0, 0xffff]. This is because the server will send us a ACK0 to inform us
50 to start the upload. When the client received ACK0, it will remove 0 from the
51 range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
52 without option negotiation, the server will directly send us the BLOCK1 in
53 response to the client's RRQ. When received BLOCK1, the client will remove
54 it from the block range and send an ACK. It also works if there is option
57 @param Head The block range head to initialize
58 @param Start The Start block number.
59 @param End The last block number.
61 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
62 @retval EFI_SUCCESS The initial block range is created.
66 Mtftp4InitBlockRange (
72 MTFTP4_BLOCK_RANGE
*Range
;
74 Range
= Mtftp4AllocateRange (Start
, End
);
77 return EFI_OUT_OF_RESOURCES
;
80 InsertTailList (Head
, &Range
->Link
);
86 Get the first valid block number on the range list.
88 @param Head The block range head
90 @return The first valid block number, -1 if the block range is empty.
94 Mtftp4GetNextBlockNum (
98 MTFTP4_BLOCK_RANGE
*Range
;
100 if (IsListEmpty (Head
)) {
104 Range
= NET_LIST_HEAD (Head
, MTFTP4_BLOCK_RANGE
, Link
);
110 Set the last block number of the block range list.
112 It will remove all the blocks after the Last. MTFTP initialize the block range
113 to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
114 last block number, it will call this function to set the last block number.
116 @param Head The block range list
117 @param Last The last block number
121 Mtftp4SetLastBlockNum (
126 MTFTP4_BLOCK_RANGE
*Range
;
129 // Iterate from the tail to head to remove the block number
132 while (!IsListEmpty (Head
)) {
133 Range
= NET_LIST_TAIL (Head
, MTFTP4_BLOCK_RANGE
, Link
);
135 if (Range
->Start
> Last
) {
136 RemoveEntryList (&Range
->Link
);
141 if (Range
->End
> Last
) {
151 Remove the block number from the block range list.
153 @param Head The block range list to remove from
154 @param Num The block number to remove
155 @param Completed Whether Num is the last block number.
156 @param BlockCounter The continuous block counter instead of the value after roll-over.
158 @retval EFI_NOT_FOUND The block number isn't in the block range list
159 @retval EFI_SUCCESS The block number has been removed from the list
160 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
164 Mtftp4RemoveBlockNum (
167 IN BOOLEAN Completed
,
168 OUT UINT64
*BlockCounter
171 MTFTP4_BLOCK_RANGE
*Range
;
172 MTFTP4_BLOCK_RANGE
*NewRange
;
175 NET_LIST_FOR_EACH (Entry
, Head
) {
178 // Each block represents a hole [Start, End] in the file,
179 // skip to the first range with End >= Num
181 Range
= NET_LIST_USER_STRUCT (Entry
, MTFTP4_BLOCK_RANGE
, Link
);
183 if (Range
->End
< Num
) {
188 // There are three different cases for Start
189 // 1. (Start > Num) && (End >= Num):
190 // because all the holes before this one has the condition of
191 // End < Num, so this block number has been removed.
193 // 2. (Start == Num) && (End >= Num):
194 // Need to increase the Start by one, and if End == Num, this
195 // hole has been removed completely, remove it.
197 // 3. (Start < Num) && (End >= Num):
198 // if End == Num, only need to decrease the End by one because
199 // we have (Start < Num) && (Num == End), so (Start <= End - 1).
200 // if (End > Num), the hold is split into two holes, with
201 // [Start, Num - 1] and [Num + 1, End].
203 if (Range
->Start
> Num
) {
204 return EFI_NOT_FOUND
;
206 } else if (Range
->Start
== Num
) {
210 // Note that: RFC 1350 does not mention block counter roll-over,
211 // but several TFTP hosts implement the roll-over be able to accept
212 // transfers of unlimited size. There is no consensus, however, whether
213 // the counter should wrap around to zero or to one. Many implementations
214 // wrap to zero, because this is the simplest to implement. Here we choose
219 if (Range
->Round
> 0) {
220 *BlockCounter
+= Range
->Bound
+ MultU64x32 ((UINTN
) (Range
->Round
-1), (UINT32
) (Range
->Bound
+ 1)) + 1;
223 if (Range
->Start
> Range
->Bound
) {
228 if ((Range
->Start
> Range
->End
) || Completed
) {
229 RemoveEntryList (&Range
->Link
);
236 if (Range
->End
== Num
) {
239 NewRange
= Mtftp4AllocateRange ((UINT16
) (Num
+ 1), (UINT16
) Range
->End
);
241 if (NewRange
== NULL
) {
242 return EFI_OUT_OF_RESOURCES
;
245 Range
->End
= Num
- 1;
246 NetListInsertAfter (&Range
->Link
, &NewRange
->Link
);
253 return EFI_NOT_FOUND
;
258 Build then transmit the request packet for the MTFTP session.
260 @param Instance The Mtftp session
262 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
263 @retval EFI_SUCCESS The request is built and sent
264 @retval Others Failed to transmit the packet.
269 IN MTFTP4_PROTOCOL
*Instance
272 EFI_MTFTP4_PACKET
*Packet
;
273 EFI_MTFTP4_OPTION
*Options
;
274 EFI_MTFTP4_TOKEN
*Token
;
275 RETURN_STATUS Status
;
281 UINTN FileNameLength
;
283 UINTN OptionStrLength
;
284 UINTN ValueStrLength
;
286 Token
= Instance
->Token
;
287 Options
= Token
->OptionList
;
288 Mode
= Instance
->Token
->ModeStr
;
291 Mode
= (UINT8
*) "octet";
295 // Compute the packet length
297 FileNameLength
= AsciiStrLen ((CHAR8
*) Token
->Filename
);
298 ModeLength
= AsciiStrLen ((CHAR8
*) Mode
);
299 BufferLength
= (UINT32
) FileNameLength
+ (UINT32
) ModeLength
+ 4;
301 for (Index
= 0; Index
< Token
->OptionCount
; Index
++) {
302 OptionStrLength
= AsciiStrLen ((CHAR8
*) Options
[Index
].OptionStr
);
303 ValueStrLength
= AsciiStrLen ((CHAR8
*) Options
[Index
].ValueStr
);
304 BufferLength
+= (UINT32
) OptionStrLength
+ (UINT32
) ValueStrLength
+ 2;
307 // Allocate a packet then copy the data over
309 if ((Nbuf
= NetbufAlloc (BufferLength
)) == NULL
) {
310 return EFI_OUT_OF_RESOURCES
;
313 Packet
= (EFI_MTFTP4_PACKET
*) NetbufAllocSpace (Nbuf
, BufferLength
, FALSE
);
314 ASSERT (Packet
!= NULL
);
316 Packet
->OpCode
= HTONS (Instance
->Operation
);
317 BufferLength
-= sizeof (Packet
->OpCode
);
319 Cur
= Packet
->Rrq
.Filename
;
320 Status
= AsciiStrCpyS ((CHAR8
*) Cur
, BufferLength
, (CHAR8
*) Token
->Filename
);
321 ASSERT_EFI_ERROR (Status
);
322 BufferLength
-= (UINT32
) (FileNameLength
+ 1);
323 Cur
+= FileNameLength
+ 1;
324 Status
= AsciiStrCpyS ((CHAR8
*) Cur
, BufferLength
, (CHAR8
*) Mode
);
325 ASSERT_EFI_ERROR (Status
);
326 BufferLength
-= (UINT32
) (ModeLength
+ 1);
327 Cur
+= ModeLength
+ 1;
329 for (Index
= 0; Index
< Token
->OptionCount
; ++Index
) {
330 OptionStrLength
= AsciiStrLen ((CHAR8
*) Options
[Index
].OptionStr
);
331 ValueStrLength
= AsciiStrLen ((CHAR8
*) Options
[Index
].ValueStr
);
333 Status
= AsciiStrCpyS ((CHAR8
*) Cur
, BufferLength
, (CHAR8
*) Options
[Index
].OptionStr
);
334 ASSERT_EFI_ERROR (Status
);
335 BufferLength
-= (UINT32
) (OptionStrLength
+ 1);
336 Cur
+= OptionStrLength
+ 1;
338 Status
= AsciiStrCpyS ((CHAR8
*) Cur
, BufferLength
, (CHAR8
*) Options
[Index
].ValueStr
);
339 ASSERT_EFI_ERROR (Status
);
340 BufferLength
-= (UINT32
) (ValueStrLength
+ 1);
341 Cur
+= ValueStrLength
+ 1;
345 return Mtftp4SendPacket (Instance
, Nbuf
);
350 Build then send an error message.
352 @param Instance The MTFTP session
353 @param ErrCode The error code
354 @param ErrInfo The error message
356 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
357 @retval EFI_SUCCESS The error packet is transmitted.
358 @retval Others Failed to transmit the packet.
363 IN MTFTP4_PROTOCOL
*Instance
,
369 EFI_MTFTP4_PACKET
*TftpError
;
372 Len
= (UINT32
) (AsciiStrLen ((CHAR8
*) ErrInfo
) + sizeof (EFI_MTFTP4_ERROR_HEADER
));
373 Packet
= NetbufAlloc (Len
);
374 if (Packet
== NULL
) {
375 return EFI_OUT_OF_RESOURCES
;
378 TftpError
= (EFI_MTFTP4_PACKET
*) NetbufAllocSpace (Packet
, Len
, FALSE
);
379 ASSERT (TftpError
!= NULL
);
381 TftpError
->OpCode
= HTONS (EFI_MTFTP4_OPCODE_ERROR
);
382 TftpError
->Error
.ErrorCode
= HTONS (ErrCode
);
384 AsciiStrCpyS ((CHAR8
*) TftpError
->Error
.ErrorMessage
, Len
, (CHAR8
*) ErrInfo
);
386 return Mtftp4SendPacket (Instance
, Packet
);
391 The callback function called when the packet is transmitted.
393 It simply frees the packet.
395 @param Packet The transmitted (or failed to) packet
396 @param EndPoint The local and remote UDP access point
397 @param IoStatus The result of the transmission
398 @param Context Opaque parameter to the callback
405 IN UDP_END_POINT
*EndPoint
,
406 IN EFI_STATUS IoStatus
,
415 Set the timeout for the instance. User a longer time for passive instances.
417 @param Instance The Mtftp session to set time out
422 IN OUT MTFTP4_PROTOCOL
*Instance
425 if (Instance
->Master
) {
426 Instance
->PacketToLive
= Instance
->Timeout
;
428 Instance
->PacketToLive
= Instance
->Timeout
* 2;
434 Send the packet for the instance.
436 It will first save a reference to the packet for later retransmission.
437 Then determine the destination port, listen port for requests, and connected
438 port for others. At last, send the packet out.
440 @param Instance The Mtftp instance
441 @param Packet The packet to send
443 @retval EFI_SUCCESS The packet is sent out
444 @retval Others Failed to transmit the packet.
449 IN OUT MTFTP4_PROTOCOL
*Instance
,
450 IN OUT NET_BUF
*Packet
453 UDP_END_POINT UdpPoint
;
459 // Save the packet for retransmission
461 if (Instance
->LastPacket
!= NULL
) {
462 NetbufFree (Instance
->LastPacket
);
465 Instance
->LastPacket
= Packet
;
467 Instance
->CurRetry
= 0;
468 Mtftp4SetTimeout (Instance
);
470 ZeroMem (&UdpPoint
, sizeof (UdpPoint
));
471 UdpPoint
.RemoteAddr
.Addr
[0] = Instance
->ServerIp
;
474 // Send the requests to the listening port, other packets
475 // to the connected port
477 Buffer
= NetbufGetByte (Packet
, 0, NULL
);
478 ASSERT (Buffer
!= NULL
);
479 OpCode
= NTOHS (*(UINT16
*)Buffer
);
481 if ((OpCode
== EFI_MTFTP4_OPCODE_RRQ
) ||
482 (OpCode
== EFI_MTFTP4_OPCODE_DIR
) ||
483 (OpCode
== EFI_MTFTP4_OPCODE_WRQ
)) {
484 UdpPoint
.RemotePort
= Instance
->ListeningPort
;
486 UdpPoint
.RemotePort
= Instance
->ConnectedPort
;
489 NET_GET_REF (Packet
);
491 Status
= UdpIoSendDatagram (
492 Instance
->UnicastPort
,
500 if (EFI_ERROR (Status
)) {
501 NET_PUT_REF (Packet
);
509 Retransmit the last packet for the instance.
511 @param Instance The Mtftp instance
513 @retval EFI_SUCCESS The last packet is retransmitted.
514 @retval Others Failed to retransmit.
519 IN MTFTP4_PROTOCOL
*Instance
522 UDP_END_POINT UdpPoint
;
527 ASSERT (Instance
->LastPacket
!= NULL
);
529 ZeroMem (&UdpPoint
, sizeof (UdpPoint
));
530 UdpPoint
.RemoteAddr
.Addr
[0] = Instance
->ServerIp
;
533 // Set the requests to the listening port, other packets to the connected port
535 Buffer
= NetbufGetByte (Instance
->LastPacket
, 0, NULL
);
536 ASSERT (Buffer
!= NULL
);
537 OpCode
= NTOHS (*(UINT16
*) Buffer
);
539 if ((OpCode
== EFI_MTFTP4_OPCODE_RRQ
) || (OpCode
== EFI_MTFTP4_OPCODE_DIR
) ||
540 (OpCode
== EFI_MTFTP4_OPCODE_WRQ
)) {
541 UdpPoint
.RemotePort
= Instance
->ListeningPort
;
543 UdpPoint
.RemotePort
= Instance
->ConnectedPort
;
546 NET_GET_REF (Instance
->LastPacket
);
548 Status
= UdpIoSendDatagram (
549 Instance
->UnicastPort
,
550 Instance
->LastPacket
,
557 if (EFI_ERROR (Status
)) {
558 NET_PUT_REF (Instance
->LastPacket
);
566 The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.
568 @param Event The ticking event
569 @param Context The Mtftp service instance
574 Mtftp4OnTimerTickNotifyLevel (
579 MTFTP4_SERVICE
*MtftpSb
;
582 MTFTP4_PROTOCOL
*Instance
;
584 MtftpSb
= (MTFTP4_SERVICE
*) Context
;
587 // Iterate through all the children of the Mtftp service instance. Time
588 // out the current packet transmit.
590 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &MtftpSb
->Children
) {
591 Instance
= NET_LIST_USER_STRUCT (Entry
, MTFTP4_PROTOCOL
, Link
);
592 if ((Instance
->PacketToLive
== 0) || (--Instance
->PacketToLive
> 0)) {
593 Instance
->HasTimeout
= FALSE
;
595 Instance
->HasTimeout
= TRUE
;
602 The timer ticking function for the Mtftp service instance.
604 @param Event The ticking event
605 @param Context The Mtftp service instance
615 MTFTP4_SERVICE
*MtftpSb
;
618 MTFTP4_PROTOCOL
*Instance
;
619 EFI_MTFTP4_TOKEN
*Token
;
621 MtftpSb
= (MTFTP4_SERVICE
*) Context
;
624 // Iterate through all the children of the Mtftp service instance.
626 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &MtftpSb
->Children
) {
627 Instance
= NET_LIST_USER_STRUCT (Entry
, MTFTP4_PROTOCOL
, Link
);
628 if (!Instance
->HasTimeout
) {
632 Instance
->HasTimeout
= FALSE
;
635 // Call the user's time out handler
637 Token
= Instance
->Token
;
639 if (Token
!= NULL
&& Token
->TimeoutCallback
!= NULL
&&
640 EFI_ERROR (Token
->TimeoutCallback (&Instance
->Mtftp4
, Token
))) {
643 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED
,
644 (UINT8
*) "User aborted the transfer in time out"
647 Mtftp4CleanOperation (Instance
, EFI_ABORTED
);
652 // Retransmit the packet if haven't reach the maximum retry count,
653 // otherwise exit the transfer.
655 if (++Instance
->CurRetry
< Instance
->MaxRetry
) {
656 Mtftp4Retransmit (Instance
);
657 Mtftp4SetTimeout (Instance
);
659 Mtftp4CleanOperation (Instance
, EFI_TIMEOUT
);