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