1 /** @file
2   Mtftp6 support functions implementation.
3 
4   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php.
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "Mtftp6Impl.h"
17 
18 
19 /**
20   Allocate a MTFTP block range, then init it to the range of [Start, End].
21 
22   @param[in]  Start                  The start block number.
23   @param[in]  End                    The last block number in the range.
24 
25   @return Range                      The range of the allocated block buffer.
26 
27 **/
28 MTFTP6_BLOCK_RANGE *
Mtftp6AllocateRange(IN UINT16 Start,IN UINT16 End)29 Mtftp6AllocateRange (
30   IN UINT16                 Start,
31   IN UINT16                 End
32   )
33 {
34   MTFTP6_BLOCK_RANGE        *Range;
35 
36   Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE));
37 
38   if (Range == NULL) {
39     return NULL;
40   }
41 
42   InitializeListHead (&Range->Link);
43   Range->Start  = Start;
44   Range->End    = End;
45   Range->Bound  = End;
46 
47   return Range;
48 }
49 
50 
51 /**
52   Initialize the block range for either RRQ or WRQ. RRQ and WRQ have
53   different requirements for Start and End. For example, during startup,
54   WRQ initializes its whole valid block range to [0, 0xffff]. This
55   is bacause the server will send an ACK0 to inform the user to start the
56   upload. When the client receives an ACK0, it will remove 0 from the range,
57   get the next block number, which is 1, then upload the BLOCK1. For RRQ
58   without option negotiation, the server will directly send the BLOCK1
59   in response to the client's RRQ. When received BLOCK1, the client will
60   remove it from the block range and send an ACK. It also works if there
61   is option negotiation.
62 
63   @param[in]  Head                   The block range head to initialize.
64   @param[in]  Start                  The Start block number.
65   @param[in]  End                    The last block number.
66 
67   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for initial block range.
68   @retval EFI_SUCCESS            The initial block range is created.
69 
70 **/
71 EFI_STATUS
Mtftp6InitBlockRange(IN LIST_ENTRY * Head,IN UINT16 Start,IN UINT16 End)72 Mtftp6InitBlockRange (
73   IN LIST_ENTRY             *Head,
74   IN UINT16                 Start,
75   IN UINT16                 End
76   )
77 {
78   MTFTP6_BLOCK_RANGE        *Range;
79 
80   Range = Mtftp6AllocateRange (Start, End);
81 
82   if (Range == NULL) {
83     return EFI_OUT_OF_RESOURCES;
84   }
85 
86   InsertTailList (Head, &Range->Link);
87   return EFI_SUCCESS;
88 }
89 
90 
91 /**
92   Get the first valid block number on the range list.
93 
94   @param[in]  Head                   The block range head.
95 
96   @retval     ==-1                   If the block range is empty.
97   @retval     >-1                    The first valid block number.
98 
99 **/
100 INTN
Mtftp6GetNextBlockNum(IN LIST_ENTRY * Head)101 Mtftp6GetNextBlockNum (
102   IN LIST_ENTRY              *Head
103   )
104 {
105   MTFTP6_BLOCK_RANGE  *Range;
106 
107   if (IsListEmpty (Head)) {
108     return -1;
109   }
110 
111   Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link);
112   return Range->Start;
113 }
114 
115 
116 /**
117   Set the last block number of the block range list. It
118   removes all the blocks after the Last. MTFTP initialize the
119   block range to the maximum possible range, such as [0, 0xffff]
120   for WRQ. When it gets the last block number, it calls
121   this function to set the last block number.
122 
123   @param[in]  Head                   The block range list.
124   @param[in]  Last                   The last block number.
125 
126 **/
127 VOID
Mtftp6SetLastBlockNum(IN LIST_ENTRY * Head,IN UINT16 Last)128 Mtftp6SetLastBlockNum (
129   IN LIST_ENTRY             *Head,
130   IN UINT16                 Last
131   )
132 {
133   MTFTP6_BLOCK_RANGE        *Range;
134 
135   //
136   // Iterate from the tail to head to remove the block number
137   // after the last.
138   //
139   while (!IsListEmpty (Head)) {
140     Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link);
141 
142     if (Range->Start > Last) {
143       RemoveEntryList (&Range->Link);
144       FreePool (Range);
145       continue;
146     }
147 
148     if (Range->End > Last) {
149       Range->End = Last;
150     }
151     return ;
152   }
153 }
154 
155 
156 /**
157   Remove the block number from the block range list.
158 
159   @param[in]  Head                   The block range list to remove from.
160   @param[in]  Num                    The block number to remove.
161   @param[in]  Completed              Whether Num is the last block number
162   @param[out] TotalBlock             The continuous block number in all
163 
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 resources.
167 
168 **/
169 EFI_STATUS
Mtftp6RemoveBlockNum(IN LIST_ENTRY * Head,IN UINT16 Num,IN BOOLEAN Completed,OUT UINT64 * TotalBlock)170 Mtftp6RemoveBlockNum (
171   IN LIST_ENTRY             *Head,
172   IN UINT16                 Num,
173   IN BOOLEAN                Completed,
174   OUT UINT64                *TotalBlock
175   )
176 {
177   MTFTP6_BLOCK_RANGE        *Range;
178   MTFTP6_BLOCK_RANGE        *NewRange;
179   LIST_ENTRY                *Entry;
180 
181   NET_LIST_FOR_EACH (Entry, Head) {
182 
183     //
184     // Each block represents a hole [Start, End] in the file,
185     // skip to the first range with End >= Num
186     //
187     Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
188 
189     if (Range->End < Num) {
190       continue;
191     }
192 
193     //
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.
198     //
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.
202     //
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].
208     //
209     if (Range->Start > Num) {
210       return EFI_NOT_FOUND;
211 
212     } else if (Range->Start == Num) {
213       Range->Start++;
214 
215       //
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
221       // this solution.
222       //
223       *TotalBlock  = Num;
224 
225       if (Range->Round > 0) {
226         *TotalBlock += Range->Bound +  MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1;
227       }
228 
229       if (Range->Start > Range->Bound) {
230         Range->Start = 0;
231         Range->Round ++;
232       }
233 
234       if ((Range->Start > Range->End) || Completed) {
235         RemoveEntryList (&Range->Link);
236         FreePool (Range);
237       }
238 
239       return EFI_SUCCESS;
240 
241     } else {
242       if (Range->End == Num) {
243         Range->End--;
244       } else {
245         NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
246 
247         if (NewRange == NULL) {
248           return EFI_OUT_OF_RESOURCES;
249         }
250 
251         Range->End = Num - 1;
252         NetListInsertAfter (&Range->Link, &NewRange->Link);
253       }
254 
255       return EFI_SUCCESS;
256     }
257   }
258 
259   return EFI_NOT_FOUND;
260 }
261 
262 
263 /**
264   Configure the opened Udp6 instance until the corresponding Ip6 instance
265   has been configured.
266 
267   @param[in]  UdpIo                  The pointer to the Udp6 Io.
268   @param[in]  UdpCfgData             The pointer to the Udp6 configure data.
269 
270   @retval EFI_SUCCESS            Configure the Udp6 instance successfully.
271   @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not
272                                  been configured yet.
273 
274 **/
275 EFI_STATUS
Mtftp6GetMapping(IN UDP_IO * UdpIo,IN EFI_UDP6_CONFIG_DATA * UdpCfgData)276 Mtftp6GetMapping (
277   IN UDP_IO                 *UdpIo,
278   IN EFI_UDP6_CONFIG_DATA   *UdpCfgData
279   )
280 {
281   EFI_IP6_MODE_DATA         Ip6Mode;
282   EFI_UDP6_PROTOCOL         *Udp6;
283   EFI_STATUS                Status;
284   EFI_EVENT                 Event;
285 
286   Event  = NULL;
287   Udp6   = UdpIo->Protocol.Udp6;
288 
289   //
290   // Create a timer to check whether the Ip6 instance configured or not.
291   //
292   Status = gBS->CreateEvent (
293                   EVT_TIMER,
294                   TPL_CALLBACK,
295                   NULL,
296                   NULL,
297                   &Event
298                   );
299   if (EFI_ERROR (Status)) {
300     goto ON_EXIT;
301   }
302 
303   Status = gBS->SetTimer (
304                   Event,
305                   TimerRelative,
306                   MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND
307                   );
308   if (EFI_ERROR (Status)) {
309     goto ON_EXIT;
310   }
311 
312   //
313   // Check the Ip6 mode data till timeout.
314   //
315   while (EFI_ERROR (gBS->CheckEvent (Event))) {
316 
317     Udp6->Poll (Udp6);
318 
319     Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL);
320 
321     if (!EFI_ERROR (Status)) {
322       if (Ip6Mode.AddressList != NULL) {
323         FreePool (Ip6Mode.AddressList);
324       }
325 
326       if (Ip6Mode.GroupTable != NULL) {
327         FreePool (Ip6Mode.GroupTable);
328       }
329 
330       if (Ip6Mode.RouteTable != NULL) {
331         FreePool (Ip6Mode.RouteTable);
332       }
333 
334       if (Ip6Mode.NeighborCache != NULL) {
335         FreePool (Ip6Mode.NeighborCache);
336       }
337 
338       if (Ip6Mode.PrefixTable != NULL) {
339         FreePool (Ip6Mode.PrefixTable);
340       }
341 
342       if (Ip6Mode.IcmpTypeList != NULL) {
343         FreePool (Ip6Mode.IcmpTypeList);
344       }
345 
346       if  (Ip6Mode.IsConfigured) {
347         //
348         // Continue to configure the Udp6 instance.
349         //
350         Status = Udp6->Configure (Udp6, UdpCfgData);
351       } else {
352         Status = EFI_NO_MAPPING;
353       }
354     }
355   }
356 
357 ON_EXIT:
358 
359   if (Event != NULL) {
360     gBS->CloseEvent (Event);
361   }
362 
363   return Status;
364 }
365 
366 
367 /**
368   The dummy configure routine for create a new Udp6 Io.
369 
370   @param[in]  UdpIo                  The pointer to the Udp6 Io.
371   @param[in]  Context                The pointer to the context.
372 
373   @retval EFI_SUCCESS                This value is always returned.
374 
375 **/
376 EFI_STATUS
377 EFIAPI
Mtftp6ConfigDummyUdpIo(IN UDP_IO * UdpIo,IN VOID * Context)378 Mtftp6ConfigDummyUdpIo (
379   IN UDP_IO                 *UdpIo,
380   IN VOID                   *Context
381   )
382 {
383   return EFI_SUCCESS;
384 }
385 
386 
387 /**
388   The configure routine for Mtftp6 instance to transmit/receive.
389 
390   @param[in]  UdpIo                  The pointer to the Udp6 Io.
391   @param[in]  ServerIp               The pointer to the server address.
392   @param[in]  ServerPort             The pointer to the server port.
393   @param[in]  LocalIp                The pointer to the local address.
394   @param[in]  LocalPort              The pointer to the local port.
395 
396   @retval EFI_SUCCESS            Configured the Udp6 Io for Mtftp6 successfully.
397   @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not been
398                                  configured yet.
399 
400 **/
401 EFI_STATUS
Mtftp6ConfigUdpIo(IN UDP_IO * UdpIo,IN EFI_IPv6_ADDRESS * ServerIp,IN UINT16 ServerPort,IN EFI_IPv6_ADDRESS * LocalIp,IN UINT16 LocalPort)402 Mtftp6ConfigUdpIo (
403   IN UDP_IO                 *UdpIo,
404   IN EFI_IPv6_ADDRESS       *ServerIp,
405   IN UINT16                 ServerPort,
406   IN EFI_IPv6_ADDRESS       *LocalIp,
407   IN UINT16                 LocalPort
408   )
409 {
410   EFI_STATUS                Status;
411   EFI_UDP6_PROTOCOL         *Udp6;
412   EFI_UDP6_CONFIG_DATA      *Udp6Cfg;
413 
414   Udp6    = UdpIo->Protocol.Udp6;
415   Udp6Cfg = &(UdpIo->Config.Udp6);
416 
417   ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));
418 
419   //
420   // Set the Udp6 Io configure data.
421   //
422   Udp6Cfg->AcceptPromiscuous  = FALSE;
423   Udp6Cfg->AcceptAnyPort      = FALSE;
424   Udp6Cfg->AllowDuplicatePort = FALSE;
425   Udp6Cfg->TrafficClass       = 0;
426   Udp6Cfg->HopLimit           = 128;
427   Udp6Cfg->ReceiveTimeout     = 0;
428   Udp6Cfg->TransmitTimeout    = 0;
429   Udp6Cfg->StationPort        = LocalPort;
430   Udp6Cfg->RemotePort         = ServerPort;
431 
432   CopyMem (
433     &Udp6Cfg->StationAddress,
434     LocalIp,
435     sizeof (EFI_IPv6_ADDRESS)
436     );
437 
438   CopyMem (
439     &Udp6Cfg->RemoteAddress,
440     ServerIp,
441     sizeof (EFI_IPv6_ADDRESS)
442     );
443 
444   //
445   // Configure the Udp6 instance with current configure data.
446   //
447   Status = Udp6->Configure (Udp6, Udp6Cfg);
448 
449   if (Status == EFI_NO_MAPPING) {
450 
451     return Mtftp6GetMapping (UdpIo, Udp6Cfg);
452   }
453 
454   return Status;
455 }
456 
457 
458 /**
459   Build and transmit the request packet for the Mtftp6 instance.
460 
461   @param[in]  Instance               The pointer to the Mtftp6 instance.
462   @param[in]  Operation              The operation code of this packet.
463 
464   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the request.
465   @retval EFI_SUCCESS            The request is built and sent.
466   @retval Others                 Failed to transmit the packet.
467 
468 **/
469 EFI_STATUS
Mtftp6SendRequest(IN MTFTP6_INSTANCE * Instance,IN UINT16 Operation)470 Mtftp6SendRequest (
471   IN MTFTP6_INSTANCE        *Instance,
472   IN UINT16                 Operation
473   )
474 {
475   EFI_MTFTP6_PACKET         *Packet;
476   EFI_MTFTP6_OPTION         *Options;
477   EFI_MTFTP6_TOKEN          *Token;
478   RETURN_STATUS             Status;
479   NET_BUF                   *Nbuf;
480   UINT8                     *Mode;
481   UINT8                     *Cur;
482   UINTN                     Index;
483   UINT32                    BufferLength;
484   UINTN                     FileNameLength;
485   UINTN                     ModeLength;
486   UINTN                     OptionStrLength;
487   UINTN                     ValueStrLength;
488 
489   Token   = Instance->Token;
490   Options = Token->OptionList;
491   Mode    = Token->ModeStr;
492 
493   if (Mode == NULL) {
494     Mode = (UINT8 *) "octet";
495   }
496 
497   //
498   // The header format of RRQ/WRQ packet is:
499   //
500   //   2 bytes     string    1 byte     string   1 byte
501   //   ------------------------------------------------
502   //  | Opcode |  Filename  |   0  |    Mode    |   0  |
503   //   ------------------------------------------------
504   //
505   // The common option format is:
506   //
507   //    string     1 byte     string   1 byte
508   //   ---------------------------------------
509   //  | OptionStr |   0  |  ValueStr  |   0   |
510   //   ---------------------------------------
511   //
512 
513   //
514   // Compute the size of new Mtftp6 packet.
515   //
516   FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);
517   ModeLength     = AsciiStrLen ((CHAR8 *) Mode);
518   BufferLength   = (UINT32) FileNameLength + (UINT32) ModeLength + 4;
519 
520   for (Index = 0; Index < Token->OptionCount; Index++) {
521     OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
522     ValueStrLength  = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
523     BufferLength   += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;
524   }
525 
526   //
527   // Allocate a packet then copy the data.
528   //
529   if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {
530     return EFI_OUT_OF_RESOURCES;
531   }
532 
533   //
534   // Copy the opcode, filename and mode into packet.
535   //
536   Packet         = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);
537   ASSERT (Packet != NULL);
538 
539   Packet->OpCode = HTONS (Operation);
540   BufferLength  -= sizeof (Packet->OpCode);
541 
542   Cur            = Packet->Rrq.Filename;
543   Status         = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);
544   ASSERT_EFI_ERROR (Status);
545   BufferLength  -= (UINT32) (FileNameLength + 1);
546   Cur           += FileNameLength + 1;
547   Status         = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);
548   ASSERT_EFI_ERROR (Status);
549   BufferLength  -= (UINT32) (ModeLength + 1);
550   Cur           += ModeLength + 1;
551 
552   //
553   // Copy all the extension options into the packet.
554   //
555   for (Index = 0; Index < Token->OptionCount; ++Index) {
556     OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
557     ValueStrLength  = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
558 
559     Status          = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);
560     ASSERT_EFI_ERROR (Status);
561     BufferLength   -= (UINT32) (OptionStrLength + 1);
562     Cur            += OptionStrLength + 1;
563 
564     Status          = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);
565     ASSERT_EFI_ERROR (Status);
566     BufferLength   -= (UINT32) (ValueStrLength + 1);
567     Cur            += ValueStrLength + 1;
568 
569   }
570 
571   //
572   // Save the packet buf for retransmit
573   //
574   if (Instance->LastPacket != NULL) {
575     NetbufFree (Instance->LastPacket);
576   }
577 
578   Instance->LastPacket = Nbuf;
579   Instance->CurRetry   = 0;
580 
581   return Mtftp6TransmitPacket (Instance, Nbuf);
582 }
583 
584 
585 /**
586   Build and send an error packet.
587 
588   @param[in]  Instance               The pointer to the Mtftp6 instance.
589   @param[in]  ErrCode                The error code in the packet.
590   @param[in]  ErrInfo                The error message in the packet.
591 
592   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the error packet.
593   @retval EFI_SUCCESS            The error packet is transmitted.
594   @retval Others                 Failed to transmit the packet.
595 
596 **/
597 EFI_STATUS
Mtftp6SendError(IN MTFTP6_INSTANCE * Instance,IN UINT16 ErrCode,IN UINT8 * ErrInfo)598 Mtftp6SendError (
599   IN MTFTP6_INSTANCE        *Instance,
600   IN UINT16                 ErrCode,
601   IN UINT8*                 ErrInfo
602   )
603 {
604   NET_BUF                   *Nbuf;
605   EFI_MTFTP6_PACKET         *TftpError;
606   UINT32                    Len;
607 
608   //
609   // Allocate a packet then copy the data.
610   //
611   Len  = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER));
612   Nbuf = NetbufAlloc (Len);
613 
614   if (Nbuf == NULL) {
615     return EFI_OUT_OF_RESOURCES;
616   }
617 
618   TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);
619 
620   if (TftpError == NULL) {
621     NetbufFree (Nbuf);
622     return EFI_OUT_OF_RESOURCES;
623   }
624 
625   TftpError->OpCode          = HTONS (EFI_MTFTP6_OPCODE_ERROR);
626   TftpError->Error.ErrorCode = HTONS (ErrCode);
627 
628   AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, AsciiStrLen ((CHAR8 *) ErrInfo) + 1 , (CHAR8 *) ErrInfo);
629 
630   //
631   // Save the packet buf for retransmit
632   //
633   if (Instance->LastPacket != NULL) {
634     NetbufFree (Instance->LastPacket);
635   }
636 
637   Instance->LastPacket = Nbuf;
638   Instance->CurRetry   = 0;
639 
640   return Mtftp6TransmitPacket (Instance, Nbuf);
641 }
642 
643 
644 /**
645   The callback function called when the packet is transmitted.
646 
647   @param[in]  Packet                 The pointer to the packet.
648   @param[in]  UdpEpt                 The pointer to the Udp6 access point.
649   @param[in]  IoStatus               The result of the transmission.
650   @param[in]  Context                The pointer to the context.
651 
652 **/
653 VOID
654 EFIAPI
Mtftp6OnPacketSent(IN NET_BUF * Packet,IN UDP_END_POINT * UdpEpt,IN EFI_STATUS IoStatus,IN VOID * Context)655 Mtftp6OnPacketSent (
656   IN NET_BUF                   *Packet,
657   IN UDP_END_POINT             *UdpEpt,
658   IN EFI_STATUS                IoStatus,
659   IN VOID                      *Context
660   )
661 {
662   NetbufFree (Packet);
663   *(BOOLEAN *) Context = TRUE;
664 }
665 
666 
667 /**
668   Send the packet for the Mtftp6 instance.
669 
670   @param[in]  Instance               The pointer to the Mtftp6 instance.
671   @param[in]  Packet                 The pointer to the packet to be sent.
672 
673   @retval EFI_SUCCESS            The packet was sent out
674   @retval Others                 Failed to transmit the packet.
675 
676 **/
677 EFI_STATUS
Mtftp6TransmitPacket(IN MTFTP6_INSTANCE * Instance,IN NET_BUF * Packet)678 Mtftp6TransmitPacket (
679   IN MTFTP6_INSTANCE        *Instance,
680   IN NET_BUF                *Packet
681   )
682 {
683   EFI_UDP6_PROTOCOL         *Udp6;
684   EFI_UDP6_CONFIG_DATA      Udp6CfgData;
685   EFI_STATUS                Status;
686   UINT16                    *Temp;
687   UINT16                    Value;
688   UINT16                    OpCode;
689 
690   ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA));
691   Udp6 = Instance->UdpIo->Protocol.Udp6;
692 
693   //
694   // Set the live time of the packet.
695   //
696   Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2);
697 
698   Temp   = (UINT16 *) NetbufGetByte (Packet, 0, NULL);
699   ASSERT (Temp != NULL);
700 
701   Value  = *Temp;
702   OpCode = NTOHS (Value);
703 
704   if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) {
705     //
706     // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as
707     // (serverip, 69, localip, localport) to send.
708     // Usually local address and local port are both default as zero.
709     //
710     Status = Udp6->Configure (Udp6, NULL);
711 
712     if (EFI_ERROR (Status)) {
713       return Status;
714     }
715 
716     Status = Mtftp6ConfigUdpIo (
717                Instance->UdpIo,
718                &Instance->ServerIp,
719                Instance->ServerCmdPort,
720                &Instance->Config->StationIp,
721                Instance->Config->LocalPort
722                );
723 
724     if (EFI_ERROR (Status)) {
725       return Status;
726     }
727 
728     //
729     // Get the current local address and port by get Udp6 mode data.
730     //
731     Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);
732     if (EFI_ERROR (Status)) {
733       return Status;
734     }
735 
736     NET_GET_REF (Packet);
737 
738     Instance->IsTransmitted = FALSE;
739 
740     Status = UdpIoSendDatagram (
741                Instance->UdpIo,
742                Packet,
743                NULL,
744                NULL,
745                Mtftp6OnPacketSent,
746                &Instance->IsTransmitted
747                );
748 
749     if (EFI_ERROR (Status)) {
750       NET_PUT_REF (Packet);
751       return Status;
752     }
753 
754     //
755     // Poll till the packet sent out from the ip6 queue.
756     //
757     gBS->RestoreTPL (Instance->OldTpl);
758 
759     while (!Instance->IsTransmitted) {
760       Udp6->Poll (Udp6);
761     }
762 
763     Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
764 
765     //
766     // For the subsequent exchange of such requests, reconfigure the Udp6Io as
767     // (serverip, 0, localip, localport) to receive.
768     // Currently local address and local port are specified by Udp6 mode data.
769     //
770     Status = Udp6->Configure (Udp6, NULL);
771 
772     if (EFI_ERROR (Status)) {
773       return Status;
774     }
775 
776     Status = Mtftp6ConfigUdpIo (
777                Instance->UdpIo,
778                &Instance->ServerIp,
779                Instance->ServerDataPort,
780                &Udp6CfgData.StationAddress,
781                Udp6CfgData.StationPort
782                );
783   } else {
784     //
785     // For the data exchange, configure the Udp6Io as (serverip, dataport,
786     // localip, localport) to send/receive.
787     // Currently local address and local port are specified by Udp6 mode data.
788     //
789     Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);
790     if (EFI_ERROR (Status)) {
791       return Status;
792     }
793 
794     if (Udp6CfgData.RemotePort != Instance->ServerDataPort) {
795 
796       Status = Udp6->Configure (Udp6, NULL);
797 
798       if (EFI_ERROR (Status)) {
799         return Status;
800       }
801 
802       Status = Mtftp6ConfigUdpIo (
803                  Instance->UdpIo,
804                  &Instance->ServerIp,
805                  Instance->ServerDataPort,
806                  &Udp6CfgData.StationAddress,
807                  Udp6CfgData.StationPort
808                  );
809 
810       if (EFI_ERROR (Status)) {
811         return Status;
812       }
813     }
814 
815     NET_GET_REF (Packet);
816 
817     Instance->IsTransmitted = FALSE;
818 
819     Status = UdpIoSendDatagram (
820                Instance->UdpIo,
821                Packet,
822                NULL,
823                NULL,
824                Mtftp6OnPacketSent,
825                &Instance->IsTransmitted
826                );
827 
828     if (EFI_ERROR (Status)) {
829       NET_PUT_REF (Packet);
830     }
831 
832     //
833     // Poll till the packet sent out from the ip6 queue.
834     //
835     gBS->RestoreTPL (Instance->OldTpl);
836 
837     while (!Instance->IsTransmitted) {
838       Udp6->Poll (Udp6);
839     }
840 
841     Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
842   }
843 
844   return Status;
845 }
846 
847 
848 /**
849   Check packet for GetInfo callback routine.
850 
851   GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect
852   the first packet from server, then abort the session.
853 
854   @param[in]  This                   The pointer to the Mtftp6 protocol.
855   @param[in]  Token                  The pointer to the Mtftp6 token.
856   @param[in]  PacketLen              The length of the packet.
857   @param[in]  Packet                 The pointer to the received packet.
858 
859   @retval EFI_ABORTED            Abort the Mtftp6 operation.
860 
861 **/
862 EFI_STATUS
863 EFIAPI
Mtftp6CheckPacket(IN EFI_MTFTP6_PROTOCOL * This,IN EFI_MTFTP6_TOKEN * Token,IN UINT16 PacketLen,IN EFI_MTFTP6_PACKET * Packet)864 Mtftp6CheckPacket (
865   IN EFI_MTFTP6_PROTOCOL    *This,
866   IN EFI_MTFTP6_TOKEN       *Token,
867   IN UINT16                 PacketLen,
868   IN EFI_MTFTP6_PACKET      *Packet
869   )
870 {
871   MTFTP6_GETINFO_CONTEXT    *Context;
872   UINT16                    OpCode;
873 
874   Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context;
875   OpCode  = NTOHS (Packet->OpCode);
876 
877   //
878   // Set the GetInfo's return status according to the OpCode.
879   //
880   switch (OpCode) {
881   case EFI_MTFTP6_OPCODE_ERROR:
882     Context->Status = EFI_TFTP_ERROR;
883     break;
884 
885   case EFI_MTFTP6_OPCODE_OACK:
886     Context->Status = EFI_SUCCESS;
887     break;
888 
889   default:
890     Context->Status = EFI_PROTOCOL_ERROR;
891   }
892 
893   //
894   // Allocate buffer then copy the packet over. Use gBS->AllocatePool
895   // in case NetAllocatePool will implements something tricky.
896   //
897   *(Context->Packet) = AllocateZeroPool (PacketLen);
898 
899   if (*(Context->Packet) == NULL) {
900     Context->Status = EFI_OUT_OF_RESOURCES;
901     return EFI_ABORTED;
902   }
903 
904   *(Context->PacketLen) = PacketLen;
905   CopyMem (*(Context->Packet), Packet, PacketLen);
906 
907   return EFI_ABORTED;
908 }
909 
910 
911 /**
912   Clean up the current Mtftp6 operation.
913 
914   @param[in]  Instance               The pointer to the Mtftp6 instance.
915   @param[in]  Result                 The result to be returned to the user.
916 
917 **/
918 VOID
Mtftp6OperationClean(IN MTFTP6_INSTANCE * Instance,IN EFI_STATUS Result)919 Mtftp6OperationClean (
920   IN MTFTP6_INSTANCE        *Instance,
921   IN EFI_STATUS             Result
922   )
923 {
924   LIST_ENTRY                *Entry;
925   LIST_ENTRY                *Next;
926   MTFTP6_BLOCK_RANGE        *Block;
927 
928   //
929   // Clean up the current token and event.
930   //
931   if (Instance->Token != NULL) {
932     Instance->Token->Status = Result;
933     if (Instance->Token->Event != NULL) {
934       gBS->SignalEvent (Instance->Token->Event);
935     }
936     Instance->Token = NULL;
937   }
938 
939   //
940   // Clean up the corresponding Udp6Io.
941   //
942   if (Instance->UdpIo != NULL) {
943     UdpIoCleanIo (Instance->UdpIo);
944   }
945 
946   if (Instance->McastUdpIo != NULL) {
947     gBS->CloseProtocol (
948            Instance->McastUdpIo->UdpHandle,
949            &gEfiUdp6ProtocolGuid,
950            Instance->McastUdpIo->Image,
951            Instance->Handle
952            );
953     UdpIoFreeIo (Instance->McastUdpIo);
954     Instance->McastUdpIo = NULL;
955   }
956 
957   //
958   // Clean up the stored last packet.
959   //
960   if (Instance->LastPacket != NULL) {
961     NetbufFree (Instance->LastPacket);
962     Instance->LastPacket = NULL;
963   }
964 
965   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {
966     Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);
967     RemoveEntryList (Entry);
968     FreePool (Block);
969   }
970 
971   //
972   // Reinitialize the corresponding fields of the Mtftp6 operation.
973   //
974   ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));
975   ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS));
976   ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));
977 
978   Instance->ServerCmdPort  = 0;
979   Instance->ServerDataPort = 0;
980   Instance->McastPort      = 0;
981   Instance->BlkSize        = 0;
982   Instance->LastBlk        = 0;
983   Instance->PacketToLive   = 0;
984   Instance->MaxRetry       = 0;
985   Instance->CurRetry       = 0;
986   Instance->Timeout        = 0;
987   Instance->IsMaster       = TRUE;
988 }
989 
990 
991 /**
992   Start the Mtftp6 instance to perform the operation, such as read file,
993   write file, and read directory.
994 
995   @param[in]  This                   The MTFTP session.
996   @param[in]  Token                  The token than encapsues the user's request.
997   @param[in]  OpCode                 The operation to perform.
998 
999   @retval EFI_INVALID_PARAMETER  Some of the parameters are invalid.
1000   @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.
1001   @retval EFI_ALREADY_STARTED    There is pending operation for the session.
1002   @retval EFI_SUCCESS            The operation is successfully started.
1003 
1004 **/
1005 EFI_STATUS
Mtftp6OperationStart(IN EFI_MTFTP6_PROTOCOL * This,IN EFI_MTFTP6_TOKEN * Token,IN UINT16 OpCode)1006 Mtftp6OperationStart (
1007   IN EFI_MTFTP6_PROTOCOL    *This,
1008   IN EFI_MTFTP6_TOKEN       *Token,
1009   IN UINT16                 OpCode
1010   )
1011 {
1012   MTFTP6_INSTANCE           *Instance;
1013   EFI_STATUS                Status;
1014 
1015   if (This == NULL ||
1016       Token == NULL ||
1017       Token->Filename == NULL ||
1018       (Token->OptionCount != 0 && Token->OptionList == NULL) ||
1019       (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp))
1020       ) {
1021     return EFI_INVALID_PARAMETER;
1022   }
1023 
1024   //
1025   // At least define one method to collect the data for download.
1026   //
1027   if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) &&
1028       Token->Buffer == NULL &&
1029       Token->CheckPacket == NULL
1030       ) {
1031     return EFI_INVALID_PARAMETER;
1032   }
1033 
1034   //
1035   // At least define one method to provide the data for upload.
1036   //
1037   if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) {
1038     return EFI_INVALID_PARAMETER;
1039   }
1040 
1041   Instance = MTFTP6_INSTANCE_FROM_THIS (This);
1042 
1043   if (Instance->Config == NULL) {
1044     return EFI_NOT_STARTED;
1045   }
1046 
1047   if (Instance->Token != NULL) {
1048     return EFI_ACCESS_DENIED;
1049   }
1050 
1051   Status           = EFI_SUCCESS;
1052   Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1053 
1054   //
1055   // Parse the extension options in the request packet.
1056   //
1057   if (Token->OptionCount != 0) {
1058 
1059     Status = Mtftp6ParseExtensionOption (
1060                Token->OptionList,
1061                Token->OptionCount,
1062                TRUE,
1063                &Instance->ExtInfo
1064                );
1065 
1066     if (EFI_ERROR (Status)) {
1067       goto ON_ERROR;
1068     }
1069   }
1070 
1071   //
1072   // Initialize runtime data from config data or override data.
1073   //
1074   Instance->Token           = Token;
1075   Instance->ServerCmdPort   = Instance->Config->InitialServerPort;
1076   Instance->ServerDataPort  = 0;
1077   Instance->MaxRetry        = Instance->Config->TryCount;
1078   Instance->Timeout         = Instance->Config->TimeoutValue;
1079   Instance->IsMaster        = TRUE;
1080 
1081   CopyMem (
1082     &Instance->ServerIp,
1083     &Instance->Config->ServerIp,
1084     sizeof (EFI_IPv6_ADDRESS)
1085     );
1086 
1087   if (Token->OverrideData != NULL) {
1088     Instance->ServerCmdPort = Token->OverrideData->ServerPort;
1089     Instance->MaxRetry      = Token->OverrideData->TryCount;
1090     Instance->Timeout       = Token->OverrideData->TimeoutValue;
1091 
1092     CopyMem (
1093       &Instance->ServerIp,
1094       &Token->OverrideData->ServerIp,
1095       sizeof (EFI_IPv6_ADDRESS)
1096       );
1097   }
1098 
1099   //
1100   // Set default value for undefined parameters.
1101   //
1102   if (Instance->ServerCmdPort == 0) {
1103     Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT;
1104   }
1105   if (Instance->BlkSize == 0) {
1106     Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE;
1107   }
1108   if (Instance->MaxRetry == 0) {
1109     Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY;
1110   }
1111   if (Instance->Timeout == 0) {
1112     Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT;
1113   }
1114 
1115   Token->Status = EFI_NOT_READY;
1116 
1117   //
1118   // Switch the routines by the operation code.
1119   //
1120   switch (OpCode) {
1121   case EFI_MTFTP6_OPCODE_RRQ:
1122     Status = Mtftp6RrqStart (Instance, OpCode);
1123     break;
1124 
1125   case EFI_MTFTP6_OPCODE_DIR:
1126     Status = Mtftp6RrqStart (Instance, OpCode);
1127     break;
1128 
1129   case EFI_MTFTP6_OPCODE_WRQ:
1130     Status = Mtftp6WrqStart (Instance, OpCode);
1131     break;
1132 
1133   default:
1134     Status = EFI_DEVICE_ERROR;
1135     goto ON_ERROR;
1136   }
1137 
1138   if (EFI_ERROR (Status)) {
1139     goto ON_ERROR;
1140   }
1141 
1142   //
1143   // Return immediately for asynchronous or poll the instance for synchronous.
1144   //
1145   gBS->RestoreTPL (Instance->OldTpl);
1146 
1147   if (Token->Event == NULL) {
1148     while (Token->Status == EFI_NOT_READY) {
1149       This->Poll (This);
1150     }
1151     return Token->Status;
1152   }
1153 
1154   return EFI_SUCCESS;
1155 
1156 ON_ERROR:
1157 
1158   Mtftp6OperationClean (Instance, Status);
1159   gBS->RestoreTPL (Instance->OldTpl);
1160 
1161   return Status;
1162 }
1163 
1164 
1165 /**
1166   The timer ticking routine for the Mtftp6 instance.
1167 
1168   @param[in]  Event                  The pointer to the ticking event.
1169   @param[in]  Context                The pointer to the context.
1170 
1171 **/
1172 VOID
1173 EFIAPI
Mtftp6OnTimerTick(IN EFI_EVENT Event,IN VOID * Context)1174 Mtftp6OnTimerTick (
1175   IN EFI_EVENT              Event,
1176   IN VOID                   *Context
1177   )
1178 {
1179   MTFTP6_SERVICE            *Service;
1180   MTFTP6_INSTANCE           *Instance;
1181   LIST_ENTRY                *Entry;
1182   LIST_ENTRY                *Next;
1183   EFI_MTFTP6_TOKEN          *Token;
1184   EFI_STATUS                Status;
1185 
1186   Service = (MTFTP6_SERVICE *) Context;
1187 
1188   //
1189   // Iterate through all the children of the Mtftp service instance. Time
1190   // out the packet. If maximum retries reached, clean the session up.
1191   //
1192   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) {
1193 
1194     Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link);
1195 
1196     if (Instance->Token == NULL) {
1197       continue;
1198     }
1199 
1200     if (Instance->PacketToLive > 0) {
1201       Instance->PacketToLive--;
1202       continue;
1203     }
1204 
1205     Instance->CurRetry++;
1206     Token = Instance->Token;
1207 
1208     if (Token->TimeoutCallback != NULL) {
1209       //
1210       // Call the timeout callback routine if has.
1211       //
1212       Status = Token->TimeoutCallback (&Instance->Mtftp6, Token);
1213 
1214       if (EFI_ERROR (Status)) {
1215         Mtftp6SendError (
1216            Instance,
1217            EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,
1218            (UINT8 *) "User aborted the transfer in time out"
1219            );
1220         Mtftp6OperationClean (Instance, EFI_ABORTED);
1221         continue;
1222       }
1223     }
1224 
1225     //
1226     // Retransmit the packet if haven't reach the maxmium retry count,
1227     // otherwise exit the transfer.
1228     //
1229     if (Instance->CurRetry < Instance->MaxRetry) {
1230       Mtftp6TransmitPacket (Instance, Instance->LastPacket);
1231     } else {
1232       Mtftp6OperationClean (Instance, EFI_TIMEOUT);
1233       continue;
1234     }
1235   }
1236 }
1237