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