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