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 1369