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