1 /** @file
2   The implementation for Ping shell command.
3 
4   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
6 
7   This program and the accompanying materials
8   are licensed and made available under the terms and conditions of the BSD License
9   which accompanies this distribution.  The full text of the license may be found at
10   http://opensource.org/licenses/bsd-license.php.
11 
12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "UefiShellNetwork1CommandsLib.h"
18 
19 #define PING_IP4_COPY_ADDRESS(Dest, Src) (CopyMem ((Dest), (Src), sizeof (EFI_IPv4_ADDRESS)))
20 
21 UINT64          mCurrentTick = 0;
22 
23 //
24 // Function templates to match the IPv4 and IPv6 commands that we use.
25 //
26 typedef
27 EFI_STATUS
28 (EFIAPI *PING_IPX_POLL)(
29   IN VOID          *This
30   );
31 
32 typedef
33 EFI_STATUS
34 (EFIAPI *PING_IPX_TRANSMIT)(
35   IN VOID          *This,
36   IN VOID          *Token
37   );
38 
39 typedef
40 EFI_STATUS
41 (EFIAPI *PING_IPX_RECEIVE)(
42   IN VOID          *This,
43   IN VOID          *Token
44   );
45 
46 typedef
47 EFI_STATUS
48 (EFIAPI *PING_IPX_CANCEL)(
49   IN VOID          *This,
50   IN VOID          *Token OPTIONAL
51   );
52 
53 ///
54 /// A set of pointers to either IPv6 or IPv4 functions.
55 /// Unknown which one to the ping command.
56 ///
57 typedef struct {
58   PING_IPX_TRANSMIT             Transmit;
59   PING_IPX_RECEIVE              Receive;
60   PING_IPX_CANCEL               Cancel;
61   PING_IPX_POLL                 Poll;
62 }PING_IPX_PROTOCOL;
63 
64 
65 typedef union {
66   VOID                  *RxData;
67   VOID                  *TxData;
68 } PING_PACKET;
69 
70 //
71 // PING_IPX_COMPLETION_TOKEN
72 // structures are used for both transmit and receive operations.
73 // This version is IP-unaware.
74 //
75 typedef struct {
76   EFI_EVENT               Event;
77   EFI_STATUS              Status;
78   PING_PACKET             Packet;
79 } PING_IPX_COMPLETION_TOKEN;
80 
81 #pragma pack(1)
82 typedef struct _ICMPX_ECHO_REQUEST_REPLY {
83   UINT8                       Type;
84   UINT8                       Code;
85   UINT16                      Checksum;
86   UINT16                      Identifier;
87   UINT16                      SequenceNum;
88   UINT64                      TimeStamp;
89   UINT8                       Data[1];
90 } ICMPX_ECHO_REQUEST_REPLY;
91 #pragma pack()
92 
93 typedef struct _PING_ICMP_TX_INFO {
94   LIST_ENTRY                Link;
95   UINT16                    SequenceNum;
96   UINT64                    TimeStamp;
97   PING_IPX_COMPLETION_TOKEN *Token;
98 } PING_ICMPX_TX_INFO;
99 
100 #define DEFAULT_TIMEOUT       5000
101 #define MAX_SEND_NUMBER       10000
102 #define MAX_BUFFER_SIZE       32768
103 #define DEFAULT_TIMER_PERIOD  358049
104 #define ONE_SECOND            10000000
105 #define PING_IP_CHOICE_IP4    1
106 #define PING_IP_CHOICE_IP6    2
107 #define DEFAULT_SEND_COUNT    10
108 #define DEFAULT_BUFFER_SIZE   16
109 #define ICMP_V4_ECHO_REQUEST  0x8
110 #define ICMP_V4_ECHO_REPLY    0x0
111 
112 #define PING_PRIVATE_DATA_SIGNATURE  SIGNATURE_32 ('P', 'i', 'n', 'g')
113 typedef struct _PING_PRIVATE_DATA {
114   UINT32                      Signature;
115   EFI_HANDLE                  NicHandle;
116   EFI_HANDLE                  IpChildHandle;
117   EFI_EVENT                   Timer;
118 
119   EFI_STATUS                  Status;
120   LIST_ENTRY                  TxList;
121   UINT16                      RxCount;
122   UINT16                      TxCount;
123   UINT64                      RttSum;
124   UINT64                      RttMin;
125   UINT64                      RttMax;
126   UINT32                      SequenceNum;
127 
128   UINT32                      SendNum;
129   UINT32                      BufferSize;
130   UINT32                      IpChoice;
131 
132   PING_IPX_PROTOCOL           ProtocolPointers;
133   VOID                        *IpProtocol;
134   UINT8                       SrcAddress[MAX(sizeof(EFI_IPv6_ADDRESS)        , sizeof(EFI_IPv4_ADDRESS)          )];
135   UINT8                       DstAddress[MAX(sizeof(EFI_IPv6_ADDRESS)        , sizeof(EFI_IPv4_ADDRESS)          )];
136   PING_IPX_COMPLETION_TOKEN   RxToken;
137 } PING_PRIVATE_DATA;
138 
139 /**
140   Calculate the internet checksum (see RFC 1071).
141 
142   @param[in] Packet  Buffer which contains the data to be checksummed.
143   @param[in] Length  Length to be checksummed.
144 
145   @retval Checksum     Returns the 16 bit ones complement of
146                        ones complement sum of 16 bit words
147 **/
148 UINT16
149 EFIAPI
NetChecksum(IN UINT8 * Buffer,IN UINT32 Length)150 NetChecksum (
151   IN UINT8   *Buffer,
152   IN UINT32  Length
153   )
154 {
155   UINT32  Sum;
156   UINT8   Odd;
157   UINT16  *Packet;
158 
159   Packet  = (UINT16 *) Buffer;
160 
161   Sum     = 0;
162   Odd     = (UINT8) (Length & 1);
163   Length >>= 1;
164   while ((Length--) != 0) {
165     Sum += *Packet++;
166   }
167 
168   if (Odd != 0) {
169     Sum += *(UINT8 *) Packet;
170   }
171 
172   Sum = (Sum & 0xffff) + (Sum >> 16);
173 
174   //
175   // in case above carried
176   //
177   Sum += Sum >> 16;
178 
179   return (UINT16) Sum;
180 }
181 
182 /**
183   Reads and returns the current value of register.
184   In IA64, the register is the Interval Timer Vector (ITV).
185   In X86(IA32/X64), the register is the Time Stamp Counter (TSC)
186 
187   @return The current value of the register.
188 
189 **/
190 
191 STATIC CONST SHELL_PARAM_ITEM    PingParamList[] = {
192   {
193     L"-l",
194     TypeValue
195   },
196   {
197     L"-n",
198     TypeValue
199   },
200   {
201     L"-_s",
202     TypeValue
203   },
204   {
205     L"-_ip6",
206     TypeFlag
207   },
208   {
209     NULL,
210     TypeMax
211   },
212 };
213 
214 //
215 // Global Variables in Ping command.
216 //
217 STATIC CONST CHAR16      *mDstString;
218 STATIC CONST CHAR16      *mSrcString;
219 STATIC UINT64            mFrequency = 0;
220 EFI_CPU_ARCH_PROTOCOL    *gCpu = NULL;
221 
222 /**
223   Read the current time.
224 
225   @retval the current tick value.
226 **/
227 UINT64
228 EFIAPI
ReadTime(VOID)229 ReadTime (
230   VOID
231   )
232 {
233   UINT64                 TimerPeriod;
234   EFI_STATUS             Status;
235 
236   ASSERT (gCpu != NULL);
237 
238   Status = gCpu->GetTimerValue (gCpu, 0, &mCurrentTick, &TimerPeriod);
239   if (EFI_ERROR (Status)) {
240     //
241     // The WinntGetTimerValue will return EFI_UNSUPPORTED. Set the
242     // TimerPeriod by ourselves.
243     //
244     mCurrentTick += 1000000;
245   }
246 
247   return mCurrentTick;
248 }
249 
250 
251 /**
252   Get and calculate the frequency in ticks/ms.
253   The result is saved in the global variable mFrequency
254 
255   @retval EFI_SUCCESS    Calculated the frequency successfully.
256   @retval Others         Failed to calculate the frequency.
257 
258 **/
259 EFI_STATUS
260 EFIAPI
GetFrequency(VOID)261 GetFrequency (
262   VOID
263   )
264 {
265   EFI_STATUS               Status;
266   UINT64                   CurrentTick;
267   UINT64                   TimerPeriod;
268 
269   Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &gCpu);
270   if (EFI_ERROR (Status)) {
271     return Status;
272   }
273 
274   Status = gCpu->GetTimerValue (gCpu, 0, &CurrentTick, &TimerPeriod);
275 
276   if (EFI_ERROR (Status)) {
277     TimerPeriod = DEFAULT_TIMER_PERIOD;
278   }
279 
280   //
281   // The timer period is in femtosecond (1 femtosecond is 1e-15 second).
282   // So 1e+12 is divided by timer period to produce the freq in ticks/ms.
283   //
284   mFrequency = DivU64x64Remainder (1000000000000ULL, TimerPeriod, NULL);
285 
286   return EFI_SUCCESS;
287 }
288 
289 /**
290   Calculate a duration in ms.
291 
292   @param[in]  Begin     The start point of time.
293   @param[in]  End       The end point of time.
294 
295   @return               The duration in ms.
296   @retval 0             The parameters were not valid.
297 **/
298 UINT64
299 EFIAPI
CalculateTick(IN UINT64 Begin,IN UINT64 End)300 CalculateTick (
301   IN UINT64    Begin,
302   IN UINT64    End
303   )
304 {
305   if (End <= Begin) {
306     return (0);
307   }
308   return DivU64x64Remainder (End - Begin, mFrequency, NULL);
309 }
310 
311 /**
312   Destroy PING_ICMPX_TX_INFO, and recollect the memory.
313 
314   @param[in]    TxInfo    The pointer to PING_ICMPX_TX_INFO.
315   @param[in]    IpChoice  Whether the token is IPv4 or IPv6
316 **/
317 VOID
318 EFIAPI
PingDestroyTxInfo(IN PING_ICMPX_TX_INFO * TxInfo,IN UINT32 IpChoice)319 PingDestroyTxInfo (
320   IN PING_ICMPX_TX_INFO    *TxInfo,
321   IN UINT32                IpChoice
322   )
323 {
324   EFI_IP6_TRANSMIT_DATA    *Ip6TxData;
325   EFI_IP4_TRANSMIT_DATA    *Ip4TxData;
326   EFI_IP6_FRAGMENT_DATA    *FragData;
327   UINTN                    Index;
328 
329   if (TxInfo == NULL) {
330     return;
331   }
332 
333   if (TxInfo->Token != NULL) {
334 
335     if (TxInfo->Token->Event != NULL) {
336       gBS->CloseEvent (TxInfo->Token->Event);
337     }
338 
339     if (TxInfo->Token->Packet.TxData != NULL) {
340       if (IpChoice == PING_IP_CHOICE_IP6) {
341         Ip6TxData = TxInfo->Token->Packet.TxData;
342 
343         if (Ip6TxData->OverrideData != NULL) {
344           FreePool (Ip6TxData->OverrideData);
345         }
346 
347         if (Ip6TxData->ExtHdrs != NULL) {
348           FreePool (Ip6TxData->ExtHdrs);
349         }
350 
351         for (Index = 0; Index < Ip6TxData->FragmentCount; Index++) {
352           FragData = Ip6TxData->FragmentTable[Index].FragmentBuffer;
353           if (FragData != NULL) {
354             FreePool (FragData);
355           }
356         }
357       } else {
358         Ip4TxData = TxInfo->Token->Packet.TxData;
359 
360         if (Ip4TxData->OverrideData != NULL) {
361           FreePool (Ip4TxData->OverrideData);
362         }
363 
364         for (Index = 0; Index < Ip4TxData->FragmentCount; Index++) {
365           FragData = Ip4TxData->FragmentTable[Index].FragmentBuffer;
366           if (FragData != NULL) {
367             FreePool (FragData);
368           }
369         }
370       }
371     }
372 
373     FreePool (TxInfo->Token);
374   }
375 
376   FreePool (TxInfo);
377 }
378 
379 /**
380   Match the request, and reply with SequenceNum/TimeStamp.
381 
382   @param[in]    Private    The pointer to PING_PRIVATE_DATA.
383   @param[in]    Packet     The pointer to ICMPX_ECHO_REQUEST_REPLY.
384 
385   @retval EFI_SUCCESS      The match is successful.
386   @retval EFI_NOT_FOUND    The reply can't be matched with any request.
387 
388 **/
389 EFI_STATUS
390 EFIAPI
Ping6MatchEchoReply(IN PING_PRIVATE_DATA * Private,IN ICMPX_ECHO_REQUEST_REPLY * Packet)391 Ping6MatchEchoReply (
392   IN PING_PRIVATE_DATA           *Private,
393   IN ICMPX_ECHO_REQUEST_REPLY    *Packet
394   )
395 {
396   PING_ICMPX_TX_INFO     *TxInfo;
397   LIST_ENTRY             *Entry;
398   LIST_ENTRY             *NextEntry;
399 
400   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
401     TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
402 
403     if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
404       Private->RxCount++;
405       RemoveEntryList (&TxInfo->Link);
406       PingDestroyTxInfo (TxInfo, Private->IpChoice);
407       return EFI_SUCCESS;
408     }
409   }
410 
411   return EFI_NOT_FOUND;
412 }
413 
414 /**
415   The original intention is to send a request.
416   Currently, the application retransmits an icmp6 echo request packet
417   per second in sendnumber times that is specified by the user.
418   Because nothing can be done here, all things move to the timer rountine.
419 
420   @param[in]    Event      A EFI_EVENT type event.
421   @param[in]    Context    The pointer to Context.
422 
423 **/
424 VOID
425 EFIAPI
Ping6OnEchoRequestSent(IN EFI_EVENT Event,IN VOID * Context)426 Ping6OnEchoRequestSent (
427   IN EFI_EVENT    Event,
428   IN VOID         *Context
429   )
430 {
431 }
432 
433 /**
434   receive reply, match and print reply infomation.
435 
436   @param[in]    Event      A EFI_EVENT type event.
437   @param[in]    Context    The pointer to context.
438 
439 **/
440 VOID
441 EFIAPI
Ping6OnEchoReplyReceived(IN EFI_EVENT Event,IN VOID * Context)442 Ping6OnEchoReplyReceived (
443   IN EFI_EVENT    Event,
444   IN VOID         *Context
445   )
446 {
447   EFI_STATUS                  Status;
448   PING_PRIVATE_DATA           *Private;
449   ICMPX_ECHO_REQUEST_REPLY    *Reply;
450   UINT32                      PayLoad;
451   UINT64                      Rtt;
452   CHAR8                       Near;
453 
454   Private = (PING_PRIVATE_DATA *) Context;
455 
456   if (Private == NULL || Private->Status == EFI_ABORTED || Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
457     return;
458   }
459 
460   if (Private->RxToken.Packet.RxData == NULL) {
461     return;
462   }
463 
464   if (Private->IpChoice == PING_IP_CHOICE_IP6) {
465     Reply   = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer;
466     PayLoad = ((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength;
467     if (((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->NextHeader != IP6_ICMP) {
468       goto ON_EXIT;
469     }
470     if (!IP6_IS_MULTICAST ((EFI_IPv6_ADDRESS*)&Private->DstAddress) &&
471         !EFI_IP6_EQUAL (&((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv6_ADDRESS*)&Private->DstAddress)) {
472       goto ON_EXIT;
473     }
474 
475     if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
476       goto ON_EXIT;
477     }
478   } else {
479     Reply   = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->FragmentTable[0].FragmentBuffer;
480     PayLoad = ((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->DataLength;
481     if (!IP4_IS_MULTICAST (EFI_IP4(*(EFI_IPv4_ADDRESS*)Private->DstAddress)) &&
482         !EFI_IP4_EQUAL (&((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->SourceAddress, (EFI_IPv4_ADDRESS*)&Private->DstAddress)) {
483       goto ON_EXIT;
484     }
485 
486     if ((Reply->Type != ICMP_V4_ECHO_REPLY) || (Reply->Code != 0)) {
487       goto ON_EXIT;
488     }
489   }
490 
491 
492   if (PayLoad != Private->BufferSize) {
493     goto ON_EXIT;
494   }
495   //
496   // Check whether the reply matches the sent request before.
497   //
498   Status = Ping6MatchEchoReply (Private, Reply);
499   if (EFI_ERROR(Status)) {
500     goto ON_EXIT;
501   }
502   //
503   // Display statistics on this icmp6 echo reply packet.
504   //
505   Rtt  = CalculateTick (Reply->TimeStamp, ReadTime ());
506   if (Rtt != 0) {
507     Near = (CHAR8) '=';
508   } else {
509     Near = (CHAR8) '<';
510   }
511 
512   Private->RttSum += Rtt;
513   Private->RttMin  = Private->RttMin > Rtt ? Rtt : Private->RttMin;
514   Private->RttMax  = Private->RttMax < Rtt ? Rtt : Private->RttMax;
515 
516   ShellPrintHiiEx (
517     -1,
518     -1,
519     NULL,
520     STRING_TOKEN (STR_PING_REPLY_INFO),
521     gShellNetwork1HiiHandle,
522     PayLoad,
523     mDstString,
524     Reply->SequenceNum,
525     Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->Header->HopLimit:0,
526     Near,
527     Rtt
528     );
529 
530 ON_EXIT:
531 
532   if (Private->RxCount < Private->SendNum) {
533     //
534     // Continue to receive icmp echo reply packets.
535     //
536     Private->RxToken.Status = EFI_ABORTED;
537 
538     Status = Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken);
539 
540     if (EFI_ERROR (Status)) {
541       Private->Status = EFI_ABORTED;
542     }
543   } else {
544     //
545     // All reply have already been received from the dest host.
546     //
547     Private->Status = EFI_SUCCESS;
548   }
549   //
550   // Singal to recycle the each rxdata here, not at the end of process.
551   //
552   gBS->SignalEvent (Private->IpChoice == PING_IP_CHOICE_IP6?((EFI_IP6_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->RecycleSignal:((EFI_IP4_RECEIVE_DATA*)Private->RxToken.Packet.RxData)->RecycleSignal);
553 }
554 
555 /**
556   Create a PING_IPX_COMPLETION_TOKEN.
557 
558   @param[in]    Private        The pointer of PING_PRIVATE_DATA.
559   @param[in]    TimeStamp      The TimeStamp of request.
560   @param[in]    SequenceNum    The SequenceNum of request.
561 
562   @return The pointer of PING_IPX_COMPLETION_TOKEN.
563 
564 **/
565 PING_IPX_COMPLETION_TOKEN *
566 EFIAPI
PingGenerateToken(IN PING_PRIVATE_DATA * Private,IN UINT64 TimeStamp,IN UINT16 SequenceNum)567 PingGenerateToken (
568   IN PING_PRIVATE_DATA    *Private,
569   IN UINT64                TimeStamp,
570   IN UINT16                SequenceNum
571   )
572 {
573   EFI_STATUS                  Status;
574   PING_IPX_COMPLETION_TOKEN   *Token;
575   VOID                        *TxData;
576   ICMPX_ECHO_REQUEST_REPLY    *Request;
577   UINT16                        HeadSum;
578   UINT16                        TempChecksum;
579 
580   Request = AllocateZeroPool (Private->BufferSize);
581   if (Request == NULL) {
582     return NULL;
583   }
584   TxData = AllocateZeroPool (Private->IpChoice==PING_IP_CHOICE_IP6?sizeof (EFI_IP6_TRANSMIT_DATA):sizeof (EFI_IP4_TRANSMIT_DATA));
585   if (TxData == NULL) {
586     FreePool (Request);
587     return NULL;
588   }
589   Token = AllocateZeroPool (sizeof (PING_IPX_COMPLETION_TOKEN));
590   if (Token == NULL) {
591     FreePool (Request);
592     FreePool (TxData);
593     return NULL;
594   }
595 
596   //
597   // Assembly echo request packet.
598   //
599   Request->Type        = (UINT8)(Private->IpChoice==PING_IP_CHOICE_IP6?ICMP_V6_ECHO_REQUEST:ICMP_V4_ECHO_REQUEST);
600   Request->Code        = 0;
601   Request->SequenceNum = SequenceNum;
602   Request->Identifier  = 0;
603   Request->Checksum    = 0;
604 
605   //
606   // Assembly token for transmit.
607   //
608   if (Private->IpChoice==PING_IP_CHOICE_IP6) {
609     Request->TimeStamp   = TimeStamp;
610     ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrsLength                   = 0;
611     ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrs                         = NULL;
612     ((EFI_IP6_TRANSMIT_DATA*)TxData)->OverrideData                    = 0;
613     ((EFI_IP6_TRANSMIT_DATA*)TxData)->DataLength                      = Private->BufferSize;
614     ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentCount                   = 1;
615     ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request;
616     ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize;
617   } else {
618     ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsLength                   = 0;
619     ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsBuffer                   = NULL;
620     ((EFI_IP4_TRANSMIT_DATA*)TxData)->OverrideData                    = 0;
621     ((EFI_IP4_TRANSMIT_DATA*)TxData)->TotalDataLength                 = Private->BufferSize;
622     ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentCount                   = 1;
623     ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request;
624     ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize;
625     ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[0]      = Private->DstAddress[0];
626     ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[1]      = Private->DstAddress[1];
627     ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[2]      = Private->DstAddress[2];
628     ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[3]      = Private->DstAddress[3];
629 
630     HeadSum = NetChecksum ((UINT8 *) Request, Private->BufferSize);
631     Request->TimeStamp   = TimeStamp;
632     TempChecksum = NetChecksum ((UINT8 *) &Request->TimeStamp, sizeof (UINT64));
633     Request->Checksum = (UINT16)(~NetAddChecksum (HeadSum, TempChecksum));
634   }
635 
636 
637   Token->Status         = EFI_ABORTED;
638   Token->Packet.TxData  = TxData;
639 
640   Status = gBS->CreateEvent (
641                   EVT_NOTIFY_SIGNAL,
642                   TPL_CALLBACK,
643                   Ping6OnEchoRequestSent,
644                   Private,
645                   &Token->Event
646                   );
647 
648   if (EFI_ERROR (Status)) {
649     FreePool (Request);
650     FreePool (TxData);
651     FreePool (Token);
652     return NULL;
653   }
654 
655   return Token;
656 }
657 
658 /**
659   Transmit the PING_IPX_COMPLETION_TOKEN.
660 
661   @param[in]    Private    The pointer of PING_PRIVATE_DATA.
662 
663   @retval EFI_SUCCESS             Transmitted successfully.
664   @retval EFI_OUT_OF_RESOURCES    No memory is available on the platform.
665   @retval others                  Transmitted unsuccessfully.
666 
667 **/
668 EFI_STATUS
669 EFIAPI
PingSendEchoRequest(IN PING_PRIVATE_DATA * Private)670 PingSendEchoRequest (
671   IN PING_PRIVATE_DATA    *Private
672   )
673 {
674   EFI_STATUS             Status;
675   PING_ICMPX_TX_INFO     *TxInfo;
676 
677   TxInfo = AllocateZeroPool (sizeof (PING_ICMPX_TX_INFO));
678 
679   if (TxInfo == NULL) {
680     return EFI_OUT_OF_RESOURCES;
681   }
682 
683   TxInfo->TimeStamp   = ReadTime ();
684   TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
685   TxInfo->Token       = PingGenerateToken (
686                           Private,
687                           TxInfo->TimeStamp,
688                           TxInfo->SequenceNum
689                           );
690 
691   if (TxInfo->Token == NULL) {
692     PingDestroyTxInfo (TxInfo, Private->IpChoice);
693     return EFI_OUT_OF_RESOURCES;
694   }
695 
696   ASSERT(Private->ProtocolPointers.Transmit != NULL);
697   Status = Private->ProtocolPointers.Transmit (Private->IpProtocol, TxInfo->Token);
698 
699   if (EFI_ERROR (Status)) {
700     PingDestroyTxInfo (TxInfo, Private->IpChoice);
701     return Status;
702   }
703 
704   InsertTailList (&Private->TxList, &TxInfo->Link);
705   Private->TxCount++;
706 
707   return EFI_SUCCESS;
708 }
709 
710 /**
711   Place a completion token into the receive packet queue to receive the echo reply.
712 
713   @param[in]    Private    The pointer of PING_PRIVATE_DATA.
714 
715   @retval EFI_SUCCESS      Put the token into the receive packet queue successfully.
716   @retval others           Put the token into the receive packet queue unsuccessfully.
717 
718 **/
719 EFI_STATUS
720 EFIAPI
Ping6ReceiveEchoReply(IN PING_PRIVATE_DATA * Private)721 Ping6ReceiveEchoReply (
722   IN PING_PRIVATE_DATA    *Private
723   )
724 {
725   EFI_STATUS    Status;
726 
727   ZeroMem (&Private->RxToken, sizeof (PING_IPX_COMPLETION_TOKEN));
728 
729   Status = gBS->CreateEvent (
730                   EVT_NOTIFY_SIGNAL,
731                   TPL_CALLBACK,
732                   Ping6OnEchoReplyReceived,
733                   Private,
734                   &Private->RxToken.Event
735                   );
736 
737   if (EFI_ERROR (Status)) {
738     return Status;
739   }
740 
741   Private->RxToken.Status = EFI_NOT_READY;
742 
743   return (Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken));
744 }
745 
746 /**
747   Remove the timeout request from the list.
748 
749   @param[in]    Event    A EFI_EVENT type event.
750   @param[in]    Context  The pointer to Context.
751 
752 **/
753 VOID
754 EFIAPI
Ping6OnTimerRoutine(IN EFI_EVENT Event,IN VOID * Context)755 Ping6OnTimerRoutine (
756   IN EFI_EVENT    Event,
757   IN VOID         *Context
758   )
759 {
760   EFI_STATUS             Status;
761   PING_PRIVATE_DATA      *Private;
762   PING_ICMPX_TX_INFO     *TxInfo;
763   LIST_ENTRY             *Entry;
764   LIST_ENTRY             *NextEntry;
765   UINT64                 Time;
766 
767   Private = (PING_PRIVATE_DATA *) Context;
768   if (Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
769     Private->Status = EFI_NOT_FOUND;
770     return;
771   }
772 
773   //
774   // Retransmit icmp6 echo request packets per second in sendnumber times.
775   //
776   if (Private->TxCount < Private->SendNum) {
777 
778     Status = PingSendEchoRequest (Private);
779     if (Private->TxCount != 0){
780       if (EFI_ERROR (Status)) {
781         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_SEND_REQUEST), gShellNetwork1HiiHandle, Private->TxCount + 1);
782       }
783     }
784   }
785   //
786   // Check whether any icmp6 echo request in the list timeout.
787   //
788   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
789     TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
790     Time   = CalculateTick (TxInfo->TimeStamp, ReadTime ());
791 
792     //
793     // Remove the timeout echo request from txlist.
794     //
795     if (Time > DEFAULT_TIMEOUT) {
796 
797       if (EFI_ERROR (TxInfo->Token->Status)) {
798         Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
799       }
800       //
801       // Remove the timeout icmp6 echo request from list.
802       //
803       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_TIMEOUT), gShellNetwork1HiiHandle, TxInfo->SequenceNum);
804 
805       RemoveEntryList (&TxInfo->Link);
806       PingDestroyTxInfo (TxInfo, Private->IpChoice);
807 
808       if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
809         //
810         // All the left icmp6 echo request in the list timeout.
811         //
812         Private->Status = EFI_TIMEOUT;
813       }
814     }
815   }
816 }
817 
818 /**
819   Determine if a IP4 address is Link Local.
820 
821   169.254.1.0 through 169.254.254.255 is link local.
822 
823   @param[in] Address  The address to test.
824 
825   @retval TRUE      It is.
826   @retval FALSE     It is not.
827 **/
828 BOOLEAN
829 EFIAPI
PingNetIp4IsLinkLocalAddr(IN CONST EFI_IPv4_ADDRESS * Address)830 PingNetIp4IsLinkLocalAddr (
831   IN CONST EFI_IPv4_ADDRESS *Address
832   )
833 {
834   return ((BOOLEAN)(Address->Addr[0] == 169 && Address->Addr[1] == 254 && Address->Addr[2] >= 1 && Address->Addr[2] <= 254));
835 }
836 
837 /**
838   Determine if a IP4 address is unspecified.
839 
840   @param[in] Address  The address to test.
841 
842   @retval TRUE      It is.
843   @retval FALSE     It is not.
844 **/
845 BOOLEAN
846 EFIAPI
PingNetIp4IsUnspecifiedAddr(IN CONST EFI_IPv4_ADDRESS * Address)847 PingNetIp4IsUnspecifiedAddr (
848   IN CONST EFI_IPv4_ADDRESS *Address
849   )
850 {
851   return  ((BOOLEAN)((ReadUnaligned32 ((UINT32*)&Address->Addr[0])) == 0x00000000));
852 }
853 
854 /**
855   Create a valid IP instance.
856 
857   @param[in]    Private    The pointer of PING_PRIVATE_DATA.
858 
859   @retval EFI_SUCCESS              Create a valid IPx instance successfully.
860   @retval EFI_ABORTED              Locate handle with ipx service binding protocol unsuccessfully.
861   @retval EFI_INVALID_PARAMETER    The source address is unspecified when the destination address is a link-local address.
862   @retval EFI_OUT_OF_RESOURCES     No memory is available on the platform.
863   @retval EFI_NOT_FOUND            The source address is not found.
864 **/
865 EFI_STATUS
866 EFIAPI
PingCreateIpInstance(IN PING_PRIVATE_DATA * Private)867 PingCreateIpInstance (
868   IN  PING_PRIVATE_DATA    *Private
869   )
870 {
871   EFI_STATUS                       Status;
872   UINTN                            HandleIndex;
873   UINTN                            HandleNum;
874   EFI_HANDLE                       *HandleBuffer;
875   EFI_SERVICE_BINDING_PROTOCOL     *EfiSb;
876   VOID                             *IpXCfg;
877   EFI_IP6_CONFIG_DATA              Ip6Config;
878   EFI_IP4_CONFIG_DATA              Ip4Config;
879   VOID                             *IpXInterfaceInfo;
880   UINTN                            IfInfoSize;
881   EFI_IPv6_ADDRESS                 *Addr;
882   UINTN                            AddrIndex;
883 
884   HandleBuffer      = NULL;
885   EfiSb             = NULL;
886   IpXInterfaceInfo  = NULL;
887   IfInfoSize        = 0;
888 
889   //
890   // Locate all the handles with ip6 service binding protocol.
891   //
892   Status = gBS->LocateHandleBuffer (
893                   ByProtocol,
894                   Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
895                   NULL,
896                   &HandleNum,
897                   &HandleBuffer
898                   );
899   if (EFI_ERROR (Status) || (HandleNum == 0) || (HandleBuffer == NULL)) {
900     return EFI_ABORTED;
901   }
902   //
903   // Source address is required when pinging a link-local address on multi-
904   // interfaces host.
905   //
906   if (Private->IpChoice == PING_IP_CHOICE_IP6) {
907     if (NetIp6IsLinkLocalAddr ((EFI_IPv6_ADDRESS*)&Private->DstAddress) &&
908         NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS*)&Private->SrcAddress) &&
909         (HandleNum > 1)) {
910       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", mSrcString);
911       Status = EFI_INVALID_PARAMETER;
912       goto ON_ERROR;
913     }
914   } else {
915     ASSERT(Private->IpChoice == PING_IP_CHOICE_IP4);
916     if (PingNetIp4IsLinkLocalAddr ((EFI_IPv4_ADDRESS*)&Private->DstAddress) &&
917         PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS*)&Private->SrcAddress) &&
918         (HandleNum > 1)) {
919       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", mSrcString);
920       Status = EFI_INVALID_PARAMETER;
921       goto ON_ERROR;
922     }
923   }
924   //
925   // For each ip6 protocol, check interface addresses list.
926   //
927   for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
928 
929     EfiSb             = NULL;
930     IpXInterfaceInfo  = NULL;
931     IfInfoSize        = 0;
932 
933     Status = gBS->HandleProtocol (
934                     HandleBuffer[HandleIndex],
935                     Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
936                     (VOID **) &EfiSb
937                     );
938     if (EFI_ERROR (Status)) {
939       goto ON_ERROR;
940     }
941 
942     if (Private->IpChoice == PING_IP_CHOICE_IP6?NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS*)&Private->SrcAddress):PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS*)&Private->SrcAddress)) {
943       //
944       // No need to match interface address.
945       //
946       break;
947     } else {
948       //
949       // Ip6config protocol and ip6 service binding protocol are installed
950       // on the same handle.
951       //
952       Status = gBS->HandleProtocol (
953                       HandleBuffer[HandleIndex],
954                       Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ConfigProtocolGuid:&gEfiIp4Config2ProtocolGuid,
955                       (VOID **) &IpXCfg
956                       );
957 
958       if (EFI_ERROR (Status)) {
959         goto ON_ERROR;
960       }
961       //
962       // Get the interface information size.
963       //
964       if (Private->IpChoice == PING_IP_CHOICE_IP6) {
965         Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
966                            IpXCfg,
967                            Ip6ConfigDataTypeInterfaceInfo,
968                            &IfInfoSize,
969                            NULL
970                            );
971       } else {
972         Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData (
973                            IpXCfg,
974                            Ip4Config2DataTypeInterfaceInfo,
975                            &IfInfoSize,
976                            NULL
977                            );
978       }
979 
980       //
981       // Skip the ones not in current use.
982       //
983       if (Status == EFI_NOT_STARTED) {
984         continue;
985       }
986 
987       if (Status != EFI_BUFFER_TOO_SMALL) {
988         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
989         goto ON_ERROR;
990       }
991 
992       IpXInterfaceInfo = AllocateZeroPool (IfInfoSize);
993 
994       if (IpXInterfaceInfo == NULL) {
995         Status = EFI_OUT_OF_RESOURCES;
996         goto ON_ERROR;
997       }
998       //
999       // Get the interface info.
1000       //
1001       if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1002         Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
1003                            IpXCfg,
1004                            Ip6ConfigDataTypeInterfaceInfo,
1005                            &IfInfoSize,
1006                            IpXInterfaceInfo
1007                            );
1008       } else {
1009         Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData (
1010                            IpXCfg,
1011                            Ip4Config2DataTypeInterfaceInfo,
1012                            &IfInfoSize,
1013                            IpXInterfaceInfo
1014                            );
1015       }
1016 
1017       if (EFI_ERROR (Status)) {
1018         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
1019         goto ON_ERROR;
1020       }
1021       //
1022       // Check whether the source address is one of the interface addresses.
1023       //
1024       if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1025         for (AddrIndex = 0; AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount; AddrIndex++) {
1026 
1027           Addr = &(((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfo[AddrIndex].Address);
1028           if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
1029             //
1030             // Match a certain interface address.
1031             //
1032             break;
1033           }
1034         }
1035 
1036         if (AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount) {
1037           //
1038           // Found a nic handle with right interface address.
1039           //
1040           break;
1041         }
1042       } else {
1043         //
1044         // IP4 address check
1045         //
1046         if (EFI_IP4_EQUAL (&Private->SrcAddress, &((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) {
1047           //
1048           // Match a certain interface address.
1049           //
1050           break;
1051         }
1052       }
1053     }
1054 
1055     FreePool (IpXInterfaceInfo);
1056     IpXInterfaceInfo = NULL;
1057   }
1058   //
1059   // No exact interface address matched.
1060   //
1061 
1062   if (HandleIndex == HandleNum) {
1063     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIGD_NIC_NF), gShellNetwork1HiiHandle, L"ping");
1064     Status = EFI_NOT_FOUND;
1065     goto ON_ERROR;
1066   }
1067 
1068   Private->NicHandle = HandleBuffer[HandleIndex];
1069 
1070   ASSERT (EfiSb != NULL);
1071   Status = EfiSb->CreateChild (EfiSb, &Private->IpChildHandle);
1072 
1073   if (EFI_ERROR (Status)) {
1074     goto ON_ERROR;
1075   }
1076   if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1077     Status = gBS->OpenProtocol (
1078                     Private->IpChildHandle,
1079                     &gEfiIp6ProtocolGuid,
1080                     &Private->IpProtocol,
1081                     gImageHandle,
1082                     Private->IpChildHandle,
1083                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
1084                     );
1085     if (EFI_ERROR (Status)) {
1086       goto ON_ERROR;
1087     }
1088 
1089 
1090     ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
1091 
1092     //
1093     // Configure the ip6 instance for icmp6 packet exchange.
1094     //
1095     Ip6Config.DefaultProtocol   = 58;
1096     Ip6Config.AcceptAnyProtocol = FALSE;
1097     Ip6Config.AcceptIcmpErrors  = TRUE;
1098     Ip6Config.AcceptPromiscuous = FALSE;
1099     Ip6Config.TrafficClass      = 0;
1100     Ip6Config.HopLimit          = 128;
1101     Ip6Config.FlowLabel         = 0;
1102     Ip6Config.ReceiveTimeout    = 0;
1103     Ip6Config.TransmitTimeout   = 0;
1104 
1105     IP6_COPY_ADDRESS (&Ip6Config.StationAddress,     &Private->SrcAddress);
1106     IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
1107 
1108     Status = ((EFI_IP6_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip6Config);
1109 
1110     if (EFI_ERROR (Status)) {
1111       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
1112       goto ON_ERROR;
1113     }
1114 
1115     Private->ProtocolPointers.Transmit  = (PING_IPX_TRANSMIT )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Transmit;
1116     Private->ProtocolPointers.Receive   = (PING_IPX_RECEIVE  )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Receive;
1117     Private->ProtocolPointers.Cancel    = (PING_IPX_CANCEL   )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Cancel;
1118     Private->ProtocolPointers.Poll      = (PING_IPX_POLL     )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Poll;
1119   } else {
1120     Status = gBS->OpenProtocol (
1121                     Private->IpChildHandle,
1122                     &gEfiIp4ProtocolGuid,
1123                     &Private->IpProtocol,
1124                     gImageHandle,
1125                     Private->IpChildHandle,
1126                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
1127                     );
1128     if (EFI_ERROR (Status)) {
1129       goto ON_ERROR;
1130     }
1131 
1132 
1133     ZeroMem (&Ip4Config, sizeof (EFI_IP4_CONFIG_DATA));
1134 
1135     //
1136     // Configure the ip4 instance for icmp4 packet exchange.
1137     //
1138     Ip4Config.DefaultProtocol   = 1;
1139     Ip4Config.AcceptAnyProtocol = FALSE;
1140     Ip4Config.AcceptBroadcast   = FALSE;
1141     Ip4Config.AcceptIcmpErrors  = TRUE;
1142     Ip4Config.AcceptPromiscuous = FALSE;
1143     Ip4Config.DoNotFragment     = FALSE;
1144     Ip4Config.RawData           = FALSE;
1145     Ip4Config.ReceiveTimeout    = 0;
1146     Ip4Config.TransmitTimeout   = 0;
1147     Ip4Config.UseDefaultAddress = TRUE;
1148     Ip4Config.TimeToLive        = 128;
1149     Ip4Config.TypeOfService     = 0;
1150 
1151     Status = ((EFI_IP4_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip4Config);
1152 
1153     if (EFI_ERROR (Status)) {
1154       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
1155       goto ON_ERROR;
1156     }
1157 
1158     Private->ProtocolPointers.Transmit  = (PING_IPX_TRANSMIT )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Transmit;
1159     Private->ProtocolPointers.Receive   = (PING_IPX_RECEIVE  )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Receive;
1160     Private->ProtocolPointers.Cancel    = (PING_IPX_CANCEL   )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Cancel;
1161     Private->ProtocolPointers.Poll      = (PING_IPX_POLL     )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Poll;
1162   }
1163 
1164   if (HandleBuffer != NULL) {
1165     FreePool (HandleBuffer);
1166   }
1167 
1168   return EFI_SUCCESS;
1169 
1170 ON_ERROR:
1171   if (HandleBuffer != NULL) {
1172     FreePool (HandleBuffer);
1173   }
1174 
1175   if (IpXInterfaceInfo != NULL) {
1176     FreePool (IpXInterfaceInfo);
1177   }
1178 
1179   if ((EfiSb != NULL) && (Private->IpChildHandle != NULL)) {
1180     EfiSb->DestroyChild (EfiSb, Private->IpChildHandle);
1181   }
1182 
1183   return Status;
1184 }
1185 
1186 /**
1187   Destroy the IP instance.
1188 
1189   @param[in]    Private    The pointer of PING_PRIVATE_DATA.
1190 
1191 **/
1192 VOID
1193 EFIAPI
Ping6DestroyIp6Instance(IN PING_PRIVATE_DATA * Private)1194 Ping6DestroyIp6Instance (
1195   IN PING_PRIVATE_DATA    *Private
1196   )
1197 {
1198   EFI_STATUS                      Status;
1199   EFI_SERVICE_BINDING_PROTOCOL    *IpSb;
1200 
1201   gBS->CloseProtocol (
1202          Private->IpChildHandle,
1203          Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ProtocolGuid:&gEfiIp4ProtocolGuid,
1204          gImageHandle,
1205          Private->IpChildHandle
1206          );
1207 
1208   Status = gBS->HandleProtocol (
1209                   Private->NicHandle,
1210                   Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
1211                   (VOID **) &IpSb
1212                   );
1213 
1214   if (!EFI_ERROR(Status)) {
1215     IpSb->DestroyChild (IpSb, Private->IpChildHandle);
1216   }
1217 }
1218 
1219 /**
1220   The Ping Process.
1221 
1222   @param[in]   SendNumber     The send request count.
1223   @param[in]   BufferSize     The send buffer size.
1224   @param[in]   SrcAddress     The source address.
1225   @param[in]   DstAddress     The destination address.
1226   @param[in]   IpChoice       The choice between IPv4 and IPv6.
1227 
1228   @retval SHELL_SUCCESS  The ping processed successfullly.
1229   @retval others         The ping processed unsuccessfully.
1230 **/
1231 SHELL_STATUS
1232 EFIAPI
ShellPing(IN UINT32 SendNumber,IN UINT32 BufferSize,IN EFI_IPv6_ADDRESS * SrcAddress,IN EFI_IPv6_ADDRESS * DstAddress,IN UINT32 IpChoice)1233 ShellPing (
1234   IN UINT32              SendNumber,
1235   IN UINT32              BufferSize,
1236   IN EFI_IPv6_ADDRESS    *SrcAddress,
1237   IN EFI_IPv6_ADDRESS    *DstAddress,
1238   IN UINT32              IpChoice
1239   )
1240 {
1241   EFI_STATUS             Status;
1242   PING_PRIVATE_DATA      *Private;
1243   PING_ICMPX_TX_INFO     *TxInfo;
1244   LIST_ENTRY             *Entry;
1245   LIST_ENTRY             *NextEntry;
1246   SHELL_STATUS           ShellStatus;
1247 
1248   ShellStatus = SHELL_SUCCESS;
1249   Private     = AllocateZeroPool (sizeof (PING_PRIVATE_DATA));
1250 
1251   if (Private == NULL) {
1252     return (SHELL_OUT_OF_RESOURCES);
1253   }
1254 
1255   Private->IpChoice    = IpChoice;
1256   Private->Signature   = PING_PRIVATE_DATA_SIGNATURE;
1257   Private->SendNum     = SendNumber;
1258   Private->BufferSize  = BufferSize;
1259   Private->RttMin      = ~((UINT64 )(0x0));
1260   Private->Status      = EFI_NOT_READY;
1261 
1262   CopyMem(&Private->SrcAddress, SrcAddress, sizeof(Private->SrcAddress));
1263   CopyMem(&Private->DstAddress, DstAddress, sizeof(Private->DstAddress));
1264 
1265   InitializeListHead (&Private->TxList);
1266 
1267   //
1268   // Open and configure a ip instance for us.
1269   //
1270   Status = PingCreateIpInstance (Private);
1271 
1272   if (EFI_ERROR (Status)) {
1273     ShellStatus = SHELL_ACCESS_DENIED;
1274     goto ON_EXIT;
1275   }
1276   //
1277   // Print the command line itself.
1278   //
1279   ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_START), gShellNetwork1HiiHandle, mDstString, Private->BufferSize);
1280   //
1281   // Create a ipv6 token to receive the first icmp6 echo reply packet.
1282   //
1283   Status = Ping6ReceiveEchoReply (Private);
1284 
1285   if (EFI_ERROR (Status)) {
1286     ShellStatus = SHELL_ACCESS_DENIED;
1287     goto ON_EXIT;
1288   }
1289   //
1290   // Create and start timer to send icmp6 echo request packet per second.
1291   //
1292   Status = gBS->CreateEvent (
1293                   EVT_TIMER | EVT_NOTIFY_SIGNAL,
1294                   TPL_CALLBACK,
1295                   Ping6OnTimerRoutine,
1296                   Private,
1297                   &Private->Timer
1298                   );
1299 
1300   if (EFI_ERROR (Status)) {
1301     ShellStatus = SHELL_ACCESS_DENIED;
1302     goto ON_EXIT;
1303   }
1304   //
1305   // Create a ipv6 token to send the first icmp6 echo request packet.
1306   //
1307   Status = PingSendEchoRequest (Private);
1308   //
1309   // EFI_NOT_READY for IPsec is enable and IKE is not established.
1310   //
1311   if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
1312     ShellStatus = SHELL_ACCESS_DENIED;
1313     if(Status == EFI_NOT_FOUND) {
1314       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOSOURCE_INDO), gShellNetwork1HiiHandle, mDstString);
1315     } else if (Status == RETURN_NO_MAPPING) {
1316       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOROUTE_FOUND), gShellNetwork1HiiHandle, mDstString, mSrcString);
1317     } else {
1318       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NETWORK_ERROR), gShellNetwork1HiiHandle, L"ping", Status);
1319     }
1320 
1321     goto ON_EXIT;
1322   }
1323 
1324   Status = gBS->SetTimer (
1325                   Private->Timer,
1326                   TimerPeriodic,
1327                   ONE_SECOND
1328                   );
1329 
1330   if (EFI_ERROR (Status)) {
1331     ShellStatus = SHELL_ACCESS_DENIED;
1332     goto ON_EXIT;
1333   }
1334   //
1335   // Control the ping6 process by two factors:
1336   // 1. Hot key
1337   // 2. Private->Status
1338   //   2.1. success means all icmp6 echo request packets get reply packets.
1339   //   2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1340   //   2.3. noready means ping6 process is on-the-go.
1341   //
1342   while (Private->Status == EFI_NOT_READY) {
1343     Status = Private->ProtocolPointers.Poll (Private->IpProtocol);
1344     if (ShellGetExecutionBreakFlag()) {
1345       Private->Status = EFI_ABORTED;
1346       goto ON_STAT;
1347     }
1348   }
1349 
1350 ON_STAT:
1351   //
1352   // Display the statistics in all.
1353   //
1354   gBS->SetTimer (Private->Timer, TimerCancel, 0);
1355 
1356   if (Private->TxCount != 0) {
1357     ShellPrintHiiEx (
1358       -1,
1359       -1,
1360       NULL,
1361       STRING_TOKEN (STR_PING_STAT),
1362       gShellNetwork1HiiHandle,
1363       Private->TxCount,
1364       Private->RxCount,
1365       (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
1366       Private->RttSum
1367       );
1368   }
1369 
1370   if (Private->RxCount != 0) {
1371     ShellPrintHiiEx (
1372       -1,
1373       -1,
1374       NULL,
1375       STRING_TOKEN (STR_PING_RTT),
1376       gShellNetwork1HiiHandle,
1377       Private->RttMin,
1378       Private->RttMax,
1379       DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL)
1380       );
1381   }
1382 
1383 ON_EXIT:
1384 
1385   if (Private != NULL) {
1386 
1387     NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
1388       TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
1389 
1390       if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
1391         Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
1392       }
1393 
1394       RemoveEntryList (&TxInfo->Link);
1395       PingDestroyTxInfo (TxInfo, Private->IpChoice);
1396     }
1397 
1398     if (Private->Timer != NULL) {
1399       gBS->CloseEvent (Private->Timer);
1400     }
1401 
1402     if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
1403       Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, &Private->RxToken);
1404     }
1405 
1406     if (Private->RxToken.Event != NULL) {
1407       gBS->CloseEvent (Private->RxToken.Event);
1408     }
1409 
1410     if (Private->IpChildHandle != NULL) {
1411       Ping6DestroyIp6Instance (Private);
1412     }
1413 
1414     FreePool (Private);
1415   }
1416 
1417   return ShellStatus;
1418 }
1419 
1420 /**
1421   Function for 'ping' command.
1422 
1423   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
1424   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
1425 
1426   @retval SHELL_SUCCESS  The ping processed successfullly.
1427   @retval others         The ping processed unsuccessfully.
1428 
1429 **/
1430 SHELL_STATUS
1431 EFIAPI
ShellCommandRunPing(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)1432 ShellCommandRunPing (
1433   IN EFI_HANDLE        ImageHandle,
1434   IN EFI_SYSTEM_TABLE  *SystemTable
1435   )
1436 {
1437   EFI_STATUS          Status;
1438   SHELL_STATUS        ShellStatus;
1439   EFI_IPv6_ADDRESS    DstAddress;
1440   EFI_IPv6_ADDRESS    SrcAddress;
1441   UINT64              BufferSize;
1442   UINTN               SendNumber;
1443   LIST_ENTRY          *ParamPackage;
1444   CONST CHAR16        *ValueStr;
1445   UINTN               NonOptionCount;
1446   UINT32              IpChoice;
1447   CHAR16              *ProblemParam;
1448 
1449   //
1450   // we use IPv6 buffers to hold items...
1451   // make sure this is enough space!
1452   //
1453   ASSERT(sizeof(EFI_IPv4_ADDRESS        ) <= sizeof(EFI_IPv6_ADDRESS         ));
1454   ASSERT(sizeof(EFI_IP4_COMPLETION_TOKEN) <= sizeof(EFI_IP6_COMPLETION_TOKEN ));
1455 
1456   IpChoice = PING_IP_CHOICE_IP4;
1457 
1458   ShellStatus = SHELL_SUCCESS;
1459   ProblemParam = NULL;
1460 
1461   Status = ShellCommandLineParseEx (PingParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
1462   if (EFI_ERROR(Status)) {
1463     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ProblemParam);
1464     ShellStatus = SHELL_INVALID_PARAMETER;
1465     goto ON_EXIT;
1466   }
1467 
1468   if (ShellCommandLineGetFlag (ParamPackage, L"-_ip6")) {
1469     IpChoice = PING_IP_CHOICE_IP6;
1470   }
1471 
1472   //
1473   // Parse the paramter of count number.
1474   //
1475   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
1476   if (ValueStr != NULL) {
1477     SendNumber = ShellStrToUintn (ValueStr);
1478 
1479     //
1480     // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1481     //
1482     if ((SendNumber == 0) || (SendNumber > MAX_SEND_NUMBER)) {
1483       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1484       ShellStatus = SHELL_INVALID_PARAMETER;
1485       goto ON_EXIT;
1486     }
1487   } else {
1488     SendNumber = DEFAULT_SEND_COUNT;
1489   }
1490   //
1491   // Parse the paramter of buffer size.
1492   //
1493   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
1494   if (ValueStr != NULL) {
1495     BufferSize = ShellStrToUintn (ValueStr);
1496 
1497     //
1498     // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1499     //
1500     if ((BufferSize < 16) || (BufferSize > MAX_BUFFER_SIZE)) {
1501       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1502       ShellStatus = SHELL_INVALID_PARAMETER;
1503       goto ON_EXIT;
1504     }
1505   } else {
1506     BufferSize = DEFAULT_BUFFER_SIZE;
1507   }
1508 
1509   ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
1510   ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
1511 
1512   //
1513   // Parse the paramter of source ip address.
1514   //
1515   ValueStr = ShellCommandLineGetValue (ParamPackage, L"-_s");
1516   if (ValueStr != NULL) {
1517     mSrcString = ValueStr;
1518     if (IpChoice == PING_IP_CHOICE_IP6) {
1519       Status = NetLibStrToIp6 (ValueStr, &SrcAddress);
1520     } else {
1521       Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&SrcAddress);
1522     }
1523     if (EFI_ERROR (Status)) {
1524       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1525       ShellStatus = SHELL_INVALID_PARAMETER;
1526       goto ON_EXIT;
1527     }
1528   }
1529   //
1530   // Parse the paramter of destination ip address.
1531   //
1532   NonOptionCount = ShellCommandLineGetCount(ParamPackage);
1533   if (NonOptionCount < 2) {
1534     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellNetwork1HiiHandle, L"ping");
1535     ShellStatus = SHELL_INVALID_PARAMETER;
1536     goto ON_EXIT;
1537   }
1538   if (NonOptionCount > 2) {
1539     ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellNetwork1HiiHandle, L"ping");
1540     ShellStatus = SHELL_INVALID_PARAMETER;
1541     goto ON_EXIT;
1542   }
1543   ValueStr = ShellCommandLineGetRawValue (ParamPackage, 1);
1544   if (ValueStr != NULL) {
1545     mDstString = ValueStr;
1546     if (IpChoice == PING_IP_CHOICE_IP6) {
1547       Status = NetLibStrToIp6 (ValueStr, &DstAddress);
1548     } else {
1549       Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&DstAddress);
1550     }
1551     if (EFI_ERROR (Status)) {
1552       ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1553       ShellStatus = SHELL_INVALID_PARAMETER;
1554       goto ON_EXIT;
1555     }
1556   }
1557   //
1558   // Get frequency to calculate the time from ticks.
1559   //
1560   Status = GetFrequency ();
1561 
1562   if (EFI_ERROR(Status)) {
1563     goto ON_EXIT;
1564   }
1565   //
1566   // Enter into ping process.
1567   //
1568   ShellStatus = ShellPing (
1569              (UINT32)SendNumber,
1570              (UINT32)BufferSize,
1571              &SrcAddress,
1572              &DstAddress,
1573              IpChoice
1574              );
1575 
1576 ON_EXIT:
1577   ShellCommandLineFreeVarList (ParamPackage);
1578   return ShellStatus;
1579 }
1580