]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellNetwork1CommandsLib/Ping.c
ShellPkg: Fix typos in comments and variables
[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 - 2016, 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 Private->Status = EFI_ABORTED;
633 }
634 } else {
635 //
636 // All reply have already been received from the dest host.
637 //
638 Private->Status = EFI_SUCCESS;
639 }
640 //
641 // Singal to recycle the each rxdata here, not at the end of process.
642 //
643 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);
644 }
645
646 /**
647 Create a PING_IPX_COMPLETION_TOKEN.
648
649 @param[in] Private The pointer of PING_PRIVATE_DATA.
650 @param[in] TimeStamp The TimeStamp of request.
651 @param[in] SequenceNum The SequenceNum of request.
652
653 @return The pointer of PING_IPX_COMPLETION_TOKEN.
654
655 **/
656 PING_IPX_COMPLETION_TOKEN *
657 PingGenerateToken (
658 IN PING_PRIVATE_DATA *Private,
659 IN UINT32 TimeStamp,
660 IN UINT16 SequenceNum
661 )
662 {
663 EFI_STATUS Status;
664 PING_IPX_COMPLETION_TOKEN *Token;
665 VOID *TxData;
666 ICMPX_ECHO_REQUEST_REPLY *Request;
667 UINT16 HeadSum;
668 UINT16 TempChecksum;
669
670 Request = AllocateZeroPool (Private->BufferSize);
671 if (Request == NULL) {
672 return NULL;
673 }
674 TxData = AllocateZeroPool (Private->IpChoice==PING_IP_CHOICE_IP6?sizeof (EFI_IP6_TRANSMIT_DATA):sizeof (EFI_IP4_TRANSMIT_DATA));
675 if (TxData == NULL) {
676 FreePool (Request);
677 return NULL;
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
727 Token->Status = EFI_ABORTED;
728 Token->Packet.TxData = TxData;
729
730 Status = gBS->CreateEvent (
731 EVT_NOTIFY_SIGNAL,
732 TPL_CALLBACK,
733 Ping6OnEchoRequestSent,
734 Private,
735 &Token->Event
736 );
737
738 if (EFI_ERROR (Status)) {
739 FreePool (Request);
740 FreePool (TxData);
741 FreePool (Token);
742 return NULL;
743 }
744
745 return Token;
746 }
747
748 /**
749 Transmit the PING_IPX_COMPLETION_TOKEN.
750
751 @param[in] Private The pointer of PING_PRIVATE_DATA.
752
753 @retval EFI_SUCCESS Transmitted successfully.
754 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
755 @retval others Transmitted unsuccessfully.
756
757 **/
758 EFI_STATUS
759 PingSendEchoRequest (
760 IN PING_PRIVATE_DATA *Private
761 )
762 {
763 EFI_STATUS Status;
764 PING_ICMPX_TX_INFO *TxInfo;
765
766 TxInfo = AllocateZeroPool (sizeof (PING_ICMPX_TX_INFO));
767
768 if (TxInfo == NULL) {
769 return EFI_OUT_OF_RESOURCES;
770 }
771
772 TxInfo->TimeStamp = ReadTime (Private);
773 TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
774 TxInfo->Token = PingGenerateToken (
775 Private,
776 TxInfo->TimeStamp,
777 TxInfo->SequenceNum
778 );
779
780 if (TxInfo->Token == NULL) {
781 PingDestroyTxInfo (TxInfo, Private->IpChoice);
782 return EFI_OUT_OF_RESOURCES;
783 }
784
785 ASSERT(Private->ProtocolPointers.Transmit != NULL);
786 Status = Private->ProtocolPointers.Transmit (Private->IpProtocol, TxInfo->Token);
787
788 if (EFI_ERROR (Status)) {
789 PingDestroyTxInfo (TxInfo, Private->IpChoice);
790 return Status;
791 }
792
793 InsertTailList (&Private->TxList, &TxInfo->Link);
794 Private->TxCount++;
795
796 return EFI_SUCCESS;
797 }
798
799 /**
800 Place a completion token into the receive packet queue to receive the echo reply.
801
802 @param[in] Private The pointer of PING_PRIVATE_DATA.
803
804 @retval EFI_SUCCESS Put the token into the receive packet queue successfully.
805 @retval others Put the token into the receive packet queue unsuccessfully.
806
807 **/
808 EFI_STATUS
809 Ping6ReceiveEchoReply (
810 IN PING_PRIVATE_DATA *Private
811 )
812 {
813 EFI_STATUS Status;
814
815 ZeroMem (&Private->RxToken, sizeof (PING_IPX_COMPLETION_TOKEN));
816
817 Status = gBS->CreateEvent (
818 EVT_NOTIFY_SIGNAL,
819 TPL_CALLBACK,
820 Ping6OnEchoReplyReceived,
821 Private,
822 &Private->RxToken.Event
823 );
824
825 if (EFI_ERROR (Status)) {
826 return Status;
827 }
828
829 Private->RxToken.Status = EFI_NOT_READY;
830
831 return (Private->ProtocolPointers.Receive (Private->IpProtocol, &Private->RxToken));
832 }
833
834 /**
835 Remove the timeout request from the list.
836
837 @param[in] Event A EFI_EVENT type event.
838 @param[in] Context The pointer to Context.
839
840 **/
841 VOID
842 EFIAPI
843 Ping6OnTimerRoutine (
844 IN EFI_EVENT Event,
845 IN VOID *Context
846 )
847 {
848 EFI_STATUS Status;
849 PING_PRIVATE_DATA *Private;
850 PING_ICMPX_TX_INFO *TxInfo;
851 LIST_ENTRY *Entry;
852 LIST_ENTRY *NextEntry;
853 UINT64 Time;
854
855 Private = (PING_PRIVATE_DATA *) Context;
856 if (Private->Signature != PING_PRIVATE_DATA_SIGNATURE) {
857 Private->Status = EFI_NOT_FOUND;
858 return;
859 }
860
861 //
862 // Retransmit icmp6 echo request packets per second in sendnumber times.
863 //
864 if (Private->TxCount < Private->SendNum) {
865
866 Status = PingSendEchoRequest (Private);
867 if (Private->TxCount != 0){
868 if (EFI_ERROR (Status)) {
869 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_SEND_REQUEST), gShellNetwork1HiiHandle, Private->TxCount + 1);
870 }
871 }
872 }
873 //
874 // Check whether any icmp6 echo request in the list timeout.
875 //
876 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
877 TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
878 Time = CalculateTick (Private, TxInfo->TimeStamp, ReadTime (Private));
879
880 //
881 // Remove the timeout echo request from txlist.
882 //
883 if (Time > DEFAULT_TIMEOUT) {
884
885 if (EFI_ERROR (TxInfo->Token->Status)) {
886 Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
887 }
888 //
889 // Remove the timeout icmp6 echo request from list.
890 //
891 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_TIMEOUT), gShellNetwork1HiiHandle, TxInfo->SequenceNum);
892
893 RemoveEntryList (&TxInfo->Link);
894 PingDestroyTxInfo (TxInfo, Private->IpChoice);
895
896 Private->RxCount++;
897 Private->FailedCount++;
898
899 if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
900 //
901 // All the left icmp6 echo request in the list timeout.
902 //
903 Private->Status = EFI_TIMEOUT;
904 }
905 }
906 }
907 }
908
909 /**
910 Determine if a IP4 address is Link Local.
911
912 169.254.1.0 through 169.254.254.255 is link local.
913
914 @param[in] Address The address to test.
915
916 @retval TRUE It is.
917 @retval FALSE It is not.
918 **/
919 BOOLEAN
920 PingNetIp4IsLinkLocalAddr (
921 IN CONST EFI_IPv4_ADDRESS *Address
922 )
923 {
924 return ((BOOLEAN)(Address->Addr[0] == 169 && Address->Addr[1] == 254 && Address->Addr[2] >= 1 && Address->Addr[2] <= 254));
925 }
926
927 /**
928 Determine if a IP4 address is unspecified.
929
930 @param[in] Address The address to test.
931
932 @retval TRUE It is.
933 @retval FALSE It is not.
934 **/
935 BOOLEAN
936 PingNetIp4IsUnspecifiedAddr (
937 IN CONST EFI_IPv4_ADDRESS *Address
938 )
939 {
940 return ((BOOLEAN)((ReadUnaligned32 ((UINT32*)&Address->Addr[0])) == 0x00000000));
941 }
942
943 /**
944 Create a valid IP instance.
945
946 @param[in] Private The pointer of PING_PRIVATE_DATA.
947
948 @retval EFI_SUCCESS Create a valid IPx instance successfully.
949 @retval EFI_ABORTED Locate handle with ipx service binding protocol unsuccessfully.
950 @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link-local address.
951 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
952 @retval EFI_NOT_FOUND The source address is not found.
953 **/
954 EFI_STATUS
955 PingCreateIpInstance (
956 IN PING_PRIVATE_DATA *Private
957 )
958 {
959 EFI_STATUS Status;
960 UINTN HandleIndex;
961 UINTN HandleNum;
962 EFI_HANDLE *HandleBuffer;
963 BOOLEAN UnspecifiedSrc;
964 BOOLEAN MediaPresent;
965 EFI_SERVICE_BINDING_PROTOCOL *EfiSb;
966 VOID *IpXCfg;
967 EFI_IP6_CONFIG_DATA Ip6Config;
968 EFI_IP4_CONFIG_DATA Ip4Config;
969 VOID *IpXInterfaceInfo;
970 UINTN IfInfoSize;
971 EFI_IPv6_ADDRESS *Addr;
972 UINTN AddrIndex;
973
974 HandleBuffer = NULL;
975 UnspecifiedSrc = FALSE;
976 MediaPresent = TRUE;
977 EfiSb = NULL;
978 IpXInterfaceInfo = NULL;
979 IfInfoSize = 0;
980
981 //
982 // Locate all the handles with ip6 service binding protocol.
983 //
984 Status = gBS->LocateHandleBuffer (
985 ByProtocol,
986 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
987 NULL,
988 &HandleNum,
989 &HandleBuffer
990 );
991 if (EFI_ERROR (Status) || (HandleNum == 0) || (HandleBuffer == NULL)) {
992 return EFI_ABORTED;
993 }
994
995 if (Private->IpChoice == PING_IP_CHOICE_IP6 ? NetIp6IsUnspecifiedAddr ((EFI_IPv6_ADDRESS*)&Private->SrcAddress) : \
996 PingNetIp4IsUnspecifiedAddr ((EFI_IPv4_ADDRESS*)&Private->SrcAddress)) {
997 //
998 // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
999 //
1000 UnspecifiedSrc = TRUE;
1001 }
1002
1003 //
1004 // Source address is required when pinging a link-local address.
1005 //
1006 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1007 if (NetIp6IsLinkLocalAddr ((EFI_IPv6_ADDRESS*)&Private->DstAddress) && UnspecifiedSrc) {
1008 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle);
1009 Status = EFI_INVALID_PARAMETER;
1010 goto ON_ERROR;
1011 }
1012 } else {
1013 ASSERT(Private->IpChoice == PING_IP_CHOICE_IP4);
1014 if (PingNetIp4IsLinkLocalAddr ((EFI_IPv4_ADDRESS*)&Private->DstAddress) && UnspecifiedSrc) {
1015 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_INVALID_SOURCE), gShellNetwork1HiiHandle);
1016 Status = EFI_INVALID_PARAMETER;
1017 goto ON_ERROR;
1018 }
1019 }
1020
1021 //
1022 // For each ip6 protocol, check interface addresses list.
1023 //
1024 for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
1025 EfiSb = NULL;
1026 IpXInterfaceInfo = NULL;
1027 IfInfoSize = 0;
1028
1029 if (UnspecifiedSrc) {
1030 //
1031 // Check media.
1032 //
1033 NetLibDetectMedia (HandleBuffer[HandleIndex], &MediaPresent);
1034 if (!MediaPresent) {
1035 //
1036 // Skip this one.
1037 //
1038 continue;
1039 }
1040 }
1041
1042 Status = gBS->HandleProtocol (
1043 HandleBuffer[HandleIndex],
1044 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
1045 (VOID **) &EfiSb
1046 );
1047 if (EFI_ERROR (Status)) {
1048 goto ON_ERROR;
1049 }
1050
1051 //
1052 // Ip6config protocol and ip6 service binding protocol are installed
1053 // on the same handle.
1054 //
1055 Status = gBS->HandleProtocol (
1056 HandleBuffer[HandleIndex],
1057 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ConfigProtocolGuid:&gEfiIp4Config2ProtocolGuid,
1058 (VOID **) &IpXCfg
1059 );
1060
1061 if (EFI_ERROR (Status)) {
1062 goto ON_ERROR;
1063 }
1064 //
1065 // Get the interface information size.
1066 //
1067 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1068 Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
1069 IpXCfg,
1070 Ip6ConfigDataTypeInterfaceInfo,
1071 &IfInfoSize,
1072 NULL
1073 );
1074 } else {
1075 Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData (
1076 IpXCfg,
1077 Ip4Config2DataTypeInterfaceInfo,
1078 &IfInfoSize,
1079 NULL
1080 );
1081 }
1082
1083 //
1084 // Skip the ones not in current use.
1085 //
1086 if (Status == EFI_NOT_STARTED) {
1087 continue;
1088 }
1089
1090 if (Status != EFI_BUFFER_TOO_SMALL) {
1091 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
1092 goto ON_ERROR;
1093 }
1094
1095 IpXInterfaceInfo = AllocateZeroPool (IfInfoSize);
1096
1097 if (IpXInterfaceInfo == NULL) {
1098 Status = EFI_OUT_OF_RESOURCES;
1099 goto ON_ERROR;
1100 }
1101 //
1102 // Get the interface info.
1103 //
1104 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1105 Status = ((EFI_IP6_CONFIG_PROTOCOL*)IpXCfg)->GetData (
1106 IpXCfg,
1107 Ip6ConfigDataTypeInterfaceInfo,
1108 &IfInfoSize,
1109 IpXInterfaceInfo
1110 );
1111 } else {
1112 Status = ((EFI_IP4_CONFIG2_PROTOCOL*)IpXCfg)->GetData (
1113 IpXCfg,
1114 Ip4Config2DataTypeInterfaceInfo,
1115 &IfInfoSize,
1116 IpXInterfaceInfo
1117 );
1118 }
1119
1120 if (EFI_ERROR (Status)) {
1121 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_GETDATA), gShellNetwork1HiiHandle, Status);
1122 goto ON_ERROR;
1123 }
1124 //
1125 // Check whether the source address is one of the interface addresses.
1126 //
1127 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1128 for (AddrIndex = 0; AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount; AddrIndex++) {
1129 Addr = &(((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfo[AddrIndex].Address);
1130
1131 if (UnspecifiedSrc) {
1132 if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {
1133 //
1134 // Select the interface automatically.
1135 //
1136 CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress));
1137 break;
1138 }
1139 } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
1140 //
1141 // Match a certain interface address.
1142 //
1143 break;
1144 }
1145 }
1146
1147 if (AddrIndex < ((EFI_IP6_CONFIG_INTERFACE_INFO*)IpXInterfaceInfo)->AddressInfoCount) {
1148 //
1149 // Found a nic handle with right interface address.
1150 //
1151 break;
1152 }
1153 } else {
1154 if (UnspecifiedSrc) {
1155 if (!PingNetIp4IsUnspecifiedAddr (&((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress) &&
1156 !PingNetIp4IsLinkLocalAddr (&((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) {
1157 //
1158 // Select the interface automatically.
1159 //
1160 break;
1161 }
1162 } else if (EFI_IP4_EQUAL (&Private->SrcAddress, &((EFI_IP4_CONFIG2_INTERFACE_INFO*)IpXInterfaceInfo)->StationAddress)) {
1163 //
1164 // Match a certain interface address.
1165 //
1166 break;
1167 }
1168 }
1169
1170 FreePool (IpXInterfaceInfo);
1171 IpXInterfaceInfo = NULL;
1172 }
1173 //
1174 // No exact interface address matched.
1175 //
1176
1177 if (HandleIndex == HandleNum) {
1178 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIGD_NIC_NF), gShellNetwork1HiiHandle, L"ping");
1179 Status = EFI_NOT_FOUND;
1180 goto ON_ERROR;
1181 }
1182
1183 Private->NicHandle = HandleBuffer[HandleIndex];
1184
1185 ASSERT (EfiSb != NULL);
1186 Status = EfiSb->CreateChild (EfiSb, &Private->IpChildHandle);
1187
1188 if (EFI_ERROR (Status)) {
1189 goto ON_ERROR;
1190 }
1191 if (Private->IpChoice == PING_IP_CHOICE_IP6) {
1192 Status = gBS->OpenProtocol (
1193 Private->IpChildHandle,
1194 &gEfiIp6ProtocolGuid,
1195 &Private->IpProtocol,
1196 gImageHandle,
1197 Private->IpChildHandle,
1198 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1199 );
1200 if (EFI_ERROR (Status)) {
1201 goto ON_ERROR;
1202 }
1203
1204
1205 ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
1206
1207 //
1208 // Configure the ip6 instance for icmp6 packet exchange.
1209 //
1210 Ip6Config.DefaultProtocol = 58;
1211 Ip6Config.AcceptAnyProtocol = FALSE;
1212 Ip6Config.AcceptIcmpErrors = TRUE;
1213 Ip6Config.AcceptPromiscuous = FALSE;
1214 Ip6Config.TrafficClass = 0;
1215 Ip6Config.HopLimit = 128;
1216 Ip6Config.FlowLabel = 0;
1217 Ip6Config.ReceiveTimeout = 0;
1218 Ip6Config.TransmitTimeout = 0;
1219
1220 IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
1221 IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
1222
1223 Status = ((EFI_IP6_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip6Config);
1224
1225 if (EFI_ERROR (Status)) {
1226 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
1227 goto ON_ERROR;
1228 }
1229
1230 Private->ProtocolPointers.Transmit = (PING_IPX_TRANSMIT )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Transmit;
1231 Private->ProtocolPointers.Receive = (PING_IPX_RECEIVE )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Receive;
1232 Private->ProtocolPointers.Cancel = (PING_IPX_CANCEL )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Cancel;
1233 Private->ProtocolPointers.Poll = (PING_IPX_POLL )((EFI_IP6_PROTOCOL*)Private->IpProtocol)->Poll;
1234 } else {
1235 Status = gBS->OpenProtocol (
1236 Private->IpChildHandle,
1237 &gEfiIp4ProtocolGuid,
1238 &Private->IpProtocol,
1239 gImageHandle,
1240 Private->IpChildHandle,
1241 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1242 );
1243 if (EFI_ERROR (Status)) {
1244 goto ON_ERROR;
1245 }
1246
1247
1248 ZeroMem (&Ip4Config, sizeof (EFI_IP4_CONFIG_DATA));
1249
1250 //
1251 // Configure the ip4 instance for icmp4 packet exchange.
1252 //
1253 Ip4Config.DefaultProtocol = 1;
1254 Ip4Config.AcceptAnyProtocol = FALSE;
1255 Ip4Config.AcceptBroadcast = FALSE;
1256 Ip4Config.AcceptIcmpErrors = TRUE;
1257 Ip4Config.AcceptPromiscuous = FALSE;
1258 Ip4Config.DoNotFragment = FALSE;
1259 Ip4Config.RawData = FALSE;
1260 Ip4Config.ReceiveTimeout = 0;
1261 Ip4Config.TransmitTimeout = 0;
1262 Ip4Config.UseDefaultAddress = TRUE;
1263 Ip4Config.TimeToLive = 128;
1264 Ip4Config.TypeOfService = 0;
1265
1266 Status = ((EFI_IP4_PROTOCOL*)(Private->IpProtocol))->Configure (Private->IpProtocol, &Ip4Config);
1267
1268 if (EFI_ERROR (Status)) {
1269 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_CONFIG), gShellNetwork1HiiHandle, Status);
1270 goto ON_ERROR;
1271 }
1272
1273 Private->ProtocolPointers.Transmit = (PING_IPX_TRANSMIT )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Transmit;
1274 Private->ProtocolPointers.Receive = (PING_IPX_RECEIVE )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Receive;
1275 Private->ProtocolPointers.Cancel = (PING_IPX_CANCEL )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Cancel;
1276 Private->ProtocolPointers.Poll = (PING_IPX_POLL )((EFI_IP4_PROTOCOL*)Private->IpProtocol)->Poll;
1277 }
1278
1279 if (HandleBuffer != NULL) {
1280 FreePool (HandleBuffer);
1281 }
1282
1283 return EFI_SUCCESS;
1284
1285 ON_ERROR:
1286 if (HandleBuffer != NULL) {
1287 FreePool (HandleBuffer);
1288 }
1289
1290 if (IpXInterfaceInfo != NULL) {
1291 FreePool (IpXInterfaceInfo);
1292 }
1293
1294 if ((EfiSb != NULL) && (Private->IpChildHandle != NULL)) {
1295 EfiSb->DestroyChild (EfiSb, Private->IpChildHandle);
1296 }
1297
1298 return Status;
1299 }
1300
1301 /**
1302 Destroy the IP instance.
1303
1304 @param[in] Private The pointer of PING_PRIVATE_DATA.
1305
1306 **/
1307 VOID
1308 Ping6DestroyIp6Instance (
1309 IN PING_PRIVATE_DATA *Private
1310 )
1311 {
1312 EFI_STATUS Status;
1313 EFI_SERVICE_BINDING_PROTOCOL *IpSb;
1314
1315 gBS->CloseProtocol (
1316 Private->IpChildHandle,
1317 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ProtocolGuid:&gEfiIp4ProtocolGuid,
1318 gImageHandle,
1319 Private->IpChildHandle
1320 );
1321
1322 Status = gBS->HandleProtocol (
1323 Private->NicHandle,
1324 Private->IpChoice == PING_IP_CHOICE_IP6?&gEfiIp6ServiceBindingProtocolGuid:&gEfiIp4ServiceBindingProtocolGuid,
1325 (VOID **) &IpSb
1326 );
1327
1328 if (!EFI_ERROR(Status)) {
1329 IpSb->DestroyChild (IpSb, Private->IpChildHandle);
1330 }
1331 }
1332
1333
1334 /**
1335 The Ping Process.
1336
1337 @param[in] SendNumber The send request count.
1338 @param[in] BufferSize The send buffer size.
1339 @param[in] SrcAddress The source address.
1340 @param[in] DstAddress The destination address.
1341 @param[in] IpChoice The choice between IPv4 and IPv6.
1342
1343 @retval SHELL_SUCCESS The ping processed successfullly.
1344 @retval others The ping processed unsuccessfully.
1345 **/
1346 SHELL_STATUS
1347 ShellPing (
1348 IN UINT32 SendNumber,
1349 IN UINT32 BufferSize,
1350 IN EFI_IPv6_ADDRESS *SrcAddress,
1351 IN EFI_IPv6_ADDRESS *DstAddress,
1352 IN UINT32 IpChoice
1353 )
1354 {
1355 EFI_STATUS Status;
1356 PING_PRIVATE_DATA *Private;
1357 PING_ICMPX_TX_INFO *TxInfo;
1358 LIST_ENTRY *Entry;
1359 LIST_ENTRY *NextEntry;
1360 SHELL_STATUS ShellStatus;
1361
1362 ShellStatus = SHELL_SUCCESS;
1363 Private = AllocateZeroPool (sizeof (PING_PRIVATE_DATA));
1364
1365 if (Private == NULL) {
1366 return (SHELL_OUT_OF_RESOURCES);
1367 }
1368
1369 Private->IpChoice = IpChoice;
1370 Private->Signature = PING_PRIVATE_DATA_SIGNATURE;
1371 Private->SendNum = SendNumber;
1372 Private->BufferSize = BufferSize;
1373 Private->RttMin = ~((UINT64 )(0x0));
1374 Private->Status = EFI_NOT_READY;
1375
1376 CopyMem(&Private->SrcAddress, SrcAddress, sizeof(Private->SrcAddress));
1377 CopyMem(&Private->DstAddress, DstAddress, sizeof(Private->DstAddress));
1378
1379 InitializeListHead (&Private->TxList);
1380
1381 //
1382 // Open and configure a ip instance for us.
1383 //
1384 Status = PingCreateIpInstance (Private);
1385
1386 if (EFI_ERROR (Status)) {
1387 ShellStatus = SHELL_ACCESS_DENIED;
1388 goto ON_EXIT;
1389 }
1390 //
1391 // Print the command line itself.
1392 //
1393 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_START), gShellNetwork1HiiHandle, mDstString, Private->BufferSize);
1394 //
1395 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1396 //
1397 Status = Ping6ReceiveEchoReply (Private);
1398
1399 if (EFI_ERROR (Status)) {
1400 ShellStatus = SHELL_ACCESS_DENIED;
1401 goto ON_EXIT;
1402 }
1403 //
1404 // Create and start timer to send icmp6 echo request packet per second.
1405 //
1406 Status = gBS->CreateEvent (
1407 EVT_TIMER | EVT_NOTIFY_SIGNAL,
1408 TPL_CALLBACK,
1409 Ping6OnTimerRoutine,
1410 Private,
1411 &Private->Timer
1412 );
1413
1414 if (EFI_ERROR (Status)) {
1415 ShellStatus = SHELL_ACCESS_DENIED;
1416 goto ON_EXIT;
1417 }
1418
1419 //
1420 // Start a timer to calculate the RTT.
1421 //
1422 Status = PingInitRttTimer (Private);
1423 if (EFI_ERROR (Status)) {
1424 ShellStatus = SHELL_ACCESS_DENIED;
1425 goto ON_EXIT;
1426 }
1427
1428 //
1429 // Create a ipv6 token to send the first icmp6 echo request packet.
1430 //
1431 Status = PingSendEchoRequest (Private);
1432 //
1433 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1434 //
1435 if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
1436 ShellStatus = SHELL_ACCESS_DENIED;
1437 if(Status == EFI_NOT_FOUND) {
1438 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOSOURCE_INDO), gShellNetwork1HiiHandle, mDstString);
1439 } else if (Status == RETURN_NO_MAPPING) {
1440 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NOROUTE_FOUND), gShellNetwork1HiiHandle, mDstString, mSrcString);
1441 } else {
1442 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING_NETWORK_ERROR), gShellNetwork1HiiHandle, L"ping", Status);
1443 }
1444
1445 goto ON_EXIT;
1446 }
1447
1448 Status = gBS->SetTimer (
1449 Private->Timer,
1450 TimerPeriodic,
1451 ONE_SECOND
1452 );
1453
1454 if (EFI_ERROR (Status)) {
1455 ShellStatus = SHELL_ACCESS_DENIED;
1456 goto ON_EXIT;
1457 }
1458 //
1459 // Control the ping6 process by two factors:
1460 // 1. Hot key
1461 // 2. Private->Status
1462 // 2.1. success means all icmp6 echo request packets get reply packets.
1463 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1464 // 2.3. noready means ping6 process is on-the-go.
1465 //
1466 while (Private->Status == EFI_NOT_READY) {
1467 Status = Private->ProtocolPointers.Poll (Private->IpProtocol);
1468 if (ShellGetExecutionBreakFlag()) {
1469 Private->Status = EFI_ABORTED;
1470 goto ON_STAT;
1471 }
1472 }
1473
1474 ON_STAT:
1475 //
1476 // Display the statistics in all.
1477 //
1478 gBS->SetTimer (Private->Timer, TimerCancel, 0);
1479
1480 if (Private->TxCount != 0) {
1481 ShellPrintHiiEx (
1482 -1,
1483 -1,
1484 NULL,
1485 STRING_TOKEN (STR_PING_STAT),
1486 gShellNetwork1HiiHandle,
1487 Private->TxCount,
1488 (Private->RxCount - Private->FailedCount),
1489 (100 - ((100 * (Private->RxCount - Private->FailedCount)) / Private->TxCount)),
1490 Private->RttSum
1491 );
1492 }
1493
1494 if (Private->RxCount > Private->FailedCount) {
1495 ShellPrintHiiEx (
1496 -1,
1497 -1,
1498 NULL,
1499 STRING_TOKEN (STR_PING_RTT),
1500 gShellNetwork1HiiHandle,
1501 Private->RttMin,
1502 Private->RttMin + Private->TimerPeriod,
1503 Private->RttMax,
1504 Private->RttMax + Private->TimerPeriod,
1505 DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL),
1506 DivU64x64Remainder (Private->RttSum, (Private->RxCount - Private->FailedCount), NULL) + Private->TimerPeriod
1507 );
1508 }
1509
1510 ON_EXIT:
1511
1512 if (Private != NULL) {
1513
1514 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
1515 TxInfo = BASE_CR (Entry, PING_ICMPX_TX_INFO, Link);
1516
1517 if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
1518 Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, TxInfo->Token);
1519 }
1520
1521 RemoveEntryList (&TxInfo->Link);
1522 PingDestroyTxInfo (TxInfo, Private->IpChoice);
1523 }
1524
1525 PingFreeRttTimer (Private);
1526
1527 if (Private->Timer != NULL) {
1528 gBS->CloseEvent (Private->Timer);
1529 }
1530
1531 if (Private->IpProtocol != NULL && Private->ProtocolPointers.Cancel != NULL) {
1532 Status = Private->ProtocolPointers.Cancel (Private->IpProtocol, &Private->RxToken);
1533 }
1534
1535 if (Private->RxToken.Event != NULL) {
1536 gBS->CloseEvent (Private->RxToken.Event);
1537 }
1538
1539 if (Private->IpChildHandle != NULL) {
1540 Ping6DestroyIp6Instance (Private);
1541 }
1542
1543 FreePool (Private);
1544 }
1545
1546 return ShellStatus;
1547 }
1548
1549 /**
1550 Function for 'ping' command.
1551
1552 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1553 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1554
1555 @retval SHELL_SUCCESS The ping processed successfullly.
1556 @retval others The ping processed unsuccessfully.
1557
1558 **/
1559 SHELL_STATUS
1560 EFIAPI
1561 ShellCommandRunPing (
1562 IN EFI_HANDLE ImageHandle,
1563 IN EFI_SYSTEM_TABLE *SystemTable
1564 )
1565 {
1566 EFI_STATUS Status;
1567 SHELL_STATUS ShellStatus;
1568 EFI_IPv6_ADDRESS DstAddress;
1569 EFI_IPv6_ADDRESS SrcAddress;
1570 UINT64 BufferSize;
1571 UINTN SendNumber;
1572 LIST_ENTRY *ParamPackage;
1573 CONST CHAR16 *ValueStr;
1574 UINTN NonOptionCount;
1575 UINT32 IpChoice;
1576 CHAR16 *ProblemParam;
1577
1578 //
1579 // we use IPv6 buffers to hold items...
1580 // make sure this is enough space!
1581 //
1582 ASSERT(sizeof(EFI_IPv4_ADDRESS ) <= sizeof(EFI_IPv6_ADDRESS ));
1583 ASSERT(sizeof(EFI_IP4_COMPLETION_TOKEN) <= sizeof(EFI_IP6_COMPLETION_TOKEN ));
1584
1585 IpChoice = PING_IP_CHOICE_IP4;
1586
1587 ShellStatus = SHELL_SUCCESS;
1588 ProblemParam = NULL;
1589
1590 Status = ShellCommandLineParseEx (PingParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
1591 if (EFI_ERROR(Status)) {
1592 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ProblemParam);
1593 ShellStatus = SHELL_INVALID_PARAMETER;
1594 goto ON_EXIT;
1595 }
1596
1597 if (ShellCommandLineGetFlag (ParamPackage, L"-_ip6")) {
1598 IpChoice = PING_IP_CHOICE_IP6;
1599 }
1600
1601 //
1602 // Parse the parameter of count number.
1603 //
1604 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
1605 if (ValueStr != NULL) {
1606 SendNumber = ShellStrToUintn (ValueStr);
1607
1608 //
1609 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1610 //
1611 if ((SendNumber == 0) || (SendNumber > MAX_SEND_NUMBER)) {
1612 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1613 ShellStatus = SHELL_INVALID_PARAMETER;
1614 goto ON_EXIT;
1615 }
1616 } else {
1617 SendNumber = DEFAULT_SEND_COUNT;
1618 }
1619 //
1620 // Parse the parameter of buffer size.
1621 //
1622 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
1623 if (ValueStr != NULL) {
1624 BufferSize = ShellStrToUintn (ValueStr);
1625
1626 //
1627 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1628 //
1629 if ((BufferSize < 16) || (BufferSize > MAX_BUFFER_SIZE)) {
1630 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1631 ShellStatus = SHELL_INVALID_PARAMETER;
1632 goto ON_EXIT;
1633 }
1634 } else {
1635 BufferSize = DEFAULT_BUFFER_SIZE;
1636 }
1637
1638 ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
1639 ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
1640
1641 //
1642 // Parse the parameter of source ip address.
1643 //
1644 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
1645 if (ValueStr == NULL) {
1646 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-_s");
1647 }
1648
1649 if (ValueStr != NULL) {
1650 mSrcString = ValueStr;
1651 if (IpChoice == PING_IP_CHOICE_IP6) {
1652 Status = NetLibStrToIp6 (ValueStr, &SrcAddress);
1653 } else {
1654 Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&SrcAddress);
1655 }
1656 if (EFI_ERROR (Status)) {
1657 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1658 ShellStatus = SHELL_INVALID_PARAMETER;
1659 goto ON_EXIT;
1660 }
1661 }
1662 //
1663 // Parse the parameter of destination ip address.
1664 //
1665 NonOptionCount = ShellCommandLineGetCount(ParamPackage);
1666 if (NonOptionCount < 2) {
1667 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellNetwork1HiiHandle, L"ping");
1668 ShellStatus = SHELL_INVALID_PARAMETER;
1669 goto ON_EXIT;
1670 }
1671 if (NonOptionCount > 2) {
1672 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellNetwork1HiiHandle, L"ping");
1673 ShellStatus = SHELL_INVALID_PARAMETER;
1674 goto ON_EXIT;
1675 }
1676 ValueStr = ShellCommandLineGetRawValue (ParamPackage, 1);
1677 if (ValueStr != NULL) {
1678 mDstString = ValueStr;
1679 if (IpChoice == PING_IP_CHOICE_IP6) {
1680 Status = NetLibStrToIp6 (ValueStr, &DstAddress);
1681 } else {
1682 Status = NetLibStrToIp4 (ValueStr, (EFI_IPv4_ADDRESS*)&DstAddress);
1683 }
1684 if (EFI_ERROR (Status)) {
1685 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellNetwork1HiiHandle, L"ping", ValueStr);
1686 ShellStatus = SHELL_INVALID_PARAMETER;
1687 goto ON_EXIT;
1688 }
1689 }
1690
1691 //
1692 // Enter into ping process.
1693 //
1694 ShellStatus = ShellPing (
1695 (UINT32)SendNumber,
1696 (UINT32)BufferSize,
1697 &SrcAddress,
1698 &DstAddress,
1699 IpChoice
1700 );
1701
1702 ON_EXIT:
1703 ShellCommandLineFreeVarList (ParamPackage);
1704 return ShellStatus;
1705 }