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