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