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