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