ShellPkg: Fix typo of 'determines' in ShellPkg.
[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 ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrsLength = 0;
610 ((EFI_IP6_TRANSMIT_DATA*)TxData)->ExtHdrs = NULL;
611 ((EFI_IP6_TRANSMIT_DATA*)TxData)->OverrideData = 0;
612 ((EFI_IP6_TRANSMIT_DATA*)TxData)->DataLength = Private->BufferSize;
613 ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentCount = 1;
614 ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request;
615 ((EFI_IP6_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize;
616 } else {
617 ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsLength = 0;
618 ((EFI_IP4_TRANSMIT_DATA*)TxData)->OptionsBuffer = NULL;
619 ((EFI_IP4_TRANSMIT_DATA*)TxData)->OverrideData = 0;
620 ((EFI_IP4_TRANSMIT_DATA*)TxData)->TotalDataLength = Private->BufferSize;
621 ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentCount = 1;
622 ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentBuffer = (VOID *) Request;
623 ((EFI_IP4_TRANSMIT_DATA*)TxData)->FragmentTable[0].FragmentLength = Private->BufferSize;
624 ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[0] = Private->DstAddress[0];
625 ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[1] = Private->DstAddress[1];
626 ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[2] = Private->DstAddress[2];
627 ((EFI_IP4_TRANSMIT_DATA*)TxData)->DestinationAddress.Addr[3] = Private->DstAddress[3];
628
629 HeadSum = NetChecksum ((UINT8 *) Request, Private->BufferSize);
630 Request->TimeStamp = TimeStamp;
631 TempChecksum = NetChecksum ((UINT8 *) &Request->TimeStamp, sizeof (UINT64));
632 Request->Checksum = (UINT16)(~NetAddChecksum (HeadSum, TempChecksum));
633 }
634
635
636 Token->Status = EFI_ABORTED;
637 Token->Packet.TxData = TxData;
638
639 Status = gBS->CreateEvent (
640 EVT_NOTIFY_SIGNAL,
641 TPL_CALLBACK,
642 Ping6OnEchoRequestSent,
643 Private,
644 &Token->Event
645 );
646
647 if (EFI_ERROR (Status)) {
648 FreePool (Request);
649 FreePool (TxData);
650 FreePool (Token);
651 return NULL;
652 }
653
654 return Token;
655 }
656
657 /**
658 Transmit the PING_IPX_COMPLETION_TOKEN.
659
660 @param[in] Private The pointer of PING_PRIVATE_DATA.
661
662 @retval EFI_SUCCESS Transmitted successfully.
663 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
664 @retval others Transmitted unsuccessfully.
665
666 **/
667 EFI_STATUS
668 EFIAPI
669 PingSendEchoRequest (
670 IN PING_PRIVATE_DATA *Private
671 )
672 {
673 EFI_STATUS Status;
674 PING_ICMPX_TX_INFO *TxInfo;
675
676 TxInfo = AllocateZeroPool (sizeof (PING_ICMPX_TX_INFO));
677
678 if (TxInfo == NULL) {
679 return EFI_OUT_OF_RESOURCES;
680 }
681
682 TxInfo->TimeStamp = ReadTime ();
683 TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
684 TxInfo->Token = PingGenerateToken (
685 Private,
686 TxInfo->TimeStamp,
687 TxInfo->SequenceNum
688 );
689
690 if (TxInfo->Token == NULL) {
691 PingDestroyTxInfo (TxInfo, Private->IpChoice);
692 return EFI_OUT_OF_RESOURCES;
693 }
694
695 ASSERT(Private->ProtocolPointers.Transmit != NULL);
696 Status = Private->ProtocolPointers.Transmit (Private->IpProtocol, TxInfo->Token);
697
698 if (EFI_ERROR (Status)) {
699 PingDestroyTxInfo (TxInfo, Private->IpChoice);
700 return Status;
701 }
702
703 InsertTailList (&Private->TxList, &TxInfo->Link);
704 Private->TxCount++;
705
706 return EFI_SUCCESS;
707 }
708
709 /**
710 Place a completion token into the receive packet queue to receive the echo reply.
711
712 @param[in] Private The pointer of PING_PRIVATE_DATA.
713
714 @retval EFI_SUCCESS Put the token into the receive packet queue successfully.
715 @retval others Put the token into the receive packet queue unsuccessfully.
716
717 **/
718 EFI_STATUS
719 EFIAPI
720 Ping6ReceiveEchoReply (
721 IN PING_PRIVATE_DATA *Private
722 )
723 {
724 EFI_STATUS Status;
725
726 ZeroMem (&Private->RxToken, sizeof (PING_IPX_COMPLETION_TOKEN));
727
728 Status = gBS->CreateEvent (
729 EVT_NOTIFY_SIGNAL,
730 TPL_CALLBACK,
731 Ping6OnEchoReplyReceived,
732 Private,
733 &Private->RxToken.Event
734 );
735
736 if (EFI_ERROR (Status)) {
737 return Status;
738 }
739
740 Private->RxToken.Status = EFI_NOT_READY;
741
742 return (Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken));
743 }
744
745 /**
746 Remove the timeout request from the list.
747
748 @param[in] Event A EFI_EVENT type event.
749 @param[in] Context The pointer to Context.
750
751 **/
752 VOID
753 EFIAPI
754 Ping6OnTimerRoutine (
755 IN EFI_EVENT Event,
756 IN VOID *Context
757 )
758 {
759 EFI_STATUS Status;
760 PING_PRIVATE_DATA *Private;
761 PING_ICMPX_TX_INFO *TxInfo;
762 LIST_ENTRY *Entry;
763 LIST_ENTRY *NextEntry;
764 UINT64 Time;
765
766 Private = (PING_PRIVATE_DATA *) Context;
767 if (Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
768 Private->Status = EFI_NOT_FOUND;
769 return;
770 }
771
772 //
773 // Retransmit icmp6 echo request packets per second in sendnumber times.
774 //
775 if (Private->TxCount < Private->SendNum) {
776
777 Status = PingSendEchoRequest (Private);
778 if (Private->TxCount != 0){
779 if (EFI_ERROR (Status)) {
780 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_SEND_REQUEST), gShellNetwork1HiiHandle, Private->TxCount + 1);
781 }
782 }
783 }
784 //
785 // Check whether any icmp6 echo request in the list timeout.
786 //
787 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
788 TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
789 Time = CalculateTick (TxInfo->TimeStamp, ReadTime ());
790
791 //
792 // Remove the timeout echo request from txlist.
793 //
794 if (Time > DEFAULT_TIMEOUT) {
795
796 if (EFI_ERROR (TxInfo->Token->Status)) {
797 Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
798 }
799 //
800 // Remove the timeout icmp6 echo request from list.
801 //
802 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_TIMEOUT), gShellNetwork1HiiHandle, TxInfo->SequenceNum);
803
804 RemoveEntryList (&TxInfo->Link);
805 PingDestroyTxInfo (TxInfo, Private->IpChoice);
806
807 //
808 // We dont need to wait for this some other time...
809 //
810 Private->RxCount++;
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 EFI_SERVICE_BINDING_PROTOCOL *EfiSb;
880 VOID *IpXCfg;
881 EFI_IP6_CONFIG_DATA Ip6Config;
882 EFI_IP4_CONFIG_DATA Ip4Config;
883 VOID *IpXInterfaceInfo;
884 UINTN IfInfoSize;
885 EFI_IPv6_ADDRESS *Addr;
886 UINTN AddrIndex;
887
888 HandleBuffer = NULL;
889 EfiSb = NULL;
890 IpXInterfaceInfo = NULL;
891 IfInfoSize = 0;
892
893 //
894 // Locate all the handles with ip6 service binding protocol.
895 //
896 Status = gBS->LocateHandleBuffer (
897 ByProtocol,
898 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
899 NULL,
900 &HandleNum,
901 &HandleBuffer
902 );
903 if (EFI_ERROR (Status) || (HandleNum == 0) || (HandleBuffer == NULL)) {
904 return EFI_ABORTED;
905 }
906 //
907 // Source address is required when pinging a link-local address on multi-
908 // interfaces host.
909 //
910 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
911 if (NetIp6IsLinkLocalAddr ((EFI_IPv6_ADDRESS*)&Private->DstAddress) &&
912 NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS*)&Private->SrcAddress) &&
913 (HandleNum > 1)) {
914 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", mSrcString);
915 Status = EFI_INVALID_PARAMETER;
916 goto ON_ERROR;
917 }
918 } else {
919 ASSERT(Private->IpChoice == PING_IP_CHOICE_IP4);
920 if (PingNetIp4IsLinkLocalAddr ((EFI_IPv4_ADDRESS*)&Private->DstAddress) &&
921 PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS*)&Private->SrcAddress) &&
922 (HandleNum > 1)) {
923 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", mSrcString);
924 Status = EFI_INVALID_PARAMETER;
925 goto ON_ERROR;
926 }
927 }
928 //
929 // For each ip6 protocol, check interface addresses list.
930 //
931 for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
932
933 EfiSb = NULL;
934 IpXInterfaceInfo = NULL;
935 IfInfoSize = 0;
936
937 Status = gBS->HandleProtocol (
938 HandleBuffer[HandleIndex],
939 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
940 (VOID **) &EfiSb
941 );
942 if (EFI_ERROR (Status)) {
943 goto ON_ERROR;
944 }
945
946 if (Private->IpChoice == PING_IP_CHOICE_IP6?NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS*)&Private->SrcAddress):PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS*)&Private->SrcAddress)) {
947 //
948 // No need to match interface address.
949 //
950 break;
951 } else {
952 //
953 // Ip6config protocol and ip6 service binding protocol are installed
954 // on the same handle.
955 //
956 Status = gBS->HandleProtocol (
957 HandleBuffer[HandleIndex],
958 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ConfigProtocolGuid:&gEfiIp4ConfigProtocolGuid,
959 (VOID **) &IpXCfg
960 );
961
962 if (EFI_ERROR (Status)) {
963 goto ON_ERROR;
964 }
965 //
966 // Get the interface information size.
967 //
968 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
969 Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
970 IpXCfg,
971 Ip6ConfigDataTypeInterfaceInfo,
972 &IfInfoSize,
973 NULL
974 );
975 } else {
976 Status = ((EFI_IP4_CONFIG_PROTOCOL*)IpXCfg)->GetData (
977 IpXCfg,
978 &IfInfoSize,
979 NULL
980 );
981 }
982
983 //
984 // Skip the ones not in current use.
985 //
986 if (Status == EFI_NOT_STARTED) {
987 continue;
988 }
989
990 if (Status != EFI_BUFFER_TOO_SMALL) {
991 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
992 goto ON_ERROR;
993 }
994
995 IpXInterfaceInfo = AllocateZeroPool (IfInfoSize);
996
997 if (IpXInterfaceInfo == NULL) {
998 Status = EFI_OUT_OF_RESOURCES;
999 goto ON_ERROR;
1000 }
1001 //
1002 // Get the interface info.
1003 //
1004 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1005 Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
1006 IpXCfg,
1007 Ip6ConfigDataTypeInterfaceInfo,
1008 &IfInfoSize,
1009 IpXInterfaceInfo
1010 );
1011 } else {
1012 Status = ((EFI_IP4_CONFIG_PROTOCOL*)IpXCfg)->GetData (
1013 IpXCfg,
1014 &IfInfoSize,
1015 IpXInterfaceInfo
1016 );
1017 }
1018
1019 if (EFI_ERROR (Status)) {
1020 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
1021 goto ON_ERROR;
1022 }
1023 //
1024 // Check whether the source address is one of the interface addresses.
1025 //
1026 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1027 for (AddrIndex = 0; AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount; AddrIndex++) {
1028
1029 Addr = &(((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfo[AddrIndex].Address);
1030 if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
1031 //
1032 // Match a certain interface address.
1033 //
1034 break;
1035 }
1036 }
1037
1038 if (AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount) {
1039 //
1040 // Found a nic handle with right interface address.
1041 //
1042 break;
1043 }
1044 } else {
1045 //
1046 // IP4 address check
1047 //
1048 if (EFI_IP4_EQUAL (&Private->SrcAddress, &((EFI_IP4_IPCONFIG_DATA*)IpXInterfaceInfo)->StationAddress)) {
1049 //
1050 // Match a certain interface address.
1051 //
1052 break;
1053 }
1054 }
1055 }
1056
1057 FreePool (IpXInterfaceInfo);
1058 IpXInterfaceInfo = NULL;
1059 }
1060 //
1061 // No exact interface address matched.
1062 //
1063
1064 if (HandleIndex == HandleNum) {
1065 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIGD_NIC_NF), gShellNetwork1HiiHandle, L"ping");
1066 Status = EFI_NOT_FOUND;
1067 goto ON_ERROR;
1068 }
1069
1070 Private->NicHandle = HandleBuffer[HandleIndex];
1071
1072 ASSERT (EfiSb != NULL);
1073 Status = EfiSb->CreateChild (EfiSb, &Private->IpChildHandle);
1074
1075 if (EFI_ERROR (Status)) {
1076 goto ON_ERROR;
1077 }
1078 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1079 Status = gBS->OpenProtocol (
1080 Private->IpChildHandle,
1081 &gEfiIp6ProtocolGuid,
1082 &Private->IpProtocol,
1083 gImageHandle,
1084 Private->IpChildHandle,
1085 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1086 );
1087 if (EFI_ERROR (Status)) {
1088 goto ON_ERROR;
1089 }
1090
1091
1092 ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
1093
1094 //
1095 // Configure the ip6 instance for icmp6 packet exchange.
1096 //
1097 Ip6Config.DefaultProtocol = 58;
1098 Ip6Config.AcceptAnyProtocol = FALSE;
1099 Ip6Config.AcceptIcmpErrors = TRUE;
1100 Ip6Config.AcceptPromiscuous = FALSE;
1101 Ip6Config.TrafficClass = 0;
1102 Ip6Config.HopLimit = 128;
1103 Ip6Config.FlowLabel = 0;
1104 Ip6Config.ReceiveTimeout = 0;
1105 Ip6Config.TransmitTimeout = 0;
1106
1107 IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
1108 IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
1109
1110 Status = ((EFI_IP6_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip6Config);
1111
1112 if (EFI_ERROR (Status)) {
1113 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
1114 goto ON_ERROR;
1115 }
1116
1117 Private->ProtocolPointers.Transmit = (PING_IPX_TRANSMIT )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Transmit;
1118 Private->ProtocolPointers.Receive = (PING_IPX_RECEIVE )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Receive;
1119 Private->ProtocolPointers.Cancel = (PING_IPX_CANCEL )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Cancel;
1120 Private->ProtocolPointers.Poll = (PING_IPX_POLL )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Poll;
1121 } else {
1122 Status = gBS->OpenProtocol (
1123 Private->IpChildHandle,
1124 &gEfiIp4ProtocolGuid,
1125 &Private->IpProtocol,
1126 gImageHandle,
1127 Private->IpChildHandle,
1128 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1129 );
1130 if (EFI_ERROR (Status)) {
1131 goto ON_ERROR;
1132 }
1133
1134
1135 ZeroMem (&Ip4Config, sizeof (EFI_IP4_CONFIG_DATA));
1136
1137 //
1138 // Configure the ip4 instance for icmp4 packet exchange.
1139 //
1140 // PING_IP4_COPY_ADDRESS (&Ip4Config.StationAddress, &Private->SrcAddress);
1141 // Ip4Config.SubnetMask.Addr[0] = 0xFF;
1142 // Ip4Config.SubnetMask.Addr[1] = 0xFF;
1143 // Ip4Config.SubnetMask.Addr[2] = 0xFF;
1144 // Ip4Config.SubnetMask.Addr[3] = 0x00;
1145 Ip4Config.DefaultProtocol = 1;
1146 Ip4Config.AcceptAnyProtocol = FALSE;
1147 Ip4Config.AcceptBroadcast = FALSE;
1148 Ip4Config.AcceptIcmpErrors = TRUE;
1149 Ip4Config.AcceptPromiscuous = FALSE;
1150 Ip4Config.DoNotFragment = FALSE;
1151 Ip4Config.RawData = FALSE;
1152 Ip4Config.ReceiveTimeout = 0;
1153 Ip4Config.TransmitTimeout = 0;
1154 Ip4Config.UseDefaultAddress = TRUE;
1155 Ip4Config.TimeToLive = 128;
1156 Ip4Config.TypeOfService = 0;
1157
1158 Status = ((EFI_IP4_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip4Config);
1159
1160 if (EFI_ERROR (Status)) {
1161 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
1162 goto ON_ERROR;
1163 }
1164
1165 Private->ProtocolPointers.Transmit = (PING_IPX_TRANSMIT )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Transmit;
1166 Private->ProtocolPointers.Receive = (PING_IPX_RECEIVE )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Receive;
1167 Private->ProtocolPointers.Cancel = (PING_IPX_CANCEL )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Cancel;
1168 Private->ProtocolPointers.Poll = (PING_IPX_POLL )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Poll;
1169 }
1170
1171 if (HandleBuffer != NULL) {
1172 FreePool (HandleBuffer);
1173 }
1174
1175 return EFI_SUCCESS;
1176
1177 ON_ERROR:
1178 if (HandleBuffer != NULL) {
1179 FreePool (HandleBuffer);
1180 }
1181
1182 if (IpXInterfaceInfo != NULL) {
1183 FreePool (IpXInterfaceInfo);
1184 }
1185
1186 if ((EfiSb != NULL) && (Private->IpChildHandle != NULL)) {
1187 EfiSb->DestroyChild (EfiSb, Private->IpChildHandle);
1188 }
1189
1190 return Status;
1191 }
1192
1193 /**
1194 Destroy the IP instance.
1195
1196 @param[in] Private The pointer of PING_PRIVATE_DATA.
1197
1198 **/
1199 VOID
1200 EFIAPI
1201 Ping6DestroyIp6Instance (
1202 IN PING_PRIVATE_DATA *Private
1203 )
1204 {
1205 EFI_STATUS Status;
1206 EFI_SERVICE_BINDING_PROTOCOL *IpSb;
1207
1208 gBS->CloseProtocol (
1209 Private->IpChildHandle,
1210 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ProtocolGuid:&gEfiIp4ProtocolGuid,
1211 gImageHandle,
1212 Private->IpChildHandle
1213 );
1214
1215 Status = gBS->HandleProtocol (
1216 Private->NicHandle,
1217 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
1218 (VOID **) &IpSb
1219 );
1220
1221 if (!EFI_ERROR(Status)) {
1222 IpSb->DestroyChild (IpSb, Private->IpChildHandle);
1223 }
1224 }
1225
1226 /**
1227 The Ping Process.
1228
1229 @param[in] SendNumber The send request count.
1230 @param[in] BufferSize The send buffer size.
1231 @param[in] SrcAddress The source address.
1232 @param[in] DstAddress The destination address.
1233 @param[in] IpChoice The choice between IPv4 and IPv6.
1234
1235 @retval SHELL_SUCCESS The ping processed successfullly.
1236 @retval others The ping processed unsuccessfully.
1237 **/
1238 SHELL_STATUS
1239 EFIAPI
1240 ShellPing (
1241 IN UINT32 SendNumber,
1242 IN UINT32 BufferSize,
1243 IN EFI_IPv6_ADDRESS *SrcAddress,
1244 IN EFI_IPv6_ADDRESS *DstAddress,
1245 IN UINT32 IpChoice
1246 )
1247 {
1248 EFI_STATUS Status;
1249 PING_PRIVATE_DATA *Private;
1250 PING_ICMPX_TX_INFO *TxInfo;
1251 LIST_ENTRY *Entry;
1252 LIST_ENTRY *NextEntry;
1253 SHELL_STATUS ShellStatus;
1254
1255 ShellStatus = SHELL_SUCCESS;
1256 Private = AllocateZeroPool (sizeof (PING_PRIVATE_DATA));
1257
1258 if (Private == NULL) {
1259 return (SHELL_OUT_OF_RESOURCES);
1260 }
1261
1262 Private->IpChoice = IpChoice;
1263 Private->Signature = PING_PRIVATE_DATA_SIGNATURE;
1264 Private->SendNum = SendNumber;
1265 Private->BufferSize = BufferSize;
1266 Private->RttMin = ~((UINT64 )(0x0));
1267 Private->Status = EFI_NOT_READY;
1268
1269 CopyMem(&Private->SrcAddress, SrcAddress, sizeof(Private->SrcAddress));
1270 CopyMem(&Private->DstAddress, DstAddress, sizeof(Private->DstAddress));
1271
1272 InitializeListHead (&Private->TxList);
1273
1274 //
1275 // Open and configure a ip instance for us.
1276 //
1277 Status = PingCreateIpInstance (Private);
1278
1279 if (EFI_ERROR (Status)) {
1280 ShellStatus = SHELL_ACCESS_DENIED;
1281 goto ON_EXIT;
1282 }
1283 //
1284 // Print the command line itself.
1285 //
1286 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_START), gShellNetwork1HiiHandle, mDstString, Private->BufferSize);
1287 //
1288 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1289 //
1290 Status = Ping6ReceiveEchoReply (Private);
1291
1292 if (EFI_ERROR (Status)) {
1293 ShellStatus = SHELL_ACCESS_DENIED;
1294 goto ON_EXIT;
1295 }
1296 //
1297 // Create and start timer to send icmp6 echo request packet per second.
1298 //
1299 Status = gBS->CreateEvent (
1300 EVT_TIMER | EVT_NOTIFY_SIGNAL,
1301 TPL_CALLBACK,
1302 Ping6OnTimerRoutine,
1303 Private,
1304 &Private->Timer
1305 );
1306
1307 if (EFI_ERROR (Status)) {
1308 ShellStatus = SHELL_ACCESS_DENIED;
1309 goto ON_EXIT;
1310 }
1311 //
1312 // Create a ipv6 token to send the first icmp6 echo request packet.
1313 //
1314 Status = PingSendEchoRequest (Private);
1315 //
1316 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1317 //
1318 if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
1319 ShellStatus = SHELL_ACCESS_DENIED;
1320 if(Status == EFI_NOT_FOUND) {
1321 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOSOURCE_INDO), gShellNetwork1HiiHandle, mDstString);
1322 } else if (Status == RETURN_NO_MAPPING) {
1323 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOROUTE_FOUND), gShellNetwork1HiiHandle, mDstString, mSrcString);
1324 } else {
1325 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NETWORK_ERROR), gShellNetwork1HiiHandle, L"ping", Status);
1326 }
1327
1328 goto ON_EXIT;
1329 }
1330
1331 Status = gBS->SetTimer (
1332 Private->Timer,
1333 TimerPeriodic,
1334 ONE_SECOND
1335 );
1336
1337 if (EFI_ERROR (Status)) {
1338 ShellStatus = SHELL_ACCESS_DENIED;
1339 goto ON_EXIT;
1340 }
1341 //
1342 // Control the ping6 process by two factors:
1343 // 1. Hot key
1344 // 2. Private->Status
1345 // 2.1. success means all icmp6 echo request packets get reply packets.
1346 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1347 // 2.3. noready means ping6 process is on-the-go.
1348 //
1349 while (Private->Status == EFI_NOT_READY) {
1350 Status = Private->ProtocolPointers.Poll (Private->IpProtocol);
1351 if (ShellGetExecutionBreakFlag()) {
1352 Private->Status = EFI_ABORTED;
1353 goto ON_STAT;
1354 }
1355 }
1356
1357 ON_STAT:
1358 //
1359 // Display the statistics in all.
1360 //
1361 gBS->SetTimer (Private->Timer, TimerCancel, 0);
1362
1363 if (Private->TxCount != 0) {
1364 ShellPrintHiiEx (
1365 -1,
1366 -1,
1367 NULL,
1368 STRING_TOKEN (STR_PING_STAT),
1369 gShellNetwork1HiiHandle,
1370 Private->TxCount,
1371 Private->RxCount,
1372 (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
1373 Private->RttSum
1374 );
1375 }
1376
1377 if (Private->RxCount != 0) {
1378 ShellPrintHiiEx (
1379 -1,
1380 -1,
1381 NULL,
1382 STRING_TOKEN (STR_PING_RTT),
1383 gShellNetwork1HiiHandle,
1384 Private->RttMin,
1385 Private->RttMax,
1386 DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL)
1387 );
1388 }
1389
1390 ON_EXIT:
1391
1392 if (Private != NULL) {
1393
1394 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
1395 TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
1396
1397 if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
1398 Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
1399 }
1400
1401 RemoveEntryList (&TxInfo->Link);
1402 PingDestroyTxInfo (TxInfo, Private->IpChoice);
1403 }
1404
1405 if (Private->Timer != NULL) {
1406 gBS->CloseEvent (Private->Timer);
1407 }
1408
1409 if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
1410 Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, &Private->RxToken);
1411 }
1412
1413 if (Private->RxToken.Event != NULL) {
1414 gBS->CloseEvent (Private->RxToken.Event);
1415 }
1416
1417 if (Private->IpChildHandle != NULL) {
1418 Ping6DestroyIp6Instance (Private);
1419 }
1420
1421 FreePool (Private);
1422 }
1423
1424 return ShellStatus;
1425 }
1426
1427 /**
1428 Function for 'ping' command.
1429
1430 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1431 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1432 **/
1433 SHELL_STATUS
1434 EFIAPI
1435 ShellCommandRunPing (
1436 IN EFI_HANDLE ImageHandle,
1437 IN EFI_SYSTEM_TABLE *SystemTable
1438 )
1439 {
1440 EFI_STATUS Status;
1441 SHELL_STATUS ShellStatus;
1442 EFI_IPv6_ADDRESS DstAddress;
1443 EFI_IPv6_ADDRESS SrcAddress;
1444 UINT64 BufferSize;
1445 UINTN SendNumber;
1446 LIST_ENTRY *ParamPackage;
1447 CONST CHAR16 *ValueStr;
1448 UINTN NonOptionCount;
1449 UINT32 IpChoice;
1450 CHAR16 *ProblemParam;
1451
1452 //
1453 // we use IPv6 buffers to hold items...
1454 // make sure this is enough space!
1455 //
1456 ASSERT(sizeof(EFI_IPv4_ADDRESS ) <= sizeof(EFI_IPv6_ADDRESS ));
1457 ASSERT(sizeof(EFI_IP4_COMPLETION_TOKEN) <= sizeof(EFI_IP6_COMPLETION_TOKEN ));
1458
1459 IpChoice = PING_IP_CHOICE_IP4;
1460
1461 ShellStatus = SHELL_SUCCESS;
1462 ProblemParam = NULL;
1463
1464 Status = ShellCommandLineParseEx (PingParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
1465 if (EFI_ERROR(Status)) {
1466 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ProblemParam);
1467 ShellStatus = SHELL_INVALID_PARAMETER;
1468 goto ON_EXIT;
1469 }
1470
1471 if (ShellCommandLineGetFlag (ParamPackage, L"-_ip6")) {
1472 IpChoice = PING_IP_CHOICE_IP6;
1473 }
1474
1475 //
1476 // Parse the paramter of count number.
1477 //
1478 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
1479 if (ValueStr != NULL) {
1480 SendNumber = ShellStrToUintn (ValueStr);
1481
1482 //
1483 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1484 //
1485 if ((SendNumber == 0) || (SendNumber > MAX_SEND_NUMBER)) {
1486 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1487 ShellStatus = SHELL_INVALID_PARAMETER;
1488 goto ON_EXIT;
1489 }
1490 } else {
1491 SendNumber = DEFAULT_SEND_COUNT;
1492 }
1493 //
1494 // Parse the paramter of buffer size.
1495 //
1496 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
1497 if (ValueStr != NULL) {
1498 BufferSize = ShellStrToUintn (ValueStr);
1499
1500 //
1501 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1502 //
1503 if ((BufferSize < 16) || (BufferSize > MAX_BUFFER_SIZE)) {
1504 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1505 ShellStatus = SHELL_INVALID_PARAMETER;
1506 goto ON_EXIT;
1507 }
1508 } else {
1509 BufferSize = DEFAULT_BUFFER_SIZE;
1510 }
1511
1512 ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
1513 ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
1514
1515 //
1516 // Parse the paramter of source ip address.
1517 //
1518 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-_s");
1519 if (ValueStr != NULL) {
1520 mSrcString = ValueStr;
1521 if (IpChoice == PING_IP_CHOICE_IP6) {
1522 Status = NetLibStrToIp6 (ValueStr, &SrcAddress);
1523 } else {
1524 Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&SrcAddress);
1525 }
1526 if (EFI_ERROR (Status)) {
1527 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1528 ShellStatus = SHELL_INVALID_PARAMETER;
1529 goto ON_EXIT;
1530 }
1531 }
1532 //
1533 // Parse the paramter of destination ip address.
1534 //
1535 NonOptionCount = ShellCommandLineGetCount(ParamPackage);
1536 if (NonOptionCount < 2) {
1537 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellNetwork1HiiHandle, L"ping");
1538 ShellStatus = SHELL_INVALID_PARAMETER;
1539 goto ON_EXIT;
1540 }
1541 if (NonOptionCount > 2) {
1542 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellNetwork1HiiHandle, L"ping");
1543 ShellStatus = SHELL_INVALID_PARAMETER;
1544 goto ON_EXIT;
1545 }
1546 ValueStr = ShellCommandLineGetRawValue (ParamPackage, 1);
1547 if (ValueStr != NULL) {
1548 mDstString = ValueStr;
1549 if (IpChoice == PING_IP_CHOICE_IP6) {
1550 Status = NetLibStrToIp6 (ValueStr, &DstAddress);
1551 } else {
1552 Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&DstAddress);
1553 }
1554 if (EFI_ERROR (Status)) {
1555 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1556 ShellStatus = SHELL_INVALID_PARAMETER;
1557 goto ON_EXIT;
1558 }
1559 }
1560 //
1561 // Get frequency to calculate the time from ticks.
1562 //
1563 Status = GetFrequency ();
1564
1565 if (EFI_ERROR(Status)) {
1566 goto ON_EXIT;
1567 }
1568 //
1569 // Enter into ping process.
1570 //
1571 ShellStatus = ShellPing (
1572 (UINT32)SendNumber,
1573 (UINT32)BufferSize,
1574 &SrcAddress,
1575 &DstAddress,
1576 IpChoice
1577 );
1578
1579 ON_EXIT:
1580 ShellCommandLineFreeVarList (ParamPackage);
1581 return ShellStatus;
1582 }