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