ShellPkg/Dp: Add null pointer check
[mirror_edk2.git] / ShellPkg / Library / UefiShellNetwork2CommandsLib / Ping6.c
1 /** @file\r
2   The implementation for Ping6 application.\r
3 \r
4   Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>\r
5 \r
6   This program and the accompanying materials\r
7   are licensed and made available under the terms and conditions of the BSD License\r
8   which accompanies this distribution.  The full text of the license may be found at\r
9   http://opensource.org/licenses/bsd-license.php.\r
10 \r
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 \r
14 **/\r
15 \r
16 #include "UefiShellNetwork2CommandsLib.h"\r
17 \r
18 #define PING6_DEFAULT_TIMEOUT      5000\r
19 #define PING6_MAX_SEND_NUMBER      10000\r
20 #define PING6_MAX_BUFFER_SIZE      32768\r
21 #define PING6_ONE_SECOND           10000000\r
22 #define STALL_1_MILLI_SECOND  1000\r
23 \r
24 #pragma pack(1)\r
25 \r
26 typedef struct _ICMP6_ECHO_REQUEST_REPLY {\r
27   UINT8                       Type;\r
28   UINT8                       Code;\r
29   UINT16                      Checksum;\r
30   UINT16                      Identifier;\r
31   UINT16                      SequenceNum;\r
32   UINT32                      TimeStamp;\r
33   UINT8                       Data[1];\r
34 } ICMP6_ECHO_REQUEST_REPLY;\r
35 \r
36 #pragma pack()\r
37 \r
38 typedef struct _PING6_ICMP6_TX_INFO {\r
39   LIST_ENTRY                  Link;\r
40   UINT16                      SequenceNum;\r
41   UINT32                      TimeStamp;\r
42   EFI_IP6_COMPLETION_TOKEN    *Token;\r
43 } PING6_ICMP6_TX_INFO;\r
44 \r
45 typedef struct _PING6_PRIVATE_DATA {\r
46   EFI_HANDLE                  ImageHandle;\r
47   EFI_HANDLE                  NicHandle;\r
48   EFI_HANDLE                  Ip6ChildHandle;\r
49   EFI_IP6_PROTOCOL            *Ip6;\r
50   EFI_EVENT                   Timer;\r
51 \r
52   UINT32                      TimerPeriod;\r
53   UINT32                      RttTimerTick;   \r
54   EFI_EVENT                   RttTimer;\r
55 \r
56   EFI_STATUS                  Status;\r
57   LIST_ENTRY                  TxList;\r
58   EFI_IP6_COMPLETION_TOKEN    RxToken;\r
59   UINT16                      RxCount;\r
60   UINT16                      TxCount;\r
61   UINT64                      RttSum;\r
62   UINT64                      RttMin;\r
63   UINT64                      RttMax;\r
64   UINT32                      SequenceNum;\r
65 \r
66   EFI_IPv6_ADDRESS            SrcAddress;\r
67   EFI_IPv6_ADDRESS            DstAddress;\r
68   UINT32                      SendNum;\r
69   UINT32                      BufferSize;\r
70 } PING6_PRIVATE_DATA;\r
71 \r
72 \r
73 SHELL_PARAM_ITEM    Ping6ParamList[] = {\r
74   {\r
75     L"-l",\r
76     TypeValue\r
77   },\r
78   {\r
79     L"-n",\r
80     TypeValue\r
81   },\r
82   {\r
83     L"-s",\r
84     TypeValue\r
85   },\r
86   {\r
87     L"-?",\r
88     TypeFlag\r
89   },\r
90   {\r
91     NULL,\r
92     TypeMax\r
93   },\r
94 };\r
95 \r
96 //\r
97 // Global Variables in Ping6 application.\r
98 //\r
99 CONST CHAR16            *mIp6DstString;\r
100 CONST CHAR16            *mIp6SrcString;\r
101 EFI_CPU_ARCH_PROTOCOL   *Cpu = NULL;\r
102 \r
103 /**\r
104   RTT timer tick routine.\r
105 \r
106   @param[in]    Event    A EFI_EVENT type event.\r
107   @param[in]    Context  The pointer to Context.\r
108 \r
109 **/\r
110 VOID\r
111 EFIAPI\r
112 Ping6RttTimerTickRoutine (\r
113   IN EFI_EVENT    Event,\r
114   IN VOID         *Context\r
115   )\r
116 {\r
117   UINT32     *RttTimerTick;\r
118 \r
119   RttTimerTick = (UINT32*) Context;\r
120   (*RttTimerTick)++;\r
121 }\r
122 \r
123 /**\r
124   Get the timer period of the system.\r
125 \r
126   This function tries to get the system timer period by creating\r
127   an 1ms period timer.\r
128 \r
129   @return     System timer period in MS, or 0 if operation failed.\r
130 \r
131 **/\r
132 UINT32\r
133 Ping6GetTimerPeriod(\r
134   VOID\r
135   )\r
136 {\r
137   EFI_STATUS                 Status;\r
138   UINT32                     RttTimerTick;\r
139   EFI_EVENT                  TimerEvent;\r
140   UINT32                     StallCounter;\r
141   EFI_TPL                    OldTpl;\r
142 \r
143   RttTimerTick = 0;\r
144   StallCounter   = 0;\r
145 \r
146   Status = gBS->CreateEvent (\r
147                   EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
148                   TPL_NOTIFY,\r
149                   Ping6RttTimerTickRoutine,\r
150                   &RttTimerTick,\r
151                   &TimerEvent\r
152                   );\r
153   if (EFI_ERROR (Status)) {\r
154     return 0;\r
155   }\r
156 \r
157   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
158   Status = gBS->SetTimer (\r
159                   TimerEvent,\r
160                   TimerPeriodic,\r
161                   TICKS_PER_MS\r
162                   );\r
163   if (EFI_ERROR (Status)) {\r
164     gBS->CloseEvent (TimerEvent);\r
165     return 0;\r
166   }\r
167 \r
168   while (RttTimerTick < 10) {\r
169     gBS->Stall (STALL_1_MILLI_SECOND);\r
170     ++StallCounter;\r
171   }\r
172 \r
173   gBS->RestoreTPL (OldTpl);\r
174 \r
175   gBS->SetTimer (TimerEvent, TimerCancel, 0);\r
176   gBS->CloseEvent (TimerEvent);\r
177 \r
178   return StallCounter / RttTimerTick;\r
179 }\r
180 \r
181 \r
182 /**\r
183   Initialize the timer event for RTT (round trip time).\r
184 \r
185   @param[in]    Private    The pointer to PING6_PRIVATE_DATA.\r
186 \r
187   @retval EFI_SUCCESS      RTT timer is started.\r
188   @retval Others           Failed to start the RTT timer.\r
189 \r
190 **/\r
191 EFI_STATUS\r
192 Ping6InitRttTimer (\r
193   IN  PING6_PRIVATE_DATA      *Private\r
194   )\r
195 {\r
196   EFI_STATUS                 Status;\r
197 \r
198   Private->TimerPeriod = Ping6GetTimerPeriod ();\r
199   if (Private->TimerPeriod == 0) {\r
200     return EFI_ABORTED;\r
201   }\r
202   \r
203   Private->RttTimerTick = 0;\r
204   Status = gBS->CreateEvent (\r
205                   EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
206                   TPL_NOTIFY,\r
207                   Ping6RttTimerTickRoutine,\r
208                   &Private->RttTimerTick,\r
209                   &Private->RttTimer\r
210                   );\r
211   if (EFI_ERROR (Status)) {\r
212     return Status;\r
213   }\r
214 \r
215   Status = gBS->SetTimer (\r
216                   Private->RttTimer,\r
217                   TimerPeriodic,\r
218                   TICKS_PER_MS\r
219                   );\r
220   if (EFI_ERROR (Status)) {\r
221     gBS->CloseEvent (Private->RttTimer);\r
222     return Status;\r
223   }\r
224 \r
225   return EFI_SUCCESS;\r
226 \r
227 }\r
228 \r
229 /**\r
230   Free RTT timer event resource.\r
231 \r
232   @param[in]    Private    The pointer to PING6_PRIVATE_DATA.\r
233 \r
234 **/\r
235 VOID\r
236 Ping6FreeRttTimer (\r
237   IN  PING6_PRIVATE_DATA      *Private\r
238   )\r
239 {\r
240   if (Private->RttTimer != NULL) {\r
241     gBS->SetTimer (Private->RttTimer, TimerCancel, 0);\r
242     gBS->CloseEvent (Private->RttTimer);\r
243   }\r
244 }\r
245 \r
246 /**\r
247   Read the current time.\r
248   \r
249   @param[in]    Private    The pointer to PING6_PRIVATE_DATA.\r
250 \r
251   @retval the current tick value.\r
252 **/\r
253 UINT32\r
254 Ping6ReadTime (\r
255   IN  PING6_PRIVATE_DATA      *Private\r
256   )\r
257 {\r
258   return Private->RttTimerTick;\r
259 }\r
260 \r
261 /**\r
262   Get and calculate the duration in ms.\r
263 \r
264   @param[in]  Private  The pointer to PING6_PRIVATE_DATA.\r
265   @param[in]  Begin    The start point of time.\r
266   @param[in]  End      The end point of time.\r
267 \r
268   @return The duration in ms.\r
269 \r
270 **/\r
271 UINT32\r
272 Ping6CalculateTick (\r
273   IN PING6_PRIVATE_DATA      *Private,\r
274   IN UINT32    Begin,\r
275   IN UINT32    End\r
276   )\r
277 {\r
278   if (End < Begin) {\r
279     return (0);\r
280   }\r
281 \r
282   return (End - Begin) * Private->TimerPeriod;\r
283 \r
284 }\r
285 \r
286 /**\r
287   Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.\r
288 \r
289   @param[in]    TxInfo    The pointer to PING6_ICMP6_TX_INFO.\r
290 \r
291 **/\r
292 VOID\r
293 Ping6DestroyTxInfo (\r
294   IN PING6_ICMP6_TX_INFO    *TxInfo\r
295   )\r
296 {\r
297   EFI_IP6_TRANSMIT_DATA    *TxData;\r
298   EFI_IP6_FRAGMENT_DATA    *FragData;\r
299   UINTN                    Index;\r
300 \r
301   ASSERT (TxInfo != NULL);\r
302 \r
303   if (TxInfo->Token != NULL) {\r
304 \r
305     if (TxInfo->Token->Event != NULL) {\r
306       gBS->CloseEvent (TxInfo->Token->Event);\r
307     }\r
308 \r
309     TxData = TxInfo->Token->Packet.TxData;\r
310     if (TxData != NULL) {\r
311 \r
312       if (TxData->OverrideData != NULL) {\r
313         FreePool (TxData->OverrideData);\r
314       }\r
315 \r
316       if (TxData->ExtHdrs != NULL) {\r
317         FreePool (TxData->ExtHdrs);\r
318       }\r
319 \r
320       for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
321         FragData = TxData->FragmentTable[Index].FragmentBuffer;\r
322         if (FragData != NULL) {\r
323           FreePool (FragData);\r
324         }\r
325       }\r
326     }\r
327 \r
328     FreePool (TxInfo->Token);\r
329   }\r
330 \r
331   FreePool (TxInfo);\r
332 }\r
333 \r
334 /**\r
335   Match the request, and reply with SequenceNum/TimeStamp.\r
336 \r
337   @param[in]    Private    The pointer to PING6_PRIVATE_DATA.\r
338   @param[in]    Packet     The pointer to ICMP6_ECHO_REQUEST_REPLY.\r
339 \r
340   @retval EFI_SUCCESS      The match is successful.\r
341   @retval EFI_NOT_FOUND    The reply can't be matched with any request.\r
342 \r
343 **/\r
344 EFI_STATUS\r
345 Ping6OnMatchEchoReply (\r
346   IN PING6_PRIVATE_DATA          *Private,\r
347   IN ICMP6_ECHO_REQUEST_REPLY    *Packet\r
348   )\r
349 {\r
350   PING6_ICMP6_TX_INFO    *TxInfo;\r
351   LIST_ENTRY             *Entry;\r
352   LIST_ENTRY             *NextEntry;\r
353 \r
354   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
355     TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
356 \r
357     if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {\r
358       Private->RxCount++;\r
359       RemoveEntryList (&TxInfo->Link);\r
360       Ping6DestroyTxInfo (TxInfo);\r
361       return EFI_SUCCESS;\r
362     }\r
363   }\r
364 \r
365   return EFI_NOT_FOUND;\r
366 }\r
367 \r
368 /**\r
369   The original intention is to send a request.\r
370   Currently, the application retransmits an icmp6 echo request packet\r
371   per second in sendnumber times that is specified by the user.\r
372   Because nothing can be done here, all things move to the timer rountine.\r
373 \r
374   @param[in]    Event      A EFI_EVENT type event.\r
375   @param[in]    Context    The pointer to Context.\r
376 \r
377 **/\r
378 VOID\r
379 EFIAPI\r
380 Ping6OnEchoRequestSent6 (\r
381   IN EFI_EVENT    Event,\r
382   IN VOID         *Context\r
383   )\r
384 {\r
385 }\r
386 \r
387 /**\r
388   receive reply, match and print reply infomation.\r
389 \r
390   @param[in]    Event      A EFI_EVENT type event.\r
391   @param[in]    Context    The pointer to context.\r
392 \r
393 **/\r
394 VOID\r
395 EFIAPI\r
396 Ping6OnEchoReplyReceived6 (\r
397   IN EFI_EVENT    Event,\r
398   IN VOID         *Context\r
399   )\r
400 {\r
401   EFI_STATUS                  Status;\r
402   PING6_PRIVATE_DATA          *Private;\r
403   EFI_IP6_COMPLETION_TOKEN    *RxToken;\r
404   EFI_IP6_RECEIVE_DATA        *RxData;\r
405   ICMP6_ECHO_REQUEST_REPLY    *Reply;\r
406   UINT32                      PayLoad;\r
407   UINT32                      Rtt;\r
408 \r
409   Private = (PING6_PRIVATE_DATA *) Context;\r
410 \r
411   if (Private->Status == EFI_ABORTED) {\r
412     return;\r
413   }\r
414 \r
415   RxToken = &Private->RxToken;\r
416   RxData  = RxToken->Packet.RxData;\r
417   Reply   = RxData->FragmentTable[0].FragmentBuffer;\r
418   PayLoad = RxData->DataLength;\r
419 \r
420   if (RxData->Header->NextHeader != IP6_ICMP) {\r
421     goto ON_EXIT;\r
422   }\r
423 \r
424   if (!IP6_IS_MULTICAST (&Private->DstAddress) &&\r
425       !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {\r
426     goto ON_EXIT;\r
427   }\r
428 \r
429   if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {\r
430     goto ON_EXIT;\r
431   }\r
432 \r
433   if (PayLoad != Private->BufferSize) {\r
434     goto ON_EXIT;\r
435   }\r
436   //\r
437   // Check whether the reply matches the sent request before.\r
438   //\r
439   Status = Ping6OnMatchEchoReply (Private, Reply);\r
440   if (EFI_ERROR(Status)) {\r
441     goto ON_EXIT;\r
442   }\r
443   //\r
444   // Display statistics on this icmp6 echo reply packet.\r
445   //\r
446   Rtt  = Ping6CalculateTick (Private, Reply->TimeStamp, Ping6ReadTime (Private));\r
447 \r
448   Private->RttSum += Rtt;\r
449   Private->RttMin  = Private->RttMin > Rtt ? Rtt : Private->RttMin;\r
450   Private->RttMax  = Private->RttMax < Rtt ? Rtt : Private->RttMax;\r
451 \r
452   ShellPrintHiiEx (\r
453     -1,\r
454     -1,\r
455     NULL,\r
456     STRING_TOKEN (STR_PING6_REPLY_INFO),\r
457     gShellNetwork2HiiHandle,\r
458     PayLoad,\r
459     mIp6DstString,\r
460     Reply->SequenceNum,\r
461     RxData->Header->HopLimit,\r
462     Rtt,\r
463     Rtt + Private->TimerPeriod\r
464     );\r
465 \r
466 ON_EXIT:\r
467 \r
468   if (Private->RxCount < Private->SendNum) {\r
469     //\r
470     // Continue to receive icmp6 echo reply packets.\r
471     //\r
472     RxToken->Status = EFI_ABORTED;\r
473 \r
474     Status = Private->Ip6->Receive (Private->Ip6, RxToken);\r
475 \r
476     if (EFI_ERROR (Status)) {\r
477       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_RECEIVE), gShellNetwork2HiiHandle, Status);\r
478       Private->Status = EFI_ABORTED;\r
479     }\r
480   } else {\r
481     //\r
482     // All reply have already been received from the dest host.\r
483     //\r
484     Private->Status = EFI_SUCCESS;\r
485   }\r
486   //\r
487   // Singal to recycle the each rxdata here, not at the end of process.\r
488   //\r
489   gBS->SignalEvent (RxData->RecycleSignal);\r
490 }\r
491 \r
492 /**\r
493   Initial EFI_IP6_COMPLETION_TOKEN.\r
494 \r
495   @param[in]    Private        The pointer of PING6_PRIVATE_DATA.\r
496   @param[in]    TimeStamp      The TimeStamp of request.\r
497   @param[in]    SequenceNum    The SequenceNum of request.\r
498 \r
499   @return The pointer of EFI_IP6_COMPLETION_TOKEN.\r
500 \r
501 **/\r
502 EFI_IP6_COMPLETION_TOKEN *\r
503 Ping6GenerateToken (\r
504   IN PING6_PRIVATE_DATA    *Private,\r
505   IN UINT32                TimeStamp,\r
506   IN UINT16                SequenceNum\r
507   )\r
508 {\r
509   EFI_STATUS                  Status;\r
510   EFI_IP6_COMPLETION_TOKEN    *Token;\r
511   EFI_IP6_TRANSMIT_DATA       *TxData;\r
512   ICMP6_ECHO_REQUEST_REPLY    *Request;\r
513 \r
514   Request = AllocateZeroPool (Private->BufferSize);\r
515 \r
516   if (Request == NULL) {\r
517     return NULL;\r
518   }\r
519   //\r
520   // Assembly icmp6 echo request packet.\r
521   //\r
522   Request->Type        = ICMP_V6_ECHO_REQUEST;\r
523   Request->Code        = 0;\r
524   Request->SequenceNum = SequenceNum;\r
525   Request->TimeStamp   = TimeStamp;\r
526   Request->Identifier  = 0;\r
527   //\r
528   // Leave check sum to ip6 layer, since it has no idea of source address\r
529   // selection.\r
530   //\r
531   Request->Checksum    = 0;\r
532 \r
533   TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));\r
534 \r
535   if (TxData == NULL) {\r
536     FreePool (Request);\r
537     return NULL;\r
538   }\r
539   //\r
540   // Assembly ipv6 token for transmit.\r
541   //\r
542   TxData->OverrideData       = 0;\r
543   TxData->ExtHdrsLength      = 0;\r
544   TxData->ExtHdrs            = NULL;\r
545   TxData->DataLength         = Private->BufferSize;\r
546   TxData->FragmentCount      = 1;\r
547   TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;\r
548   TxData->FragmentTable[0].FragmentLength = Private->BufferSize;\r
549 \r
550   Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));\r
551 \r
552   if (Token == NULL) {\r
553     FreePool (Request);\r
554     FreePool (TxData);\r
555     return NULL;\r
556   }\r
557 \r
558   Token->Status         = EFI_ABORTED;\r
559   Token->Packet.TxData  = TxData;\r
560 \r
561   Status = gBS->CreateEvent (\r
562                   EVT_NOTIFY_SIGNAL,\r
563                   TPL_CALLBACK,\r
564                   Ping6OnEchoRequestSent6,\r
565                   Private,\r
566                   &Token->Event\r
567                   );\r
568 \r
569   if (EFI_ERROR (Status)) {\r
570     FreePool (Request);\r
571     FreePool (TxData);\r
572     FreePool (Token);\r
573     return NULL;\r
574   }\r
575 \r
576   return Token;\r
577 }\r
578 \r
579 /**\r
580   Transmit the EFI_IP6_COMPLETION_TOKEN.\r
581 \r
582   @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
583 \r
584   @retval EFI_SUCCESS             Transmitted successfully.\r
585   @retval EFI_OUT_OF_RESOURCES    No memory is available on the platform.\r
586   @retval others                  Transmitted unsuccessfully.\r
587 \r
588 **/\r
589 EFI_STATUS\r
590 Ping6SendEchoRequest (\r
591   IN PING6_PRIVATE_DATA    *Private\r
592   )\r
593 {\r
594   EFI_STATUS             Status;\r
595   PING6_ICMP6_TX_INFO    *TxInfo;\r
596 \r
597   TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));\r
598 \r
599   if (TxInfo == NULL) {\r
600     return EFI_OUT_OF_RESOURCES;\r
601   }\r
602 \r
603   TxInfo->TimeStamp   = Ping6ReadTime (Private);\r
604   TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);\r
605 \r
606   TxInfo->Token       = Ping6GenerateToken (\r
607                           Private,\r
608                           TxInfo->TimeStamp,\r
609                           TxInfo->SequenceNum\r
610                           );\r
611 \r
612   if (TxInfo->Token == NULL) {\r
613     Ping6DestroyTxInfo (TxInfo);\r
614     return EFI_OUT_OF_RESOURCES;\r
615   }\r
616 \r
617   Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);\r
618 \r
619   if (EFI_ERROR (Status)) {\r
620     Ping6DestroyTxInfo (TxInfo);\r
621     return Status;\r
622   }\r
623 \r
624   InsertTailList (&Private->TxList, &TxInfo->Link);\r
625   Private->TxCount++;\r
626 \r
627   return EFI_SUCCESS;\r
628 }\r
629 \r
630 /**\r
631   Place a completion token into the receive packet queue to receive the echo reply.\r
632 \r
633   @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
634 \r
635   @retval EFI_SUCCESS      Put the token into the receive packet queue successfully.\r
636   @retval others           Put the token into the receive packet queue unsuccessfully.\r
637 \r
638 **/\r
639 EFI_STATUS\r
640 Ping6OnReceiveEchoReply (\r
641   IN PING6_PRIVATE_DATA    *Private\r
642   )\r
643 {\r
644   EFI_STATUS    Status;\r
645 \r
646   ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));\r
647 \r
648   Status = gBS->CreateEvent (\r
649                   EVT_NOTIFY_SIGNAL,\r
650                   TPL_CALLBACK,\r
651                   Ping6OnEchoReplyReceived6,\r
652                   Private,\r
653                   &Private->RxToken.Event\r
654                   );\r
655 \r
656   if (EFI_ERROR (Status)) {\r
657     return Status;\r
658   }\r
659 \r
660   Private->RxToken.Status = EFI_NOT_READY;\r
661 \r
662   Status = Private->Ip6->Receive (Private->Ip6, &Private->RxToken);\r
663   if (EFI_ERROR (Status)) {\r
664     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_RECEIVE), gShellNetwork2HiiHandle, Status);\r
665   }\r
666   return Status;\r
667 }\r
668 \r
669 /**\r
670   Remove the timeout request from the list.\r
671 \r
672   @param[in]    Event    A EFI_EVENT type event.\r
673   @param[in]    Context  The pointer to Context.\r
674 \r
675 **/\r
676 VOID\r
677 EFIAPI\r
678 Ping6OnTimerRoutine6 (\r
679   IN EFI_EVENT    Event,\r
680   IN VOID         *Context\r
681   )\r
682 {\r
683   EFI_STATUS             Status;\r
684   PING6_PRIVATE_DATA     *Private;\r
685   PING6_ICMP6_TX_INFO    *TxInfo;\r
686   LIST_ENTRY             *Entry;\r
687   LIST_ENTRY             *NextEntry;\r
688   UINT64                 Time;\r
689 \r
690   Private = (PING6_PRIVATE_DATA *) Context;\r
691 \r
692   //\r
693   // Retransmit icmp6 echo request packets per second in sendnumber times.\r
694   //\r
695   if (Private->TxCount < Private->SendNum) {\r
696 \r
697     Status = Ping6SendEchoRequest (Private);\r
698     if (Private->TxCount != 0){\r
699       if (EFI_ERROR (Status)) {\r
700         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), gShellNetwork2HiiHandle, Private->TxCount + 1);\r
701       }\r
702     }\r
703   }\r
704   //\r
705   // Check whether any icmp6 echo request in the list timeout.\r
706   //\r
707   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
708     TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
709     Time   = Ping6CalculateTick (Private, TxInfo->TimeStamp, Ping6ReadTime (Private));\r
710 \r
711     //\r
712     // Remove the timeout echo request from txlist.\r
713     //\r
714     if (Time > PING6_DEFAULT_TIMEOUT) {\r
715 \r
716       if (EFI_ERROR (TxInfo->Token->Status)) {\r
717         Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);\r
718       }\r
719       //\r
720       // Remove the timeout icmp6 echo request from list.\r
721       //\r
722       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), gShellNetwork2HiiHandle, TxInfo->SequenceNum);\r
723 \r
724       RemoveEntryList (&TxInfo->Link);\r
725       Ping6DestroyTxInfo (TxInfo);\r
726 \r
727       if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {\r
728         //\r
729         // All the left icmp6 echo request in the list timeout.\r
730         //\r
731         Private->Status = EFI_TIMEOUT;\r
732       }\r
733     }\r
734   }\r
735 }\r
736 \r
737 /**\r
738   Create a valid IP6 instance.\r
739 \r
740   @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
741 \r
742   @retval EFI_SUCCESS              Create a valid IP6 instance successfully.\r
743   @retval EFI_ABORTED              Locate handle with ip6 service binding protocol unsuccessfully.\r
744   @retval EFI_INVALID_PARAMETER    The source address is unspecified when the destination address is a link -ocal address.\r
745   @retval EFI_OUT_OF_RESOURCES     No memory is available on the platform.\r
746   @retval EFI_NOT_FOUND            The source address is not found.\r
747 **/\r
748 EFI_STATUS\r
749 Ping6CreateIpInstance (\r
750   IN  PING6_PRIVATE_DATA    *Private\r
751   )\r
752 {\r
753   EFI_STATUS                       Status;\r
754   UINTN                            HandleIndex;\r
755   UINTN                            HandleNum;\r
756   EFI_HANDLE                       *HandleBuffer;\r
757   BOOLEAN                          UnspecifiedSrc;\r
758   EFI_STATUS                       MediaStatus;\r
759   EFI_SERVICE_BINDING_PROTOCOL     *Ip6Sb;\r
760   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;\r
761   EFI_IP6_CONFIG_DATA              Ip6Config;\r
762   EFI_IP6_CONFIG_INTERFACE_INFO    *IfInfo;\r
763   UINTN                            IfInfoSize;\r
764   EFI_IPv6_ADDRESS                 *Addr;\r
765   UINTN                            AddrIndex;\r
766 \r
767   HandleBuffer      = NULL;\r
768   UnspecifiedSrc    = FALSE;\r
769   MediaStatus       = EFI_SUCCESS;\r
770   Ip6Sb             = NULL;\r
771   IfInfo            = NULL;\r
772   IfInfoSize        = 0;\r
773 \r
774   //\r
775   // Locate all the handles with ip6 service binding protocol.\r
776   //\r
777   Status = gBS->LocateHandleBuffer (\r
778                   ByProtocol,\r
779                   &gEfiIp6ServiceBindingProtocolGuid,\r
780                   NULL,\r
781                   &HandleNum,\r
782                   &HandleBuffer\r
783                   );\r
784   if (EFI_ERROR (Status) || (HandleNum == 0)) {\r
785     return EFI_ABORTED;\r
786   }\r
787 \r
788   if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {\r
789     //\r
790     // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected. \r
791     //\r
792     UnspecifiedSrc = TRUE;\r
793   }\r
794   \r
795   //\r
796   // Source address is required when pinging a link-local address.\r
797   //\r
798   if (NetIp6IsLinkLocalAddr (&Private->DstAddress) && UnspecifiedSrc) {\r
799     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), gShellNetwork2HiiHandle);\r
800     Status = EFI_INVALID_PARAMETER;\r
801     goto ON_ERROR;\r
802   }\r
803   \r
804   //\r
805   // For each ip6 protocol, check interface addresses list.\r
806   //\r
807   for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {\r
808 \r
809     Ip6Sb      = NULL;\r
810     IfInfo     = NULL;\r
811     IfInfoSize = 0;\r
812 \r
813     if (UnspecifiedSrc) {\r
814       //\r
815       // Check media.\r
816       //\r
817       NetLibDetectMediaWaitTimeout (HandleBuffer[HandleIndex], 0, &MediaStatus);\r
818       if (MediaStatus != EFI_SUCCESS) {\r
819         //\r
820         // Skip this one.\r
821         //\r
822         continue;\r
823       }\r
824     }\r
825 \r
826     Status = gBS->HandleProtocol (\r
827                     HandleBuffer[HandleIndex],\r
828                     &gEfiIp6ServiceBindingProtocolGuid,\r
829                     (VOID **) &Ip6Sb\r
830                     );\r
831     if (EFI_ERROR (Status)) {\r
832       goto ON_ERROR;\r
833     }\r
834 \r
835     //\r
836     // Ip6config protocol and ip6 service binding protocol are installed\r
837     // on the same handle.\r
838     //\r
839     Status = gBS->HandleProtocol (\r
840                     HandleBuffer[HandleIndex],\r
841                     &gEfiIp6ConfigProtocolGuid,\r
842                     (VOID **) &Ip6Cfg\r
843                     );\r
844 \r
845     if (EFI_ERROR (Status)) {\r
846       goto ON_ERROR;\r
847     }\r
848     //\r
849     // Get the interface information size.\r
850     //\r
851     Status = Ip6Cfg->GetData (\r
852                        Ip6Cfg,\r
853                        Ip6ConfigDataTypeInterfaceInfo,\r
854                        &IfInfoSize,\r
855                        NULL\r
856                        );\r
857 \r
858     if (Status != EFI_BUFFER_TOO_SMALL) {\r
859       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);\r
860       goto ON_ERROR;\r
861     }\r
862 \r
863     IfInfo = AllocateZeroPool (IfInfoSize);\r
864 \r
865     if (IfInfo == NULL) {\r
866       Status = EFI_OUT_OF_RESOURCES;\r
867       goto ON_ERROR;\r
868     }\r
869     //\r
870     // Get the interface info.\r
871     //\r
872     Status = Ip6Cfg->GetData (\r
873                        Ip6Cfg,\r
874                        Ip6ConfigDataTypeInterfaceInfo,\r
875                        &IfInfoSize,\r
876                        IfInfo\r
877                        );\r
878 \r
879     if (EFI_ERROR (Status)) {\r
880       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);\r
881       goto ON_ERROR;\r
882     }\r
883     //\r
884     // Check whether the source address is one of the interface addresses.\r
885     //\r
886     for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {\r
887       Addr = &(IfInfo->AddressInfo[AddrIndex].Address);\r
888 \r
889       if (UnspecifiedSrc) {\r
890         if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {\r
891           //\r
892           // Select the interface automatically.\r
893           //\r
894           CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress));\r
895           break;\r
896         }\r
897       } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {\r
898         //\r
899         // Match a certain interface address.\r
900         //\r
901         break;\r
902       } \r
903     }\r
904 \r
905     if (AddrIndex < IfInfo->AddressInfoCount) {\r
906       //\r
907       // Found a nic handle with right interface address.\r
908       //\r
909       break;\r
910     }\r
911 \r
912     FreePool (IfInfo);\r
913     IfInfo = NULL;\r
914   }\r
915   //\r
916   // No exact interface address matched.\r
917   //\r
918 \r
919   if (HandleIndex == HandleNum) {\r
920     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF), gShellNetwork2HiiHandle);\r
921     Status = EFI_NOT_FOUND;\r
922     goto ON_ERROR;\r
923   }\r
924 \r
925   Private->NicHandle = HandleBuffer[HandleIndex];\r
926 \r
927   ASSERT (Ip6Sb != NULL);\r
928   Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);\r
929 \r
930   if (EFI_ERROR (Status)) {\r
931     goto ON_ERROR;\r
932   }\r
933 \r
934   Status = gBS->OpenProtocol (\r
935                   Private->Ip6ChildHandle,\r
936                   &gEfiIp6ProtocolGuid,\r
937                   (VOID **) &Private->Ip6,\r
938                   Private->ImageHandle,\r
939                   Private->Ip6ChildHandle,\r
940                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
941                   );\r
942   if (EFI_ERROR (Status)) {\r
943     goto ON_ERROR;\r
944   }\r
945 \r
946   ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));\r
947 \r
948   //\r
949   // Configure the ip6 instance for icmp6 packet exchange.\r
950   //\r
951   Ip6Config.DefaultProtocol   = 58;\r
952   Ip6Config.AcceptAnyProtocol = FALSE;\r
953   Ip6Config.AcceptIcmpErrors  = TRUE;\r
954   Ip6Config.AcceptPromiscuous = FALSE;\r
955   Ip6Config.TrafficClass      = 0;\r
956   Ip6Config.HopLimit          = 128;\r
957   Ip6Config.FlowLabel         = 0;\r
958   Ip6Config.ReceiveTimeout    = 0;\r
959   Ip6Config.TransmitTimeout   = 0;\r
960 \r
961   IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);\r
962 \r
963   IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);\r
964 \r
965   Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);\r
966 \r
967   if (EFI_ERROR (Status)) {\r
968     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), gShellNetwork2HiiHandle, Status);\r
969     goto ON_ERROR;\r
970   }\r
971 \r
972   return EFI_SUCCESS;\r
973 \r
974 ON_ERROR:\r
975   if (HandleBuffer != NULL) {\r
976     FreePool (HandleBuffer);\r
977   }\r
978 \r
979   if (IfInfo != NULL) {\r
980     FreePool (IfInfo);\r
981   }\r
982 \r
983   if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {\r
984     Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);\r
985   }\r
986 \r
987   return Status;\r
988 }\r
989 \r
990 /**\r
991   Destroy the IP6 instance.\r
992 \r
993   @param[in]    Private    The pointer of PING6_PRIVATE_DATA.\r
994 \r
995 **/\r
996 VOID\r
997 Ping6DestroyIpInstance (\r
998   IN PING6_PRIVATE_DATA    *Private\r
999   )\r
1000 {\r
1001   EFI_STATUS                      Status;\r
1002   EFI_SERVICE_BINDING_PROTOCOL    *Ip6Sb;\r
1003 \r
1004   gBS->CloseProtocol (\r
1005          Private->Ip6ChildHandle,\r
1006          &gEfiIp6ProtocolGuid,\r
1007          Private->ImageHandle,\r
1008          Private->Ip6ChildHandle\r
1009          );\r
1010 \r
1011   Status = gBS->HandleProtocol (\r
1012                   Private->NicHandle,\r
1013                   &gEfiIp6ServiceBindingProtocolGuid,\r
1014                   (VOID **) &Ip6Sb\r
1015                   );\r
1016 \r
1017   if (!EFI_ERROR(Status)) {\r
1018     Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);\r
1019   }\r
1020 }\r
1021 \r
1022 /**\r
1023   The Ping6 Process.\r
1024 \r
1025   @param[in]   ImageHandle    The firmware allocated handle for the UEFI image.\r
1026   @param[in]   SendNumber     The send request count.\r
1027   @param[in]   BufferSize     The send buffer size.\r
1028   @param[in]   SrcAddress     The source IPv6 address.\r
1029   @param[in]   DstAddress     The destination IPv6 address.\r
1030 \r
1031   @retval SHELL_SUCCESS    The ping6 processed successfullly.\r
1032   @retval others           The ping6 processed unsuccessfully.\r
1033 \r
1034 **/\r
1035 SHELL_STATUS\r
1036 ShellPing6 (\r
1037   IN EFI_HANDLE          ImageHandle,\r
1038   IN UINT32              SendNumber,\r
1039   IN UINT32              BufferSize,\r
1040   IN EFI_IPv6_ADDRESS    *SrcAddress,\r
1041   IN EFI_IPv6_ADDRESS    *DstAddress\r
1042   )\r
1043 {\r
1044   EFI_STATUS             Status;\r
1045   EFI_INPUT_KEY          Key;\r
1046   PING6_PRIVATE_DATA     *Private;\r
1047   PING6_ICMP6_TX_INFO    *TxInfo;\r
1048   LIST_ENTRY             *Entry;\r
1049   LIST_ENTRY             *NextEntry;\r
1050   SHELL_STATUS           ShellStatus;\r
1051 \r
1052   ShellStatus = SHELL_SUCCESS;\r
1053   Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));\r
1054 \r
1055   if (Private == NULL) {\r
1056     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellNetwork2HiiHandle, L"Ping6");\r
1057     ShellStatus = SHELL_OUT_OF_RESOURCES;\r
1058     goto ON_EXIT;\r
1059   }\r
1060 \r
1061   Private->ImageHandle = ImageHandle;\r
1062   Private->SendNum     = SendNumber;\r
1063   Private->BufferSize  = BufferSize;\r
1064   Private->RttMin      = ~((UINT64 )(0x0));\r
1065   Private->Status      = EFI_NOT_READY;\r
1066 \r
1067   InitializeListHead (&Private->TxList);\r
1068 \r
1069   IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);\r
1070   IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);\r
1071 \r
1072   //\r
1073   // Open and configure a ip6 instance for ping6.\r
1074   //\r
1075   Status = Ping6CreateIpInstance (Private);\r
1076 \r
1077   if (EFI_ERROR (Status)) {\r
1078     ShellStatus = SHELL_ACCESS_DENIED;\r
1079     goto ON_EXIT;\r
1080   }\r
1081   //\r
1082   // Print the command line itself.\r
1083   //\r
1084   ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), gShellNetwork2HiiHandle, mIp6DstString, Private->BufferSize);\r
1085   //\r
1086   // Create a ipv6 token to receive the first icmp6 echo reply packet.\r
1087   //\r
1088   Status = Ping6OnReceiveEchoReply (Private);\r
1089 \r
1090   if (EFI_ERROR (Status)) {\r
1091     ShellStatus = SHELL_ACCESS_DENIED;\r
1092     goto ON_EXIT;\r
1093   }\r
1094   //\r
1095   // Create and start timer to send icmp6 echo request packet per second.\r
1096   //\r
1097   Status = gBS->CreateEvent (\r
1098                   EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
1099                   TPL_CALLBACK,\r
1100                   Ping6OnTimerRoutine6,\r
1101                   Private,\r
1102                   &Private->Timer\r
1103                   );\r
1104 \r
1105   if (EFI_ERROR (Status)) {\r
1106     ShellStatus = SHELL_ACCESS_DENIED;\r
1107     goto ON_EXIT;\r
1108   }\r
1109 \r
1110   //\r
1111   // Start a timer to calculate the RTT.\r
1112   //\r
1113   Status = Ping6InitRttTimer (Private);\r
1114   if (EFI_ERROR (Status)) {\r
1115     ShellStatus = SHELL_ACCESS_DENIED;\r
1116     goto ON_EXIT;\r
1117   }\r
1118 \r
1119   //\r
1120   // Create a ipv6 token to send the first icmp6 echo request packet.\r
1121   //\r
1122   Status = Ping6SendEchoRequest (Private);\r
1123   //\r
1124   // EFI_NOT_READY for IPsec is enable and IKE is not established.\r
1125   //\r
1126   if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {\r
1127     ShellStatus = SHELL_ACCESS_DENIED;\r
1128     if(Status == EFI_NOT_FOUND) {\r
1129       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), gShellNetwork2HiiHandle, mIp6DstString);\r
1130     }\r
1131 \r
1132     goto ON_EXIT;\r
1133   }\r
1134 \r
1135   Status = gBS->SetTimer (\r
1136                   Private->Timer,\r
1137                   TimerPeriodic,\r
1138                   PING6_ONE_SECOND\r
1139                   );\r
1140 \r
1141   if (EFI_ERROR (Status)) {\r
1142     ShellStatus = SHELL_ACCESS_DENIED;\r
1143     goto ON_EXIT;\r
1144   }\r
1145   //\r
1146   // Control the ping6 process by two factors:\r
1147   // 1. Hot key\r
1148   // 2. Private->Status\r
1149   //   2.1. success means all icmp6 echo request packets get reply packets.\r
1150   //   2.2. timeout means the last icmp6 echo reply request timeout to get reply.\r
1151   //   2.3. noready means ping6 process is on-the-go.\r
1152   //\r
1153   while (Private->Status == EFI_NOT_READY) {\r
1154     Private->Ip6->Poll (Private->Ip6);\r
1155 \r
1156     //\r
1157     // Terminate the ping6 process by 'esc' or 'ctl-c'.\r
1158     //\r
1159     Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
1160 \r
1161     if (!EFI_ERROR(Status)) {\r
1162       if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||\r
1163          ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {\r
1164         goto ON_STAT;\r
1165       }\r
1166     }\r
1167   }\r
1168 \r
1169 ON_STAT:\r
1170   //\r
1171   // Display the statistics in all.\r
1172   //\r
1173   gBS->SetTimer (Private->Timer, TimerCancel, 0);\r
1174 \r
1175   if (Private->TxCount != 0) {\r
1176     ShellPrintHiiEx (\r
1177       -1,\r
1178       -1,\r
1179       NULL,\r
1180       STRING_TOKEN (STR_PING6_STAT),\r
1181       gShellNetwork2HiiHandle,\r
1182       Private->TxCount,\r
1183       Private->RxCount,\r
1184       (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,\r
1185       Private->RttSum\r
1186       );\r
1187   }\r
1188 \r
1189   if (Private->RxCount != 0) {\r
1190     ShellPrintHiiEx (\r
1191       -1,\r
1192       -1,\r
1193       NULL,\r
1194       STRING_TOKEN (STR_PING6_RTT),\r
1195       gShellNetwork2HiiHandle,\r
1196       Private->RttMin,\r
1197       Private->RttMin + Private->TimerPeriod,\r
1198       Private->RttMax,\r
1199       Private->RttMax + Private->TimerPeriod,\r
1200       DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL),\r
1201       DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL) + Private->TimerPeriod\r
1202       );\r
1203   }\r
1204 \r
1205 ON_EXIT:\r
1206 \r
1207   if (Private != NULL) {\r
1208     Private->Status = EFI_ABORTED;\r
1209 \r
1210     NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {\r
1211       TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);\r
1212 \r
1213       Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);\r
1214 \r
1215       RemoveEntryList (&TxInfo->Link);\r
1216       Ping6DestroyTxInfo (TxInfo);\r
1217     }\r
1218 \r
1219     Ping6FreeRttTimer (Private);\r
1220 \r
1221     if (Private->Timer != NULL) {\r
1222       gBS->CloseEvent (Private->Timer);\r
1223     }\r
1224 \r
1225     if (Private->Ip6 != NULL) {\r
1226       Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);\r
1227     }\r
1228 \r
1229     if (Private->RxToken.Event != NULL) {\r
1230       gBS->CloseEvent (Private->RxToken.Event);\r
1231     }\r
1232 \r
1233     if (Private->Ip6ChildHandle != NULL) {\r
1234       Ping6DestroyIpInstance (Private);\r
1235     }\r
1236 \r
1237     FreePool (Private);\r
1238   }\r
1239 \r
1240   return ShellStatus;\r
1241 }\r
1242 \r
1243 /**\r
1244   Function for 'ping6' command.\r
1245 \r
1246   @param[in] ImageHandle  Handle to the Image (NULL if Internal).\r
1247   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).\r
1248 \r
1249   @retval SHELL_SUCCESS  The ping6 processed successfullly.\r
1250   @retval others         The ping6 processed unsuccessfully.\r
1251 \r
1252 **/\r
1253 SHELL_STATUS\r
1254 EFIAPI\r
1255 ShellCommandRunPing6 (\r
1256   IN EFI_HANDLE        ImageHandle,\r
1257   IN EFI_SYSTEM_TABLE  *SystemTable\r
1258   )\r
1259 {\r
1260   EFI_STATUS          Status;\r
1261   SHELL_STATUS        ShellStatus;\r
1262   EFI_IPv6_ADDRESS    DstAddress;\r
1263   EFI_IPv6_ADDRESS    SrcAddress;\r
1264   UINT64              BufferSize;\r
1265   UINTN               SendNumber;\r
1266   LIST_ENTRY          *ParamPackage;\r
1267   CONST CHAR16        *ValueStr;\r
1268   CONST CHAR16        *ValueStrPtr;\r
1269   UINTN               NonOptionCount;\r
1270   CHAR16              *ProblemParam;\r
1271 \r
1272   ProblemParam = NULL;\r
1273   ShellStatus = SHELL_SUCCESS;\r
1274 \r
1275   Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);\r
1276   if (EFI_ERROR(Status)) {\r
1277     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);\r
1278     ShellStatus = SHELL_INVALID_PARAMETER;\r
1279     goto ON_EXIT;\r
1280   }\r
1281 \r
1282   SendNumber = 10;\r
1283   BufferSize = 16;\r
1284 \r
1285   //\r
1286   // Parse the parameter of count number.\r
1287   //\r
1288   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");\r
1289   ValueStrPtr = ValueStr;\r
1290   if (ValueStr != NULL) {\r
1291     SendNumber = ShellStrToUintn (ValueStrPtr);\r
1292 \r
1293     //\r
1294     // ShellStrToUintn will return 0 when input is 0 or an invalid input string.\r
1295     //\r
1296     if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {\r
1297       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), gShellNetwork2HiiHandle, ValueStr);\r
1298       ShellStatus = SHELL_INVALID_PARAMETER;\r
1299       goto ON_EXIT;\r
1300     }\r
1301   }\r
1302   //\r
1303   // Parse the parameter of buffer size.\r
1304   //\r
1305   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");\r
1306   ValueStrPtr = ValueStr;\r
1307   if (ValueStr != NULL) {\r
1308     BufferSize = ShellStrToUintn (ValueStrPtr);\r
1309 \r
1310     //\r
1311     // ShellStrToUintn will return 0 when input is 0 or an invalid input string.\r
1312     //\r
1313     if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {\r
1314       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), gShellNetwork2HiiHandle, ValueStr);\r
1315       ShellStatus = SHELL_INVALID_PARAMETER;\r
1316       goto ON_EXIT;\r
1317     }\r
1318   }\r
1319 \r
1320   ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));\r
1321   ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));\r
1322 \r
1323   //\r
1324   // Parse the parameter of source ip address.\r
1325   //\r
1326   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");\r
1327   ValueStrPtr = ValueStr;\r
1328   if (ValueStr != NULL) {\r
1329     mIp6SrcString = ValueStr;\r
1330     Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);\r
1331     if (EFI_ERROR (Status)) {\r
1332       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);\r
1333       ShellStatus = SHELL_INVALID_PARAMETER;\r
1334       goto ON_EXIT;\r
1335     }\r
1336   }\r
1337   //\r
1338   // Parse the parameter of destination ip address.\r
1339   //\r
1340   NonOptionCount = ShellCommandLineGetCount(ParamPackage);\r
1341   ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));\r
1342   if (NonOptionCount != 2) {\r
1343     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);\r
1344     ShellStatus = SHELL_INVALID_PARAMETER;\r
1345     goto ON_EXIT;\r
1346   }\r
1347   ValueStrPtr = ValueStr;\r
1348   if (ValueStr != NULL) {\r
1349     mIp6DstString = ValueStr;\r
1350     Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);\r
1351     if (EFI_ERROR (Status)) {\r
1352       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);\r
1353       ShellStatus = SHELL_INVALID_PARAMETER;\r
1354       goto ON_EXIT;\r
1355     }\r
1356   }\r
1357 \r
1358   //\r
1359   // Enter into ping6 process.\r
1360   //\r
1361   ShellStatus = ShellPing6 (\r
1362               ImageHandle,\r
1363               (UINT32)SendNumber,\r
1364               (UINT32)BufferSize,\r
1365               &SrcAddress,\r
1366               &DstAddress\r
1367               );\r
1368 \r
1369 ON_EXIT:\r
1370   ShellCommandLineFreeVarList (ParamPackage);\r
1371   return ShellStatus;\r
1372 }\r
1373 \r