2 Support routines for Mtftp.
4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
5 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.
15 #include "Mtftp4Impl.h"
19 Allocate a MTFTP4 block range, then init it to the range of [Start, End]
21 @param Start The start block number
22 @param End The last block number in the range
24 @return Pointer to the created block range, NULL if failed to allocate memory.
33 MTFTP4_BLOCK_RANGE
*Range
;
35 Range
= AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE
));
41 InitializeListHead (&Range
->Link
);
51 Initialize the block range for either RRQ or WRQ.
53 RRQ and WRQ have different requirements for Start and End.
54 For example, during start up, WRQ initializes its whole valid block range
55 to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us
56 to start the upload. When the client received ACK0, it will remove 0 from the
57 range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
58 without option negotiation, the server will directly send us the BLOCK1 in
59 response to the client's RRQ. When received BLOCK1, the client will remove
60 it from the block range and send an ACK. It also works if there is option
63 @param Head The block range head to initialize
64 @param Start The Start block number.
65 @param End The last block number.
67 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
68 @retval EFI_SUCCESS The initial block range is created.
72 Mtftp4InitBlockRange (
78 MTFTP4_BLOCK_RANGE
*Range
;
80 Range
= Mtftp4AllocateRange (Start
, End
);
83 return EFI_OUT_OF_RESOURCES
;
86 InsertTailList (Head
, &Range
->Link
);
92 Get the first valid block number on the range list.
94 @param Head The block range head
96 @return The first valid block number, -1 if the block range is empty.
100 Mtftp4GetNextBlockNum (
104 MTFTP4_BLOCK_RANGE
*Range
;
106 if (IsListEmpty (Head
)) {
110 Range
= NET_LIST_HEAD (Head
, MTFTP4_BLOCK_RANGE
, Link
);
116 Set the last block number of the block range list.
118 It will remove all the blocks after the Last. MTFTP initialize the block range
119 to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
120 last block number, it will call this function to set the last block number.
122 @param Head The block range list
123 @param Last The last block number
127 Mtftp4SetLastBlockNum (
132 MTFTP4_BLOCK_RANGE
*Range
;
135 // Iterate from the tail to head to remove the block number
138 while (!IsListEmpty (Head
)) {
139 Range
= NET_LIST_TAIL (Head
, MTFTP4_BLOCK_RANGE
, Link
);
141 if (Range
->Start
> Last
) {
142 RemoveEntryList (&Range
->Link
);
147 if (Range
->End
> Last
) {
157 Remove the block number from the block range list.
159 @param Head The block range list to remove from
160 @param Num The block number to remove
161 @param Completed Whether Num is the last block number
162 @param TotalBlock The continuous block number in all
164 @retval EFI_NOT_FOUND The block number isn't in the block range list
165 @retval EFI_SUCCESS The block number has been removed from the list
166 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
170 Mtftp4RemoveBlockNum (
173 IN BOOLEAN Completed
,
174 OUT UINT64
*TotalBlock
177 MTFTP4_BLOCK_RANGE
*Range
;
178 MTFTP4_BLOCK_RANGE
*NewRange
;
181 NET_LIST_FOR_EACH (Entry
, Head
) {
184 // Each block represents a hole [Start, End] in the file,
185 // skip to the first range with End >= Num
187 Range
= NET_LIST_USER_STRUCT (Entry
, MTFTP4_BLOCK_RANGE
, Link
);
189 if (Range
->End
< Num
) {
194 // There are three different cases for Start
195 // 1. (Start > Num) && (End >= Num):
196 // because all the holes before this one has the condition of
197 // End < Num, so this block number has been removed.
199 // 2. (Start == Num) && (End >= Num):
200 // Need to increase the Start by one, and if End == Num, this
201 // hole has been removed completely, remove it.
203 // 3. (Start < Num) && (End >= Num):
204 // if End == Num, only need to decrease the End by one because
205 // we have (Start < Num) && (Num == End), so (Start <= End - 1).
206 // if (End > Num), the hold is splited into two holes, with
207 // [Start, Num - 1] and [Num + 1, End].
209 if (Range
->Start
> Num
) {
210 return EFI_NOT_FOUND
;
212 } else if (Range
->Start
== Num
) {
216 // Note that: RFC 1350 does not mention block counter roll-over,
217 // but several TFTP hosts implement the roll-over be able to accept
218 // transfers of unlimited size. There is no consensus, however, whether
219 // the counter should wrap around to zero or to one. Many implementations
220 // wrap to zero, because this is the simplest to implement. Here we choose
225 if (Range
->Round
> 0) {
226 *TotalBlock
+= Range
->Bound
+ MultU64x32 ((UINTN
) (Range
->Round
-1), (UINT32
) (Range
->Bound
+ 1)) + 1;
229 if (Range
->Start
> Range
->Bound
) {
234 if ((Range
->Start
> Range
->End
) || Completed
) {
235 RemoveEntryList (&Range
->Link
);
242 if (Range
->End
== Num
) {
245 NewRange
= Mtftp4AllocateRange ((UINT16
) (Num
+ 1), (UINT16
) Range
->End
);
247 if (NewRange
== NULL
) {
248 return EFI_OUT_OF_RESOURCES
;
251 Range
->End
= Num
- 1;
252 NetListInsertAfter (&Range
->Link
, &NewRange
->Link
);
259 return EFI_NOT_FOUND
;
264 Build then transmit the request packet for the MTFTP session.
266 @param Instance The Mtftp session
268 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
269 @retval EFI_SUCCESS The request is built and sent
270 @retval Others Failed to transmit the packet.
275 IN MTFTP4_PROTOCOL
*Instance
278 EFI_MTFTP4_PACKET
*Packet
;
279 EFI_MTFTP4_OPTION
*Options
;
280 EFI_MTFTP4_TOKEN
*Token
;
289 Token
= Instance
->Token
;
290 Options
= Token
->OptionList
;
291 Mode
= Instance
->Token
->ModeStr
;
294 Mode
= (UINT8
*) "octet";
298 // Compute the packet length
300 Len1
= (UINT32
) AsciiStrLen ((CHAR8
*) Token
->Filename
);
301 Len2
= (UINT32
) AsciiStrLen ((CHAR8
*) Mode
);
302 Len
= (Len1
+ Len2
+ 4);
304 for (Index
= 0; Index
< Token
->OptionCount
; Index
++) {
305 Len1
= (UINT32
) AsciiStrLen ((CHAR8
*) Options
[Index
].OptionStr
);
306 Len2
= (UINT32
) AsciiStrLen ((CHAR8
*) Options
[Index
].ValueStr
);
307 Len
+= Len1
+ Len2
+ 2;
311 // Allocate a packet then copy the data over
313 if ((Nbuf
= NetbufAlloc (Len
)) == NULL
) {
314 return EFI_OUT_OF_RESOURCES
;
317 Packet
= (EFI_MTFTP4_PACKET
*) NetbufAllocSpace (Nbuf
, Len
, FALSE
);
318 ASSERT (Packet
!= NULL
);
320 Packet
->OpCode
= HTONS (Instance
->Operation
);
321 Cur
= Packet
->Rrq
.Filename
;
322 Cur
= (UINT8
*) AsciiStrCpyS ((CHAR8
*) Cur
, Len
- 2, (CHAR8
*) Token
->Filename
);
323 Cur
+= AsciiStrLen ((CHAR8
*) Token
->Filename
) + 1;
324 Cur
= (UINT8
*) AsciiStrCpyS ((CHAR8
*) Cur
, Len
- 2 - (AsciiStrLen ((CHAR8
*) Token
->Filename
) + 1), (CHAR8
*) Mode
);
325 Cur
+= AsciiStrLen ((CHAR8
*) Mode
) + 1;
326 Len
-= ((UINT32
) AsciiStrLen ((CHAR8
*) Token
->Filename
) + (UINT32
) AsciiStrLen ((CHAR8
*) Mode
) + 4);
328 for (Index
= 0; Index
< Token
->OptionCount
; ++Index
) {
329 Cur
= (UINT8
*) AsciiStrCpyS ((CHAR8
*) Cur
, Len
, (CHAR8
*) Options
[Index
].OptionStr
);
330 Cur
+= AsciiStrLen ((CHAR8
*) Options
[Index
].OptionStr
) + 1;
331 Len
-= (AsciiStrLen ((CHAR8
*) Options
[Index
].OptionStr
) + 1);
333 Cur
= (UINT8
*) AsciiStrCpyS ((CHAR8
*) Cur
, Len
, (CHAR8
*) Options
[Index
].ValueStr
);
334 Cur
+= AsciiStrLen ((CHAR8
*) (CHAR8
*) Options
[Index
].ValueStr
) + 1;
335 Len
-= (AsciiStrLen ((CHAR8
*) (CHAR8
*) Options
[Index
].ValueStr
) + 1);
338 return Mtftp4SendPacket (Instance
, Nbuf
);
343 Build then send an error message.
345 @param Instance The MTFTP session
346 @param ErrCode The error code
347 @param ErrInfo The error message
349 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
350 @retval EFI_SUCCESS The error packet is transmitted.
351 @retval Others Failed to transmit the packet.
356 IN MTFTP4_PROTOCOL
*Instance
,
362 EFI_MTFTP4_PACKET
*TftpError
;
365 Len
= (UINT32
) (AsciiStrLen ((CHAR8
*) ErrInfo
) + sizeof (EFI_MTFTP4_ERROR_HEADER
));
366 Packet
= NetbufAlloc (Len
);
367 if (Packet
== NULL
) {
368 return EFI_OUT_OF_RESOURCES
;
371 TftpError
= (EFI_MTFTP4_PACKET
*) NetbufAllocSpace (Packet
, Len
, FALSE
);
372 ASSERT (TftpError
!= NULL
);
374 TftpError
->OpCode
= HTONS (EFI_MTFTP4_OPCODE_ERROR
);
375 TftpError
->Error
.ErrorCode
= HTONS (ErrCode
);
377 AsciiStrCpyS ((CHAR8
*) TftpError
->Error
.ErrorMessage
, Len
, (CHAR8
*) ErrInfo
);
379 return Mtftp4SendPacket (Instance
, Packet
);
384 The callback function called when the packet is transmitted.
386 It simply frees the packet.
388 @param Packet The transmitted (or failed to) packet
389 @param EndPoint The local and remote UDP access point
390 @param IoStatus The result of the transmission
391 @param Context Opaque parameter to the callback
398 IN UDP_END_POINT
*EndPoint
,
399 IN EFI_STATUS IoStatus
,
408 Set the timeout for the instance. User a longer time for passive instances.
410 @param Instance The Mtftp session to set time out
415 IN OUT MTFTP4_PROTOCOL
*Instance
418 if (Instance
->Master
) {
419 Instance
->PacketToLive
= Instance
->Timeout
;
421 Instance
->PacketToLive
= Instance
->Timeout
* 2;
427 Send the packet for the instance.
429 It will first save a reference to the packet for later retransmission.
430 Then determine the destination port, listen port for requests, and connected
431 port for others. At last, send the packet out.
433 @param Instance The Mtftp instance
434 @param Packet The packet to send
436 @retval EFI_SUCCESS The packet is sent out
437 @retval Others Failed to transmit the packet.
442 IN OUT MTFTP4_PROTOCOL
*Instance
,
443 IN OUT NET_BUF
*Packet
446 UDP_END_POINT UdpPoint
;
452 // Save the packet for retransmission
454 if (Instance
->LastPacket
!= NULL
) {
455 NetbufFree (Instance
->LastPacket
);
458 Instance
->LastPacket
= Packet
;
460 Instance
->CurRetry
= 0;
461 Mtftp4SetTimeout (Instance
);
463 ZeroMem (&UdpPoint
, sizeof (UdpPoint
));
464 UdpPoint
.RemoteAddr
.Addr
[0] = Instance
->ServerIp
;
467 // Send the requests to the listening port, other packets
468 // to the connected port
470 Buffer
= NetbufGetByte (Packet
, 0, NULL
);
471 ASSERT (Buffer
!= NULL
);
472 OpCode
= NTOHS (*(UINT16
*)Buffer
);
474 if ((OpCode
== EFI_MTFTP4_OPCODE_RRQ
) ||
475 (OpCode
== EFI_MTFTP4_OPCODE_DIR
) ||
476 (OpCode
== EFI_MTFTP4_OPCODE_WRQ
)) {
477 UdpPoint
.RemotePort
= Instance
->ListeningPort
;
479 UdpPoint
.RemotePort
= Instance
->ConnectedPort
;
482 NET_GET_REF (Packet
);
484 Status
= UdpIoSendDatagram (
485 Instance
->UnicastPort
,
493 if (EFI_ERROR (Status
)) {
494 NET_PUT_REF (Packet
);
502 Retransmit the last packet for the instance.
504 @param Instance The Mtftp instance
506 @retval EFI_SUCCESS The last packet is retransmitted.
507 @retval Others Failed to retransmit.
512 IN MTFTP4_PROTOCOL
*Instance
515 UDP_END_POINT UdpPoint
;
520 ASSERT (Instance
->LastPacket
!= NULL
);
522 ZeroMem (&UdpPoint
, sizeof (UdpPoint
));
523 UdpPoint
.RemoteAddr
.Addr
[0] = Instance
->ServerIp
;
526 // Set the requests to the listening port, other packets to the connected port
528 Buffer
= NetbufGetByte (Instance
->LastPacket
, 0, NULL
);
529 ASSERT (Buffer
!= NULL
);
530 OpCode
= NTOHS (*(UINT16
*) Buffer
);
532 if ((OpCode
== EFI_MTFTP4_OPCODE_RRQ
) || (OpCode
== EFI_MTFTP4_OPCODE_DIR
) ||
533 (OpCode
== EFI_MTFTP4_OPCODE_WRQ
)) {
534 UdpPoint
.RemotePort
= Instance
->ListeningPort
;
536 UdpPoint
.RemotePort
= Instance
->ConnectedPort
;
539 NET_GET_REF (Instance
->LastPacket
);
541 Status
= UdpIoSendDatagram (
542 Instance
->UnicastPort
,
543 Instance
->LastPacket
,
550 if (EFI_ERROR (Status
)) {
551 NET_PUT_REF (Instance
->LastPacket
);
559 The timer ticking function for the Mtftp service instance.
561 @param Event The ticking event
562 @param Context The Mtftp service instance
572 MTFTP4_SERVICE
*MtftpSb
;
575 MTFTP4_PROTOCOL
*Instance
;
576 EFI_MTFTP4_TOKEN
*Token
;
578 MtftpSb
= (MTFTP4_SERVICE
*) Context
;
581 // Iterate through all the children of the Mtftp service instance. Time
582 // out the packet. If maximum retries reached, clean the session up.
584 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &MtftpSb
->Children
) {
585 Instance
= NET_LIST_USER_STRUCT (Entry
, MTFTP4_PROTOCOL
, Link
);
587 if ((Instance
->PacketToLive
== 0) || (--Instance
->PacketToLive
> 0)) {
592 // Call the user's time out handler
594 Token
= Instance
->Token
;
596 if ((Token
->TimeoutCallback
!= NULL
) &&
597 EFI_ERROR (Token
->TimeoutCallback (&Instance
->Mtftp4
, Token
))) {
601 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED
,
602 (UINT8
*) "User aborted the transfer in time out"
605 Mtftp4CleanOperation (Instance
, EFI_ABORTED
);
610 // Retransmit the packet if haven't reach the maxmium retry count,
611 // otherwise exit the transfer.
613 if (++Instance
->CurRetry
< Instance
->MaxRetry
) {
614 Mtftp4Retransmit (Instance
);
615 Mtftp4SetTimeout (Instance
);
617 Mtftp4CleanOperation (Instance
, EFI_TIMEOUT
);