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