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