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