1 /** @file 2 Multicast Listener Discovery support routines. 3 4 Copyright (c) 2009 - 2010, 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 "Ip6Impl.h" 17 18 /** 19 Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data. 20 21 @param[in, out] IpSb Points to IP6 service binding instance. 22 @param[in] MulticastAddr The IPv6 multicast address to be recorded. 23 @param[in] DelayTimer The maximum allowed delay before sending a responding 24 report, in units of milliseconds. 25 @return The created IP6_ML_GROUP list entry or NULL. 26 27 **/ 28 IP6_MLD_GROUP * 29 Ip6CreateMldEntry ( 30 IN OUT IP6_SERVICE *IpSb, 31 IN EFI_IPv6_ADDRESS *MulticastAddr, 32 IN UINT32 DelayTimer 33 ) 34 { 35 IP6_MLD_GROUP *Entry; 36 37 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); 38 ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); 39 40 Entry = AllocatePool (sizeof (IP6_MLD_GROUP)); 41 if (Entry != NULL) { 42 Entry->RefCnt = 1; 43 Entry->DelayTimer = DelayTimer; 44 Entry->SendByUs = FALSE; 45 IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr); 46 InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link); 47 } 48 49 return Entry; 50 } 51 52 /** 53 Search a IP6_MLD_GROUP list entry node from a list array. 54 55 @param[in] IpSb Points to IP6 service binding instance. 56 @param[in] MulticastAddr The IPv6 multicast address to be searched. 57 58 @return The found IP6_ML_GROUP list entry or NULL. 59 60 **/ 61 IP6_MLD_GROUP * 62 Ip6FindMldEntry ( 63 IN IP6_SERVICE *IpSb, 64 IN EFI_IPv6_ADDRESS *MulticastAddr 65 ) 66 { 67 LIST_ENTRY *Entry; 68 IP6_MLD_GROUP *Group; 69 70 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); 71 ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); 72 73 NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { 74 Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); 75 if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) { 76 return Group; 77 } 78 } 79 80 return NULL; 81 } 82 83 /** 84 Count the number of IP6 multicast groups that are mapped to the 85 same MAC address. Several IP6 multicast address may be mapped to 86 the same MAC address. 87 88 @param[in] MldCtrl The MLD control block to search in. 89 @param[in] Mac The MAC address to search. 90 91 @return The number of the IP6 multicast group that mapped to the same 92 multicast group Mac. 93 94 **/ 95 INTN 96 Ip6FindMac ( 97 IN IP6_MLD_SERVICE_DATA *MldCtrl, 98 IN EFI_MAC_ADDRESS *Mac 99 ) 100 { 101 LIST_ENTRY *Entry; 102 IP6_MLD_GROUP *Group; 103 INTN Count; 104 105 Count = 0; 106 107 NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) { 108 Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); 109 110 if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) { 111 Count++; 112 } 113 } 114 115 return Count; 116 } 117 118 /** 119 Generate MLD report message and send it out to MulticastAddr. 120 121 @param[in] IpSb The IP service to send the packet. 122 @param[in] Interface The IP interface to send the packet. 123 If NULL, a system interface will be selected. 124 @param[in] MulticastAddr The specific IPv6 multicast address to which 125 the message sender is listening. 126 127 @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the 128 operation. 129 @retval EFI_SUCCESS The MLD report message was successfully sent out. 130 131 **/ 132 EFI_STATUS 133 Ip6SendMldReport ( 134 IN IP6_SERVICE *IpSb, 135 IN IP6_INTERFACE *Interface OPTIONAL, 136 IN EFI_IPv6_ADDRESS *MulticastAddr 137 ) 138 { 139 IP6_MLD_HEAD *MldHead; 140 NET_BUF *Packet; 141 EFI_IP6_HEADER Head; 142 UINT16 PayloadLen; 143 UINTN OptionLen; 144 UINT8 *Options; 145 EFI_STATUS Status; 146 UINT16 HeadChecksum; 147 UINT16 PseudoChecksum; 148 149 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); 150 ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); 151 152 // 153 // Generate the packet to be sent 154 // IPv6 basic header + Hop by Hop option + MLD message 155 // 156 157 OptionLen = 0; 158 Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP); 159 ASSERT (Status == EFI_BUFFER_TOO_SMALL); 160 161 PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD)); 162 Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); 163 if (Packet == NULL) { 164 return EFI_OUT_OF_RESOURCES; 165 } 166 167 // 168 // Create the basic IPv6 header. 169 // RFC3590: Use link-local address as source address if it is available, 170 // otherwise use the unspecified address. 171 // 172 Head.FlowLabelL = 0; 173 Head.FlowLabelH = 0; 174 Head.PayloadLength = HTONS (PayloadLen); 175 Head.NextHeader = IP6_HOP_BY_HOP; 176 Head.HopLimit = 1; 177 IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr); 178 179 // 180 // If Link-Local address is not ready, we use unspecified address. 181 // 182 IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr); 183 184 NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); 185 186 // 187 // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header 188 // 189 Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE); 190 ASSERT (Options != NULL); 191 Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP); 192 if (EFI_ERROR (Status)) { 193 NetbufFree (Packet); 194 Packet = NULL; 195 return Status; 196 } 197 198 // 199 // Fill in MLD message - Report 200 // 201 MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE); 202 ASSERT (MldHead != NULL); 203 ZeroMem (MldHead, sizeof (IP6_MLD_HEAD)); 204 MldHead->Head.Type = ICMP_V6_LISTENER_REPORT; 205 MldHead->Head.Code = 0; 206 IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr); 207 208 HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD)); 209 PseudoChecksum = NetIp6PseudoHeadChecksum ( 210 &Head.SourceAddress, 211 &Head.DestinationAddress, 212 IP6_ICMP, 213 sizeof (IP6_MLD_HEAD) 214 ); 215 216 MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum); 217 218 // 219 // Transmit the packet 220 // 221 return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); 222 } 223 224 /** 225 Generate MLD Done message and send it out to MulticastAddr. 226 227 @param[in] IpSb The IP service to send the packet. 228 @param[in] MulticastAddr The specific IPv6 multicast address to which 229 the message sender is ceasing to listen. 230 231 @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the 232 operation. 233 @retval EFI_SUCCESS The MLD report message was successfully sent out. 234 235 **/ 236 EFI_STATUS 237 Ip6SendMldDone ( 238 IN IP6_SERVICE *IpSb, 239 IN EFI_IPv6_ADDRESS *MulticastAddr 240 ) 241 { 242 IP6_MLD_HEAD *MldHead; 243 NET_BUF *Packet; 244 EFI_IP6_HEADER Head; 245 UINT16 PayloadLen; 246 UINTN OptionLen; 247 UINT8 *Options; 248 EFI_STATUS Status; 249 EFI_IPv6_ADDRESS Destination; 250 UINT16 HeadChecksum; 251 UINT16 PseudoChecksum; 252 253 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); 254 ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); 255 256 // 257 // Generate the packet to be sent 258 // IPv6 basic header + Hop by Hop option + MLD message 259 // 260 261 OptionLen = 0; 262 Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP); 263 ASSERT (Status == EFI_BUFFER_TOO_SMALL); 264 265 PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD)); 266 Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); 267 if (Packet == NULL) { 268 return EFI_OUT_OF_RESOURCES; 269 } 270 271 // 272 // Create the basic IPv6 header. 273 // 274 Head.FlowLabelL = 0; 275 Head.FlowLabelH = 0; 276 Head.PayloadLength = HTONS (PayloadLen); 277 Head.NextHeader = IP6_HOP_BY_HOP; 278 Head.HopLimit = 1; 279 280 // 281 // If Link-Local address is not ready, we use unspecified address. 282 // 283 IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr); 284 285 Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination); 286 IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination); 287 288 NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); 289 290 // 291 // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header 292 // 293 Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE); 294 ASSERT (Options != NULL); 295 Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP); 296 if (EFI_ERROR (Status)) { 297 NetbufFree (Packet); 298 Packet = NULL; 299 return Status; 300 } 301 302 // 303 // Fill in MLD message - Done 304 // 305 MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE); 306 ASSERT (MldHead != NULL); 307 ZeroMem (MldHead, sizeof (IP6_MLD_HEAD)); 308 MldHead->Head.Type = ICMP_V6_LISTENER_DONE; 309 MldHead->Head.Code = 0; 310 IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr); 311 312 HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD)); 313 PseudoChecksum = NetIp6PseudoHeadChecksum ( 314 &Head.SourceAddress, 315 &Head.DestinationAddress, 316 IP6_ICMP, 317 sizeof (IP6_MLD_HEAD) 318 ); 319 320 MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum); 321 322 // 323 // Transmit the packet 324 // 325 return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); 326 } 327 328 /** 329 Init the MLD data of the IP6 service instance. Configure 330 MNP to receive ALL SYSTEM multicast. 331 332 @param[in] IpSb The IP6 service whose MLD is to be initialized. 333 334 @retval EFI_OUT_OF_RESOURCES There are not sufficient resourcet to complete the 335 operation. 336 @retval EFI_SUCCESS The MLD module successfully initialized. 337 338 **/ 339 EFI_STATUS 340 Ip6InitMld ( 341 IN IP6_SERVICE *IpSb 342 ) 343 { 344 EFI_IPv6_ADDRESS AllNodes; 345 IP6_MLD_GROUP *Group; 346 EFI_STATUS Status; 347 348 // 349 // Join the link-scope all-nodes multicast address (FF02::1). 350 // This address is started in Idle Listener state and never transitions to 351 // another state, and never sends a Report or Done for that address. 352 // 353 354 Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); 355 356 Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME); 357 if (Group == NULL) { 358 return EFI_OUT_OF_RESOURCES; 359 } 360 361 Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac); 362 if (EFI_ERROR (Status)) { 363 goto ERROR; 364 } 365 366 // 367 // Configure MNP to receive all-nodes multicast 368 // 369 Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac); 370 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { 371 goto ERROR; 372 } 373 374 return EFI_SUCCESS; 375 376 ERROR: 377 RemoveEntryList (&Group->Link); 378 FreePool (Group); 379 return Status; 380 } 381 382 /** 383 Add a group address to the array of group addresses. 384 The caller should make sure that no duplicated address 385 existed in the array. 386 387 @param[in, out] IpInstance Points to an IP6_PROTOCOL instance. 388 @param[in] Group The IP6 multicast address to add. 389 390 @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete 391 the operation. 392 @retval EFI_SUCESS The address is added to the group address array. 393 394 **/ 395 EFI_STATUS 396 Ip6CombineGroups ( 397 IN OUT IP6_PROTOCOL *IpInstance, 398 IN EFI_IPv6_ADDRESS *Group 399 ) 400 { 401 EFI_IPv6_ADDRESS *GroupList; 402 403 NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); 404 ASSERT (Group != NULL && IP6_IS_MULTICAST (Group)); 405 406 IpInstance->GroupCount++; 407 408 GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS)); 409 if (GroupList == NULL) { 410 return EFI_OUT_OF_RESOURCES; 411 } 412 413 if (IpInstance->GroupCount > 1) { 414 ASSERT (IpInstance->GroupList != NULL); 415 416 CopyMem ( 417 GroupList, 418 IpInstance->GroupList, 419 (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS) 420 ); 421 422 FreePool (IpInstance->GroupList); 423 } 424 425 IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group); 426 427 IpInstance->GroupList = GroupList; 428 429 return EFI_SUCCESS; 430 } 431 432 /** 433 Remove a group address from the array of group addresses. 434 Although the function doesn't assume the byte order of Group, 435 the network byte order is used by the caller. 436 437 @param[in, out] IpInstance Points to an IP6_PROTOCOL instance. 438 @param[in] Group The IP6 multicast address to remove. 439 440 @retval EFI_NOT_FOUND Cannot find the to be removed group address. 441 @retval EFI_SUCCESS The group address was successfully removed. 442 443 **/ 444 EFI_STATUS 445 Ip6RemoveGroup ( 446 IN OUT IP6_PROTOCOL *IpInstance, 447 IN EFI_IPv6_ADDRESS *Group 448 ) 449 { 450 UINT32 Index; 451 UINT32 Count; 452 453 Count = IpInstance->GroupCount; 454 455 for (Index = 0; Index < Count; Index++) { 456 if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) { 457 break; 458 } 459 } 460 461 if (Index == Count) { 462 return EFI_NOT_FOUND; 463 } 464 465 while (Index < Count - 1) { 466 IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1); 467 Index++; 468 } 469 470 ASSERT (IpInstance->GroupCount > 0); 471 IpInstance->GroupCount--; 472 473 return EFI_SUCCESS; 474 } 475 476 /** 477 Join the multicast group on behalf of this IP6 service binding instance. 478 479 @param[in] IpSb The IP6 service binding instance. 480 @param[in] Interface Points to an IP6_INTERFACE structure. 481 @param[in] Address The group address to join. 482 483 @retval EFI_SUCCESS Successfully join the multicast group. 484 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. 485 @retval Others Failed to join the multicast group. 486 487 **/ 488 EFI_STATUS 489 Ip6JoinGroup ( 490 IN IP6_SERVICE *IpSb, 491 IN IP6_INTERFACE *Interface, 492 IN EFI_IPv6_ADDRESS *Address 493 ) 494 { 495 IP6_MLD_GROUP *Group; 496 EFI_STATUS Status; 497 498 Group = Ip6FindMldEntry (IpSb, Address); 499 if (Group != NULL) { 500 Group->RefCnt++; 501 return EFI_SUCCESS; 502 } 503 504 // 505 // Repeat the report once or twcie after short delays [Unsolicited Report Interval] (default:10s) 506 // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss. 507 // 508 Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL); 509 if (Group == NULL) { 510 return EFI_OUT_OF_RESOURCES; 511 } 512 513 Group->SendByUs = TRUE; 514 515 Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac); 516 if (EFI_ERROR (Status)) { 517 return Status; 518 } 519 520 Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac); 521 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { 522 goto ERROR; 523 } 524 525 // 526 // Send unsolicited report when a node starts listening to a multicast address 527 // 528 Status = Ip6SendMldReport (IpSb, Interface, Address); 529 if (EFI_ERROR (Status)) { 530 goto ERROR; 531 } 532 533 return EFI_SUCCESS; 534 535 ERROR: 536 RemoveEntryList (&Group->Link); 537 FreePool (Group); 538 return Status; 539 } 540 541 /** 542 Leave the IP6 multicast group. 543 544 @param[in] IpSb The IP6 service binding instance. 545 @param[in] Address The group address to leave. 546 547 @retval EFI_NOT_FOUND The IP6 service instance isn't in the group. 548 @retval EFI_SUCCESS Successfully leave the multicast group.. 549 @retval Others Failed to leave the multicast group. 550 551 **/ 552 EFI_STATUS 553 Ip6LeaveGroup ( 554 IN IP6_SERVICE *IpSb, 555 IN EFI_IPv6_ADDRESS *Address 556 ) 557 { 558 IP6_MLD_GROUP *Group; 559 EFI_STATUS Status; 560 561 Group = Ip6FindMldEntry (IpSb, Address); 562 if (Group == NULL) { 563 return EFI_NOT_FOUND; 564 } 565 566 // 567 // If more than one instance is in the group, decrease 568 // the RefCnt then return. 569 // 570 if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) { 571 return EFI_SUCCESS; 572 } 573 574 // 575 // If multiple IP6 group addresses are mapped to the same 576 // multicast MAC address, don't configure the MNP to leave 577 // the MAC. 578 // 579 if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) { 580 Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac); 581 if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { 582 return Status; 583 } 584 } 585 586 // 587 // Send a leave report if we are the last node to report 588 // 589 if (Group->SendByUs) { 590 Status = Ip6SendMldDone (IpSb, Address); 591 if (EFI_ERROR (Status)) { 592 return Status; 593 } 594 } 595 596 RemoveEntryList (&Group->Link); 597 FreePool (Group); 598 599 return EFI_SUCCESS; 600 } 601 602 /** 603 Worker function for EfiIp6Groups(). The caller 604 should make sure that the parameters are valid. 605 606 @param[in] IpInstance The IP6 child to change the setting. 607 @param[in] JoinFlag TRUE to join the group, otherwise leave it. 608 @param[in] GroupAddress The target group address. If NULL, leave all 609 the group addresses. 610 611 @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it 612 @retval EFI_OUT_OF_RESOURCES Failed to allocate sufficient resources. 613 @retval EFI_DEVICE_ERROR Failed to set the group configuraton. 614 @retval EFI_SUCCESS Successfully updated the group setting. 615 @retval EFI_NOT_FOUND Try to leave the group which it isn't a member. 616 617 **/ 618 EFI_STATUS 619 Ip6Groups ( 620 IN IP6_PROTOCOL *IpInstance, 621 IN BOOLEAN JoinFlag, 622 IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL 623 ) 624 { 625 EFI_STATUS Status; 626 IP6_SERVICE *IpSb; 627 UINT32 Index; 628 EFI_IPv6_ADDRESS *Group; 629 630 IpSb = IpInstance->Service; 631 632 if (JoinFlag) { 633 ASSERT (GroupAddress != NULL); 634 635 for (Index = 0; Index < IpInstance->GroupCount; Index++) { 636 if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) { 637 return EFI_ALREADY_STARTED; 638 } 639 } 640 641 Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress); 642 if (!EFI_ERROR (Status)) { 643 return Ip6CombineGroups (IpInstance, GroupAddress); 644 } 645 646 return Status; 647 } 648 649 // 650 // Leave the group. Leave all the groups if GroupAddress is NULL. 651 // 652 for (Index = IpInstance->GroupCount; Index > 0; Index--) { 653 Group = IpInstance->GroupList + (Index - 1); 654 655 if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) { 656 Status = Ip6LeaveGroup (IpInstance->Service, Group); 657 if (EFI_ERROR (Status)) { 658 return Status; 659 } 660 661 Ip6RemoveGroup (IpInstance, Group); 662 663 if (IpInstance->GroupCount == 0) { 664 ASSERT (Index == 1); 665 FreePool (IpInstance->GroupList); 666 IpInstance->GroupList = NULL; 667 } 668 669 if (GroupAddress != NULL) { 670 return EFI_SUCCESS; 671 } 672 } 673 } 674 675 return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS); 676 } 677 678 /** 679 Set a random value of the delay timer for the multicast address from the range 680 [0, Maximum Response Delay]. If a timer for any address is already 681 running, it is reset to the new random value only if the requested 682 Maximum Response Delay is less than the remaining value of the 683 running timer. If the Query packet specifies a Maximum Response 684 Delay of zero, each timer is effectively set to zero, and the action 685 specified below for timer expiration is performed immediately. 686 687 @param[in] IpSb The IP6 service binding instance. 688 @param[in] MaxRespDelay The Maximum Response Delay, in milliseconds. 689 @param[in] MulticastAddr The multicast address. 690 @param[in, out] Group Points to a IP6_MLD_GROUP list entry node. 691 692 @retval EFI_SUCCESS The delay timer is successfully updated or 693 timer expiration is performed immediately. 694 @retval Others Failed to send out MLD report message. 695 696 **/ 697 EFI_STATUS 698 Ip6UpdateDelayTimer ( 699 IN IP6_SERVICE *IpSb, 700 IN UINT16 MaxRespDelay, 701 IN EFI_IPv6_ADDRESS *MulticastAddr, 702 IN OUT IP6_MLD_GROUP *Group 703 ) 704 { 705 UINT32 Delay; 706 707 // 708 // If the Query packet specifies a Maximum Response Delay of zero, perform timer 709 // expiration immediately. 710 // 711 if (MaxRespDelay == 0) { 712 Group->DelayTimer = 0; 713 return Ip6SendMldReport (IpSb, NULL, MulticastAddr); 714 } 715 716 Delay = (UINT32) (MaxRespDelay / 1000); 717 718 // 719 // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay] 720 // If a timer is already running, resets it if the request Maximum Response Delay 721 // is less than the remaining value of the running timer. 722 // 723 if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) { 724 Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ()); 725 } 726 727 return EFI_SUCCESS; 728 } 729 730 /** 731 Process the Multicast Listener Query message. 732 733 @param[in] IpSb The IP service that received the packet. 734 @param[in] Head The IP head of the MLD query packet. 735 @param[in] Packet The content of the MLD query packet with IP head 736 removed. 737 738 @retval EFI_SUCCESS The MLD query packet processed successfully. 739 @retval EFI_INVALID_PARAMETER The packet is invalid. 740 @retval Others Failed to process the packet. 741 742 **/ 743 EFI_STATUS 744 Ip6ProcessMldQuery ( 745 IN IP6_SERVICE *IpSb, 746 IN EFI_IP6_HEADER *Head, 747 IN NET_BUF *Packet 748 ) 749 { 750 EFI_IPv6_ADDRESS AllNodes; 751 IP6_MLD_GROUP *Group; 752 IP6_MLD_HEAD MldPacket; 753 LIST_ENTRY *Entry; 754 EFI_STATUS Status; 755 756 Status = EFI_INVALID_PARAMETER; 757 758 // 759 // Check the validity of the packet, generic query or specific query 760 // 761 if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { 762 goto Exit; 763 } 764 765 if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) { 766 goto Exit; 767 } 768 769 // 770 // The Packet points to MLD report raw data without Hop-By-Hop option. 771 // 772 NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket); 773 MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay); 774 775 Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); 776 if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) { 777 // 778 // Receives a Multicast-Address-Specific Query, check it firstly 779 // 780 if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) { 781 goto Exit; 782 } 783 // 784 // The node is not listening but it receives the specific query. Just return. 785 // 786 Group = Ip6FindMldEntry (IpSb, &MldPacket.Group); 787 if (Group == NULL) { 788 Status = EFI_SUCCESS; 789 goto Exit; 790 } 791 792 Status = Ip6UpdateDelayTimer ( 793 IpSb, 794 MldPacket.MaxRespDelay, 795 &MldPacket.Group, 796 Group 797 ); 798 goto Exit; 799 } 800 801 // 802 // Receives a General Query, sets a delay timer for each multicast address it is listening 803 // 804 NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { 805 Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); 806 Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group); 807 if (EFI_ERROR (Status)) { 808 goto Exit; 809 } 810 } 811 812 Status = EFI_SUCCESS; 813 814 Exit: 815 NetbufFree (Packet); 816 return Status; 817 } 818 819 /** 820 Process the Multicast Listener Report message. 821 822 @param[in] IpSb The IP service that received the packet. 823 @param[in] Head The IP head of the MLD report packet. 824 @param[in] Packet The content of the MLD report packet with IP head 825 removed. 826 827 @retval EFI_SUCCESS The MLD report packet processed successfully. 828 @retval EFI_INVALID_PARAMETER The packet is invalid. 829 830 **/ 831 EFI_STATUS 832 Ip6ProcessMldReport ( 833 IN IP6_SERVICE *IpSb, 834 IN EFI_IP6_HEADER *Head, 835 IN NET_BUF *Packet 836 ) 837 { 838 IP6_MLD_HEAD MldPacket; 839 IP6_MLD_GROUP *Group; 840 EFI_STATUS Status; 841 842 Status = EFI_INVALID_PARAMETER; 843 844 // 845 // Validate the incoming message, if invalid, drop it. 846 // 847 if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { 848 goto Exit; 849 } 850 851 if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) { 852 goto Exit; 853 } 854 855 // 856 // The Packet points to MLD report raw data without Hop-By-Hop option. 857 // 858 NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket); 859 if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) { 860 goto Exit; 861 } 862 863 Group = Ip6FindMldEntry (IpSb, &MldPacket.Group); 864 if (Group == NULL) { 865 goto Exit; 866 } 867 868 // 869 // The report is sent by another node, stop its own timer relates to the multicast address and clear 870 // 871 872 if (!Group->SendByUs) { 873 Group->DelayTimer = 0; 874 } 875 876 Status = EFI_SUCCESS; 877 878 Exit: 879 NetbufFree (Packet); 880 return Status; 881 } 882 883 /** 884 The heartbeat timer of MLD module. It sends out a solicited MLD report when 885 DelayTimer expires. 886 887 @param[in] IpSb The IP6 service binding instance. 888 889 **/ 890 VOID 891 Ip6MldTimerTicking ( 892 IN IP6_SERVICE *IpSb 893 ) 894 { 895 IP6_MLD_GROUP *Group; 896 LIST_ENTRY *Entry; 897 898 // 899 // Send solicited report when timer expires 900 // 901 NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { 902 Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); 903 if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) { 904 Ip6SendMldReport (IpSb, NULL, &Group->Address); 905 } 906 } 907 } 908 909