]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellNetwork2CommandsLib/Ping6.c
ShellPkg: update ping6 to use timer service instead of timer arch protocol .
[mirror_edk2.git] / ShellPkg / Library / UefiShellNetwork2CommandsLib / Ping6.c
1 /** @file
2 The implementation for Ping6 application.
3
4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "UefiShellNetwork2CommandsLib.h"
17
18 #define PING6_DEFAULT_TIMEOUT 5000
19 #define PING6_MAX_SEND_NUMBER 10000
20 #define PING6_MAX_BUFFER_SIZE 32768
21 #define PING6_ONE_SECOND 10000000
22 #define STALL_1_MILLI_SECOND 1000
23
24 #pragma pack(1)
25
26 typedef struct _ICMP6_ECHO_REQUEST_REPLY {
27 UINT8 Type;
28 UINT8 Code;
29 UINT16 Checksum;
30 UINT16 Identifier;
31 UINT16 SequenceNum;
32 UINT32 TimeStamp;
33 UINT8 Data[1];
34 } ICMP6_ECHO_REQUEST_REPLY;
35
36 #pragma pack()
37
38 typedef struct _PING6_ICMP6_TX_INFO {
39 LIST_ENTRY Link;
40 UINT16 SequenceNum;
41 UINT32 TimeStamp;
42 EFI_IP6_COMPLETION_TOKEN *Token;
43 } PING6_ICMP6_TX_INFO;
44
45 typedef struct _PING6_PRIVATE_DATA {
46 EFI_HANDLE ImageHandle;
47 EFI_HANDLE NicHandle;
48 EFI_HANDLE Ip6ChildHandle;
49 EFI_IP6_PROTOCOL *Ip6;
50 EFI_EVENT Timer;
51
52 UINT32 TimerPeriod;
53 UINT32 RttTimerTick;
54 EFI_EVENT RttTimer;
55
56 EFI_STATUS Status;
57 LIST_ENTRY TxList;
58 EFI_IP6_COMPLETION_TOKEN RxToken;
59 UINT16 RxCount;
60 UINT16 TxCount;
61 UINT64 RttSum;
62 UINT64 RttMin;
63 UINT64 RttMax;
64 UINT32 SequenceNum;
65
66 EFI_IPv6_ADDRESS SrcAddress;
67 EFI_IPv6_ADDRESS DstAddress;
68 UINT32 SendNum;
69 UINT32 BufferSize;
70 } PING6_PRIVATE_DATA;
71
72
73 SHELL_PARAM_ITEM Ping6ParamList[] = {
74 {
75 L"-l",
76 TypeValue
77 },
78 {
79 L"-n",
80 TypeValue
81 },
82 {
83 L"-s",
84 TypeValue
85 },
86 {
87 L"-?",
88 TypeFlag
89 },
90 {
91 NULL,
92 TypeMax
93 },
94 };
95
96 //
97 // Global Variables in Ping6 application.
98 //
99 CONST CHAR16 *mIp6DstString;
100 CONST CHAR16 *mIp6SrcString;
101 EFI_CPU_ARCH_PROTOCOL *Cpu = NULL;
102
103 /**
104 RTT timer tick routine.
105
106 @param[in] Event A EFI_EVENT type event.
107 @param[in] Context The pointer to Context.
108
109 **/
110 VOID
111 EFIAPI
112 Ping6RttTimerTickRoutine (
113 IN EFI_EVENT Event,
114 IN VOID *Context
115 )
116 {
117 UINT32 *RttTimerTick;
118
119 RttTimerTick = (UINT32*) Context;
120 (*RttTimerTick)++;
121 }
122
123 /**
124 Get the timer period of the system.
125
126 This function tries to get the system timer period by creating
127 an 1ms period timer.
128
129 @return System timer period in MS, or 0 if operation failed.
130
131 **/
132 UINT32
133 Ping6GetTimerPeriod(
134 VOID
135 )
136 {
137 EFI_STATUS Status;
138 UINT32 RttTimerTick;
139 EFI_EVENT TimerEvent;
140 UINT32 StallCounter;
141 EFI_TPL OldTpl;
142
143 RttTimerTick = 0;
144 StallCounter = 0;
145
146 Status = gBS->CreateEvent (
147 EVT_TIMER | EVT_NOTIFY_SIGNAL,
148 TPL_NOTIFY,
149 Ping6RttTimerTickRoutine,
150 &RttTimerTick,
151 &TimerEvent
152 );
153 if (EFI_ERROR (Status)) {
154 return 0;
155 }
156
157 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
158 Status = gBS->SetTimer (
159 TimerEvent,
160 TimerPeriodic,
161 TICKS_PER_MS
162 );
163 if (EFI_ERROR (Status)) {
164 gBS->CloseEvent (TimerEvent);
165 return 0;
166 }
167
168 while (RttTimerTick < 10) {
169 gBS->Stall (STALL_1_MILLI_SECOND);
170 ++StallCounter;
171 }
172
173 gBS->RestoreTPL (OldTpl);
174
175 gBS->SetTimer (TimerEvent, TimerCancel, 0);
176 gBS->CloseEvent (TimerEvent);
177
178 return StallCounter / RttTimerTick;
179 }
180
181
182 /**
183 Initialize the timer event for RTT (round trip time).
184
185 @param[in] Private The pointer to PING6_PRIVATE_DATA.
186
187 @retval EFI_SUCCESS RTT timer is started.
188 @retval Others Failed to start the RTT timer.
189
190 **/
191 EFI_STATUS
192 Ping6InitRttTimer (
193 IN PING6_PRIVATE_DATA *Private
194 )
195 {
196 EFI_STATUS Status;
197
198 Private->TimerPeriod = Ping6GetTimerPeriod ();
199 if (Private->TimerPeriod == 0) {
200 return EFI_ABORTED;
201 }
202
203 Private->RttTimerTick = 0;
204 Status = gBS->CreateEvent (
205 EVT_TIMER | EVT_NOTIFY_SIGNAL,
206 TPL_NOTIFY,
207 Ping6RttTimerTickRoutine,
208 &Private->RttTimerTick,
209 &Private->RttTimer
210 );
211 if (EFI_ERROR (Status)) {
212 return Status;
213 }
214
215 Status = gBS->SetTimer (
216 Private->RttTimer,
217 TimerPeriodic,
218 TICKS_PER_MS
219 );
220 if (EFI_ERROR (Status)) {
221 gBS->CloseEvent (Private->RttTimer);
222 return Status;
223 }
224
225 return EFI_SUCCESS;
226
227 }
228
229 /**
230 Free RTT timer event resource.
231
232 @param[in] Private The pointer to PING6_PRIVATE_DATA.
233
234 **/
235 VOID
236 Ping6FreeRttTimer (
237 IN PING6_PRIVATE_DATA *Private
238 )
239 {
240 if (Private->RttTimer != NULL) {
241 gBS->SetTimer (Private->RttTimer, TimerCancel, 0);
242 gBS->CloseEvent (Private->RttTimer);
243 }
244 }
245
246 /**
247 Read the current time.
248
249 @param[in] Private The pointer to PING6_PRIVATE_DATA.
250
251 @retval the current tick value.
252 **/
253 UINT32
254 Ping6ReadTime (
255 IN PING6_PRIVATE_DATA *Private
256 )
257 {
258 return Private->RttTimerTick;
259 }
260
261 /**
262 Get and calculate the duration in ms.
263
264 @param[in] Private The pointer to PING6_PRIVATE_DATA.
265 @param[in] Begin The start point of time.
266 @param[in] End The end point of time.
267
268 @return The duration in ms.
269
270 **/
271 UINT32
272 Ping6CalculateTick (
273 IN PING6_PRIVATE_DATA *Private,
274 IN UINT32 Begin,
275 IN UINT32 End
276 )
277 {
278 if (End < Begin) {
279 return (0);
280 }
281
282 return (End - Begin) * Private->TimerPeriod;
283
284 }
285
286 /**
287 Destroy IPING6_ICMP6_TX_INFO, and recollect the memory.
288
289 @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO.
290
291 **/
292 VOID
293 Ping6DestroyTxInfo (
294 IN PING6_ICMP6_TX_INFO *TxInfo
295 )
296 {
297 EFI_IP6_TRANSMIT_DATA *TxData;
298 EFI_IP6_FRAGMENT_DATA *FragData;
299 UINTN Index;
300
301 ASSERT (TxInfo != NULL);
302
303 if (TxInfo->Token != NULL) {
304
305 if (TxInfo->Token->Event != NULL) {
306 gBS->CloseEvent (TxInfo->Token->Event);
307 }
308
309 TxData = TxInfo->Token->Packet.TxData;
310 if (TxData != NULL) {
311
312 if (TxData->OverrideData != NULL) {
313 FreePool (TxData->OverrideData);
314 }
315
316 if (TxData->ExtHdrs != NULL) {
317 FreePool (TxData->ExtHdrs);
318 }
319
320 for (Index = 0; Index < TxData->FragmentCount; Index++) {
321 FragData = TxData->FragmentTable[Index].FragmentBuffer;
322 if (FragData != NULL) {
323 FreePool (FragData);
324 }
325 }
326 }
327
328 FreePool (TxInfo->Token);
329 }
330
331 FreePool (TxInfo);
332 }
333
334 /**
335 Match the request, and reply with SequenceNum/TimeStamp.
336
337 @param[in] Private The pointer to PING6_PRIVATE_DATA.
338 @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY.
339
340 @retval EFI_SUCCESS The match is successful.
341 @retval EFI_NOT_FOUND The reply can't be matched with any request.
342
343 **/
344 EFI_STATUS
345 Ping6OnMatchEchoReply (
346 IN PING6_PRIVATE_DATA *Private,
347 IN ICMP6_ECHO_REQUEST_REPLY *Packet
348 )
349 {
350 PING6_ICMP6_TX_INFO *TxInfo;
351 LIST_ENTRY *Entry;
352 LIST_ENTRY *NextEntry;
353
354 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
355 TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
356
357 if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) {
358 Private->RxCount++;
359 RemoveEntryList (&TxInfo->Link);
360 Ping6DestroyTxInfo (TxInfo);
361 return EFI_SUCCESS;
362 }
363 }
364
365 return EFI_NOT_FOUND;
366 }
367
368 /**
369 The original intention is to send a request.
370 Currently, the application retransmits an icmp6 echo request packet
371 per second in sendnumber times that is specified by the user.
372 Because nothing can be done here, all things move to the timer rountine.
373
374 @param[in] Event A EFI_EVENT type event.
375 @param[in] Context The pointer to Context.
376
377 **/
378 VOID
379 EFIAPI
380 Ping6OnEchoRequestSent6 (
381 IN EFI_EVENT Event,
382 IN VOID *Context
383 )
384 {
385 }
386
387 /**
388 receive reply, match and print reply infomation.
389
390 @param[in] Event A EFI_EVENT type event.
391 @param[in] Context The pointer to context.
392
393 **/
394 VOID
395 EFIAPI
396 Ping6OnEchoReplyReceived6 (
397 IN EFI_EVENT Event,
398 IN VOID *Context
399 )
400 {
401 EFI_STATUS Status;
402 PING6_PRIVATE_DATA *Private;
403 EFI_IP6_COMPLETION_TOKEN *RxToken;
404 EFI_IP6_RECEIVE_DATA *RxData;
405 ICMP6_ECHO_REQUEST_REPLY *Reply;
406 UINT32 PayLoad;
407 UINT32 Rtt;
408
409 Private = (PING6_PRIVATE_DATA *) Context;
410
411 if (Private->Status == EFI_ABORTED) {
412 return;
413 }
414
415 RxToken = &Private->RxToken;
416 RxData = RxToken->Packet.RxData;
417 Reply = RxData->FragmentTable[0].FragmentBuffer;
418 PayLoad = RxData->DataLength;
419
420 if (RxData->Header->NextHeader != IP6_ICMP) {
421 goto ON_EXIT;
422 }
423
424 if (!IP6_IS_MULTICAST (&Private->DstAddress) &&
425 !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) {
426 goto ON_EXIT;
427 }
428
429 if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) {
430 goto ON_EXIT;
431 }
432
433 if (PayLoad != Private->BufferSize) {
434 goto ON_EXIT;
435 }
436 //
437 // Check whether the reply matches the sent request before.
438 //
439 Status = Ping6OnMatchEchoReply (Private, Reply);
440 if (EFI_ERROR(Status)) {
441 goto ON_EXIT;
442 }
443 //
444 // Display statistics on this icmp6 echo reply packet.
445 //
446 Rtt = Ping6CalculateTick (Private, Reply->TimeStamp, Ping6ReadTime (Private));
447
448 Private->RttSum += Rtt;
449 Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin;
450 Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax;
451
452 ShellPrintHiiEx (
453 -1,
454 -1,
455 NULL,
456 STRING_TOKEN (STR_PING6_REPLY_INFO),
457 gShellNetwork2HiiHandle,
458 PayLoad,
459 mIp6DstString,
460 Reply->SequenceNum,
461 RxData->Header->HopLimit,
462 Rtt,
463 Rtt + Private->TimerPeriod
464 );
465
466 ON_EXIT:
467
468 if (Private->RxCount < Private->SendNum) {
469 //
470 // Continue to receive icmp6 echo reply packets.
471 //
472 RxToken->Status = EFI_ABORTED;
473
474 Status = Private->Ip6->Receive (Private->Ip6, RxToken);
475
476 if (EFI_ERROR (Status)) {
477 Private->Status = EFI_ABORTED;
478 }
479 } else {
480 //
481 // All reply have already been received from the dest host.
482 //
483 Private->Status = EFI_SUCCESS;
484 }
485 //
486 // Singal to recycle the each rxdata here, not at the end of process.
487 //
488 gBS->SignalEvent (RxData->RecycleSignal);
489 }
490
491 /**
492 Initial EFI_IP6_COMPLETION_TOKEN.
493
494 @param[in] Private The pointer of PING6_PRIVATE_DATA.
495 @param[in] TimeStamp The TimeStamp of request.
496 @param[in] SequenceNum The SequenceNum of request.
497
498 @return The pointer of EFI_IP6_COMPLETION_TOKEN.
499
500 **/
501 EFI_IP6_COMPLETION_TOKEN *
502 Ping6GenerateToken (
503 IN PING6_PRIVATE_DATA *Private,
504 IN UINT32 TimeStamp,
505 IN UINT16 SequenceNum
506 )
507 {
508 EFI_STATUS Status;
509 EFI_IP6_COMPLETION_TOKEN *Token;
510 EFI_IP6_TRANSMIT_DATA *TxData;
511 ICMP6_ECHO_REQUEST_REPLY *Request;
512
513 Request = AllocateZeroPool (Private->BufferSize);
514
515 if (Request == NULL) {
516 return NULL;
517 }
518 //
519 // Assembly icmp6 echo request packet.
520 //
521 Request->Type = ICMP_V6_ECHO_REQUEST;
522 Request->Code = 0;
523 Request->SequenceNum = SequenceNum;
524 Request->TimeStamp = TimeStamp;
525 Request->Identifier = 0;
526 //
527 // Leave check sum to ip6 layer, since it has no idea of source address
528 // selection.
529 //
530 Request->Checksum = 0;
531
532 TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA));
533
534 if (TxData == NULL) {
535 FreePool (Request);
536 return NULL;
537 }
538 //
539 // Assembly ipv6 token for transmit.
540 //
541 TxData->OverrideData = 0;
542 TxData->ExtHdrsLength = 0;
543 TxData->ExtHdrs = NULL;
544 TxData->DataLength = Private->BufferSize;
545 TxData->FragmentCount = 1;
546 TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request;
547 TxData->FragmentTable[0].FragmentLength = Private->BufferSize;
548
549 Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN));
550
551 if (Token == NULL) {
552 FreePool (Request);
553 FreePool (TxData);
554 return NULL;
555 }
556
557 Token->Status = EFI_ABORTED;
558 Token->Packet.TxData = TxData;
559
560 Status = gBS->CreateEvent (
561 EVT_NOTIFY_SIGNAL,
562 TPL_CALLBACK,
563 Ping6OnEchoRequestSent6,
564 Private,
565 &Token->Event
566 );
567
568 if (EFI_ERROR (Status)) {
569 FreePool (Request);
570 FreePool (TxData);
571 FreePool (Token);
572 return NULL;
573 }
574
575 return Token;
576 }
577
578 /**
579 Transmit the EFI_IP6_COMPLETION_TOKEN.
580
581 @param[in] Private The pointer of PING6_PRIVATE_DATA.
582
583 @retval EFI_SUCCESS Transmitted successfully.
584 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
585 @retval others Transmitted unsuccessfully.
586
587 **/
588 EFI_STATUS
589 Ping6SendEchoRequest (
590 IN PING6_PRIVATE_DATA *Private
591 )
592 {
593 EFI_STATUS Status;
594 PING6_ICMP6_TX_INFO *TxInfo;
595
596 TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO));
597
598 if (TxInfo == NULL) {
599 return EFI_OUT_OF_RESOURCES;
600 }
601
602 TxInfo->TimeStamp = Ping6ReadTime (Private);
603 TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1);
604
605 TxInfo->Token = Ping6GenerateToken (
606 Private,
607 TxInfo->TimeStamp,
608 TxInfo->SequenceNum
609 );
610
611 if (TxInfo->Token == NULL) {
612 Ping6DestroyTxInfo (TxInfo);
613 return EFI_OUT_OF_RESOURCES;
614 }
615
616 Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token);
617
618 if (EFI_ERROR (Status)) {
619 Ping6DestroyTxInfo (TxInfo);
620 return Status;
621 }
622
623 InsertTailList (&Private->TxList, &TxInfo->Link);
624 Private->TxCount++;
625
626 return EFI_SUCCESS;
627 }
628
629 /**
630 Place a completion token into the receive packet queue to receive the echo reply.
631
632 @param[in] Private The pointer of PING6_PRIVATE_DATA.
633
634 @retval EFI_SUCCESS Put the token into the receive packet queue successfully.
635 @retval others Put the token into the receive packet queue unsuccessfully.
636
637 **/
638 EFI_STATUS
639 Ping6OnReceiveEchoReply (
640 IN PING6_PRIVATE_DATA *Private
641 )
642 {
643 EFI_STATUS Status;
644
645 ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN));
646
647 Status = gBS->CreateEvent (
648 EVT_NOTIFY_SIGNAL,
649 TPL_CALLBACK,
650 Ping6OnEchoReplyReceived6,
651 Private,
652 &Private->RxToken.Event
653 );
654
655 if (EFI_ERROR (Status)) {
656 return Status;
657 }
658
659 Private->RxToken.Status = EFI_NOT_READY;
660
661 return Private->Ip6->Receive (Private->Ip6, &Private->RxToken);
662 }
663
664 /**
665 Remove the timeout request from the list.
666
667 @param[in] Event A EFI_EVENT type event.
668 @param[in] Context The pointer to Context.
669
670 **/
671 VOID
672 EFIAPI
673 Ping6OnTimerRoutine6 (
674 IN EFI_EVENT Event,
675 IN VOID *Context
676 )
677 {
678 EFI_STATUS Status;
679 PING6_PRIVATE_DATA *Private;
680 PING6_ICMP6_TX_INFO *TxInfo;
681 LIST_ENTRY *Entry;
682 LIST_ENTRY *NextEntry;
683 UINT64 Time;
684
685 Private = (PING6_PRIVATE_DATA *) Context;
686
687 //
688 // Retransmit icmp6 echo request packets per second in sendnumber times.
689 //
690 if (Private->TxCount < Private->SendNum) {
691
692 Status = Ping6SendEchoRequest (Private);
693 if (Private->TxCount != 0){
694 if (EFI_ERROR (Status)) {
695 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), gShellNetwork2HiiHandle, Private->TxCount + 1);
696 }
697 }
698 }
699 //
700 // Check whether any icmp6 echo request in the list timeout.
701 //
702 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
703 TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
704 Time = Ping6CalculateTick (Private, TxInfo->TimeStamp, Ping6ReadTime (Private));
705
706 //
707 // Remove the timeout echo request from txlist.
708 //
709 if (Time > PING6_DEFAULT_TIMEOUT) {
710
711 if (EFI_ERROR (TxInfo->Token->Status)) {
712 Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
713 }
714 //
715 // Remove the timeout icmp6 echo request from list.
716 //
717 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), gShellNetwork2HiiHandle, TxInfo->SequenceNum);
718
719 RemoveEntryList (&TxInfo->Link);
720 Ping6DestroyTxInfo (TxInfo);
721
722 if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) {
723 //
724 // All the left icmp6 echo request in the list timeout.
725 //
726 Private->Status = EFI_TIMEOUT;
727 }
728 }
729 }
730 }
731
732 /**
733 Create a valid IP6 instance.
734
735 @param[in] Private The pointer of PING6_PRIVATE_DATA.
736
737 @retval EFI_SUCCESS Create a valid IP6 instance successfully.
738 @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully.
739 @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address.
740 @retval EFI_OUT_OF_RESOURCES No memory is available on the platform.
741 @retval EFI_NOT_FOUND The source address is not found.
742 **/
743 EFI_STATUS
744 Ping6CreateIpInstance (
745 IN PING6_PRIVATE_DATA *Private
746 )
747 {
748 EFI_STATUS Status;
749 UINTN HandleIndex;
750 UINTN HandleNum;
751 EFI_HANDLE *HandleBuffer;
752 BOOLEAN UnspecifiedSrc;
753 BOOLEAN MediaPresent;
754 EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
755 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
756 EFI_IP6_CONFIG_DATA Ip6Config;
757 EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
758 UINTN IfInfoSize;
759 EFI_IPv6_ADDRESS *Addr;
760 UINTN AddrIndex;
761
762 HandleBuffer = NULL;
763 UnspecifiedSrc = FALSE;
764 MediaPresent = TRUE;
765 Ip6Sb = NULL;
766 IfInfo = NULL;
767 IfInfoSize = 0;
768
769 //
770 // Locate all the handles with ip6 service binding protocol.
771 //
772 Status = gBS->LocateHandleBuffer (
773 ByProtocol,
774 &gEfiIp6ServiceBindingProtocolGuid,
775 NULL,
776 &HandleNum,
777 &HandleBuffer
778 );
779 if (EFI_ERROR (Status) || (HandleNum == 0)) {
780 return EFI_ABORTED;
781 }
782
783 if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) {
784 //
785 // SrcAddress is unspecified. So, both connected and configured interface will be automatic selected.
786 //
787 UnspecifiedSrc = TRUE;
788 }
789
790 //
791 // Source address is required when pinging a link-local address.
792 //
793 if (NetIp6IsLinkLocalAddr (&Private->DstAddress) && UnspecifiedSrc) {
794 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), gShellNetwork2HiiHandle);
795 Status = EFI_INVALID_PARAMETER;
796 goto ON_ERROR;
797 }
798
799 //
800 // For each ip6 protocol, check interface addresses list.
801 //
802 for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
803
804 Ip6Sb = NULL;
805 IfInfo = NULL;
806 IfInfoSize = 0;
807
808 if (UnspecifiedSrc) {
809 //
810 // Check media.
811 //
812 NetLibDetectMedia (HandleBuffer[HandleIndex], &MediaPresent);
813 if (!MediaPresent) {
814 //
815 // Skip this one.
816 //
817 continue;
818 }
819 }
820
821 Status = gBS->HandleProtocol (
822 HandleBuffer[HandleIndex],
823 &gEfiIp6ServiceBindingProtocolGuid,
824 (VOID **) &Ip6Sb
825 );
826 if (EFI_ERROR (Status)) {
827 goto ON_ERROR;
828 }
829
830 //
831 // Ip6config protocol and ip6 service binding protocol are installed
832 // on the same handle.
833 //
834 Status = gBS->HandleProtocol (
835 HandleBuffer[HandleIndex],
836 &gEfiIp6ConfigProtocolGuid,
837 (VOID **) &Ip6Cfg
838 );
839
840 if (EFI_ERROR (Status)) {
841 goto ON_ERROR;
842 }
843 //
844 // Get the interface information size.
845 //
846 Status = Ip6Cfg->GetData (
847 Ip6Cfg,
848 Ip6ConfigDataTypeInterfaceInfo,
849 &IfInfoSize,
850 NULL
851 );
852
853 if (Status != EFI_BUFFER_TOO_SMALL) {
854 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
855 goto ON_ERROR;
856 }
857
858 IfInfo = AllocateZeroPool (IfInfoSize);
859
860 if (IfInfo == NULL) {
861 Status = EFI_OUT_OF_RESOURCES;
862 goto ON_ERROR;
863 }
864 //
865 // Get the interface info.
866 //
867 Status = Ip6Cfg->GetData (
868 Ip6Cfg,
869 Ip6ConfigDataTypeInterfaceInfo,
870 &IfInfoSize,
871 IfInfo
872 );
873
874 if (EFI_ERROR (Status)) {
875 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), gShellNetwork2HiiHandle, Status);
876 goto ON_ERROR;
877 }
878 //
879 // Check whether the source address is one of the interface addresses.
880 //
881 for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) {
882 Addr = &(IfInfo->AddressInfo[AddrIndex].Address);
883
884 if (UnspecifiedSrc) {
885 if (!NetIp6IsUnspecifiedAddr (Addr) && !NetIp6IsLinkLocalAddr (Addr)) {
886 //
887 // Select the interface automatically.
888 //
889 CopyMem(&Private->SrcAddress, Addr, sizeof(Private->SrcAddress));
890 break;
891 }
892 } else if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) {
893 //
894 // Match a certain interface address.
895 //
896 break;
897 }
898 }
899
900 if (AddrIndex < IfInfo->AddressInfoCount) {
901 //
902 // Found a nic handle with right interface address.
903 //
904 break;
905 }
906
907 FreePool (IfInfo);
908 IfInfo = NULL;
909 }
910 //
911 // No exact interface address matched.
912 //
913
914 if (HandleIndex == HandleNum) {
915 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_CONFIGD_NIC_NF), gShellNetwork2HiiHandle);
916 Status = EFI_NOT_FOUND;
917 goto ON_ERROR;
918 }
919
920 Private->NicHandle = HandleBuffer[HandleIndex];
921
922 ASSERT (Ip6Sb != NULL);
923 Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle);
924
925 if (EFI_ERROR (Status)) {
926 goto ON_ERROR;
927 }
928
929 Status = gBS->OpenProtocol (
930 Private->Ip6ChildHandle,
931 &gEfiIp6ProtocolGuid,
932 (VOID **) &Private->Ip6,
933 Private->ImageHandle,
934 Private->Ip6ChildHandle,
935 EFI_OPEN_PROTOCOL_GET_PROTOCOL
936 );
937 if (EFI_ERROR (Status)) {
938 goto ON_ERROR;
939 }
940
941 ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA));
942
943 //
944 // Configure the ip6 instance for icmp6 packet exchange.
945 //
946 Ip6Config.DefaultProtocol = 58;
947 Ip6Config.AcceptAnyProtocol = FALSE;
948 Ip6Config.AcceptIcmpErrors = TRUE;
949 Ip6Config.AcceptPromiscuous = FALSE;
950 Ip6Config.TrafficClass = 0;
951 Ip6Config.HopLimit = 128;
952 Ip6Config.FlowLabel = 0;
953 Ip6Config.ReceiveTimeout = 0;
954 Ip6Config.TransmitTimeout = 0;
955
956 IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress);
957
958 IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress);
959
960 Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config);
961
962 if (EFI_ERROR (Status)) {
963 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), gShellNetwork2HiiHandle, Status);
964 goto ON_ERROR;
965 }
966
967 return EFI_SUCCESS;
968
969 ON_ERROR:
970 if (HandleBuffer != NULL) {
971 FreePool (HandleBuffer);
972 }
973
974 if (IfInfo != NULL) {
975 FreePool (IfInfo);
976 }
977
978 if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) {
979 Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
980 }
981
982 return Status;
983 }
984
985 /**
986 Destroy the IP6 instance.
987
988 @param[in] Private The pointer of PING6_PRIVATE_DATA.
989
990 **/
991 VOID
992 Ping6DestroyIpInstance (
993 IN PING6_PRIVATE_DATA *Private
994 )
995 {
996 EFI_STATUS Status;
997 EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb;
998
999 gBS->CloseProtocol (
1000 Private->Ip6ChildHandle,
1001 &gEfiIp6ProtocolGuid,
1002 Private->ImageHandle,
1003 Private->Ip6ChildHandle
1004 );
1005
1006 Status = gBS->HandleProtocol (
1007 Private->NicHandle,
1008 &gEfiIp6ServiceBindingProtocolGuid,
1009 (VOID **) &Ip6Sb
1010 );
1011
1012 if (!EFI_ERROR(Status)) {
1013 Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle);
1014 }
1015 }
1016
1017 /**
1018 The Ping6 Process.
1019
1020 @param[in] ImageHandle The firmware allocated handle for the UEFI image.
1021 @param[in] SendNumber The send request count.
1022 @param[in] BufferSize The send buffer size.
1023 @param[in] SrcAddress The source IPv6 address.
1024 @param[in] DstAddress The destination IPv6 address.
1025
1026 @retval SHELL_SUCCESS The ping6 processed successfullly.
1027 @retval others The ping6 processed unsuccessfully.
1028
1029 **/
1030 SHELL_STATUS
1031 ShellPing6 (
1032 IN EFI_HANDLE ImageHandle,
1033 IN UINT32 SendNumber,
1034 IN UINT32 BufferSize,
1035 IN EFI_IPv6_ADDRESS *SrcAddress,
1036 IN EFI_IPv6_ADDRESS *DstAddress
1037 )
1038 {
1039 EFI_STATUS Status;
1040 EFI_INPUT_KEY Key;
1041 PING6_PRIVATE_DATA *Private;
1042 PING6_ICMP6_TX_INFO *TxInfo;
1043 LIST_ENTRY *Entry;
1044 LIST_ENTRY *NextEntry;
1045 SHELL_STATUS ShellStatus;
1046
1047 ShellStatus = SHELL_SUCCESS;
1048 Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA));
1049
1050 if (Private == NULL) {
1051 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellNetwork2HiiHandle, L"Ping6");
1052 ShellStatus = SHELL_OUT_OF_RESOURCES;
1053 goto ON_EXIT;
1054 }
1055
1056 Private->ImageHandle = ImageHandle;
1057 Private->SendNum = SendNumber;
1058 Private->BufferSize = BufferSize;
1059 Private->RttMin = ~((UINT64 )(0x0));
1060 Private->Status = EFI_NOT_READY;
1061
1062 InitializeListHead (&Private->TxList);
1063
1064 IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress);
1065 IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress);
1066
1067 //
1068 // Open and configure a ip6 instance for ping6.
1069 //
1070 Status = Ping6CreateIpInstance (Private);
1071
1072 if (EFI_ERROR (Status)) {
1073 ShellStatus = SHELL_ACCESS_DENIED;
1074 goto ON_EXIT;
1075 }
1076 //
1077 // Print the command line itself.
1078 //
1079 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), gShellNetwork2HiiHandle, mIp6DstString, Private->BufferSize);
1080 //
1081 // Create a ipv6 token to receive the first icmp6 echo reply packet.
1082 //
1083 Status = Ping6OnReceiveEchoReply (Private);
1084
1085 if (EFI_ERROR (Status)) {
1086 ShellStatus = SHELL_ACCESS_DENIED;
1087 goto ON_EXIT;
1088 }
1089 //
1090 // Create and start timer to send icmp6 echo request packet per second.
1091 //
1092 Status = gBS->CreateEvent (
1093 EVT_TIMER | EVT_NOTIFY_SIGNAL,
1094 TPL_CALLBACK,
1095 Ping6OnTimerRoutine6,
1096 Private,
1097 &Private->Timer
1098 );
1099
1100 if (EFI_ERROR (Status)) {
1101 ShellStatus = SHELL_ACCESS_DENIED;
1102 goto ON_EXIT;
1103 }
1104
1105 //
1106 // Start a timer to calculate the RTT.
1107 //
1108 Status = Ping6InitRttTimer (Private);
1109 if (EFI_ERROR (Status)) {
1110 ShellStatus = SHELL_ACCESS_DENIED;
1111 goto ON_EXIT;
1112 }
1113
1114 //
1115 // Create a ipv6 token to send the first icmp6 echo request packet.
1116 //
1117 Status = Ping6SendEchoRequest (Private);
1118 //
1119 // EFI_NOT_READY for IPsec is enable and IKE is not established.
1120 //
1121 if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
1122 ShellStatus = SHELL_ACCESS_DENIED;
1123 if(Status == EFI_NOT_FOUND) {
1124 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), gShellNetwork2HiiHandle, mIp6DstString);
1125 }
1126
1127 goto ON_EXIT;
1128 }
1129
1130 Status = gBS->SetTimer (
1131 Private->Timer,
1132 TimerPeriodic,
1133 PING6_ONE_SECOND
1134 );
1135
1136 if (EFI_ERROR (Status)) {
1137 ShellStatus = SHELL_ACCESS_DENIED;
1138 goto ON_EXIT;
1139 }
1140 //
1141 // Control the ping6 process by two factors:
1142 // 1. Hot key
1143 // 2. Private->Status
1144 // 2.1. success means all icmp6 echo request packets get reply packets.
1145 // 2.2. timeout means the last icmp6 echo reply request timeout to get reply.
1146 // 2.3. noready means ping6 process is on-the-go.
1147 //
1148 while (Private->Status == EFI_NOT_READY) {
1149 Private->Ip6->Poll (Private->Ip6);
1150
1151 //
1152 // Terminate the ping6 process by 'esc' or 'ctl-c'.
1153 //
1154 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1155
1156 if (!EFI_ERROR(Status)) {
1157 if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) ||
1158 ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) {
1159 goto ON_STAT;
1160 }
1161 }
1162 }
1163
1164 ON_STAT:
1165 //
1166 // Display the statistics in all.
1167 //
1168 gBS->SetTimer (Private->Timer, TimerCancel, 0);
1169
1170 if (Private->TxCount != 0) {
1171 ShellPrintHiiEx (
1172 -1,
1173 -1,
1174 NULL,
1175 STRING_TOKEN (STR_PING6_STAT),
1176 gShellNetwork2HiiHandle,
1177 Private->TxCount,
1178 Private->RxCount,
1179 (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount,
1180 Private->RttSum
1181 );
1182 }
1183
1184 if (Private->RxCount != 0) {
1185 ShellPrintHiiEx (
1186 -1,
1187 -1,
1188 NULL,
1189 STRING_TOKEN (STR_PING6_RTT),
1190 gShellNetwork2HiiHandle,
1191 Private->RttMin,
1192 Private->RttMin + Private->TimerPeriod,
1193 Private->RttMax,
1194 Private->RttMax + Private->TimerPeriod,
1195 DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL),
1196 DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL) + Private->TimerPeriod
1197 );
1198 }
1199
1200 ON_EXIT:
1201
1202 if (Private != NULL) {
1203 Private->Status = EFI_ABORTED;
1204
1205 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) {
1206 TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link);
1207
1208 Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token);
1209
1210 RemoveEntryList (&TxInfo->Link);
1211 Ping6DestroyTxInfo (TxInfo);
1212 }
1213
1214 Ping6FreeRttTimer (Private);
1215
1216 if (Private->Timer != NULL) {
1217 gBS->CloseEvent (Private->Timer);
1218 }
1219
1220 if (Private->Ip6 != NULL) {
1221 Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken);
1222 }
1223
1224 if (Private->RxToken.Event != NULL) {
1225 gBS->CloseEvent (Private->RxToken.Event);
1226 }
1227
1228 if (Private->Ip6ChildHandle != NULL) {
1229 Ping6DestroyIpInstance (Private);
1230 }
1231
1232 FreePool (Private);
1233 }
1234
1235 return ShellStatus;
1236 }
1237
1238 /**
1239 Function for 'ping6' command.
1240
1241 @param[in] ImageHandle Handle to the Image (NULL if Internal).
1242 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
1243
1244 @retval SHELL_SUCCESS The ping6 processed successfullly.
1245 @retval others The ping6 processed unsuccessfully.
1246
1247 **/
1248 SHELL_STATUS
1249 EFIAPI
1250 ShellCommandRunPing6 (
1251 IN EFI_HANDLE ImageHandle,
1252 IN EFI_SYSTEM_TABLE *SystemTable
1253 )
1254 {
1255 EFI_STATUS Status;
1256 SHELL_STATUS ShellStatus;
1257 EFI_IPv6_ADDRESS DstAddress;
1258 EFI_IPv6_ADDRESS SrcAddress;
1259 UINT64 BufferSize;
1260 UINTN SendNumber;
1261 LIST_ENTRY *ParamPackage;
1262 CONST CHAR16 *ValueStr;
1263 CONST CHAR16 *ValueStrPtr;
1264 UINTN NonOptionCount;
1265 CHAR16 *ProblemParam;
1266
1267 ProblemParam = NULL;
1268 ShellStatus = SHELL_SUCCESS;
1269
1270 Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, &ProblemParam, TRUE, FALSE);
1271 if (EFI_ERROR(Status)) {
1272 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);
1273 ShellStatus = SHELL_INVALID_PARAMETER;
1274 goto ON_EXIT;
1275 }
1276
1277 SendNumber = 10;
1278 BufferSize = 16;
1279
1280 //
1281 // Parse the parameter of count number.
1282 //
1283 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n");
1284 ValueStrPtr = ValueStr;
1285 if (ValueStr != NULL) {
1286 SendNumber = ShellStrToUintn (ValueStrPtr);
1287
1288 //
1289 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1290 //
1291 if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) {
1292 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), gShellNetwork2HiiHandle, ValueStr);
1293 ShellStatus = SHELL_INVALID_PARAMETER;
1294 goto ON_EXIT;
1295 }
1296 }
1297 //
1298 // Parse the parameter of buffer size.
1299 //
1300 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l");
1301 ValueStrPtr = ValueStr;
1302 if (ValueStr != NULL) {
1303 BufferSize = ShellStrToUintn (ValueStrPtr);
1304
1305 //
1306 // ShellStrToUintn will return 0 when input is 0 or an invalid input string.
1307 //
1308 if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) {
1309 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), gShellNetwork2HiiHandle, ValueStr);
1310 ShellStatus = SHELL_INVALID_PARAMETER;
1311 goto ON_EXIT;
1312 }
1313 }
1314
1315 ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS));
1316 ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS));
1317
1318 //
1319 // Parse the parameter of source ip address.
1320 //
1321 ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s");
1322 ValueStrPtr = ValueStr;
1323 if (ValueStr != NULL) {
1324 mIp6SrcString = ValueStr;
1325 Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress);
1326 if (EFI_ERROR (Status)) {
1327 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);
1328 ShellStatus = SHELL_INVALID_PARAMETER;
1329 goto ON_EXIT;
1330 }
1331 }
1332 //
1333 // Parse the parameter of destination ip address.
1334 //
1335 NonOptionCount = ShellCommandLineGetCount(ParamPackage);
1336 ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1));
1337 if (NonOptionCount != 2) {
1338 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), gShellNetwork2HiiHandle);
1339 ShellStatus = SHELL_INVALID_PARAMETER;
1340 goto ON_EXIT;
1341 }
1342 ValueStrPtr = ValueStr;
1343 if (ValueStr != NULL) {
1344 mIp6DstString = ValueStr;
1345 Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress);
1346 if (EFI_ERROR (Status)) {
1347 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), gShellNetwork2HiiHandle, ValueStr);
1348 ShellStatus = SHELL_INVALID_PARAMETER;
1349 goto ON_EXIT;
1350 }
1351 }
1352
1353 //
1354 // Enter into ping6 process.
1355 //
1356 ShellStatus = ShellPing6 (
1357 ImageHandle,
1358 (UINT32)SendNumber,
1359 (UINT32)BufferSize,
1360 &SrcAddress,
1361 &DstAddress
1362 );
1363
1364 ON_EXIT:
1365 ShellCommandLineFreeVarList (ParamPackage);
1366 return ShellStatus;
1367 }
1368