123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902 |
- /** @file
- Multicast Listener Discovery support routines.
- Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include "Ip6Impl.h"
- /**
- Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data.
- @param[in, out] IpSb Points to IP6 service binding instance.
- @param[in] MulticastAddr The IPv6 multicast address to be recorded.
- @param[in] DelayTimer The maximum allowed delay before sending a responding
- report, in units of milliseconds.
- @return The created IP6_ML_GROUP list entry or NULL.
- **/
- IP6_MLD_GROUP *
- Ip6CreateMldEntry (
- IN OUT IP6_SERVICE *IpSb,
- IN EFI_IPv6_ADDRESS *MulticastAddr,
- IN UINT32 DelayTimer
- )
- {
- IP6_MLD_GROUP *Entry;
- NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
- ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));
- Entry = AllocatePool (sizeof (IP6_MLD_GROUP));
- if (Entry != NULL) {
- Entry->RefCnt = 1;
- Entry->DelayTimer = DelayTimer;
- Entry->SendByUs = FALSE;
- IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr);
- InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link);
- }
- return Entry;
- }
- /**
- Search a IP6_MLD_GROUP list entry node from a list array.
- @param[in] IpSb Points to IP6 service binding instance.
- @param[in] MulticastAddr The IPv6 multicast address to be searched.
- @return The found IP6_ML_GROUP list entry or NULL.
- **/
- IP6_MLD_GROUP *
- Ip6FindMldEntry (
- IN IP6_SERVICE *IpSb,
- IN EFI_IPv6_ADDRESS *MulticastAddr
- )
- {
- LIST_ENTRY *Entry;
- IP6_MLD_GROUP *Group;
- NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
- ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));
- NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {
- Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);
- if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) {
- return Group;
- }
- }
- return NULL;
- }
- /**
- Count the number of IP6 multicast groups that are mapped to the
- same MAC address. Several IP6 multicast address may be mapped to
- the same MAC address.
- @param[in] MldCtrl The MLD control block to search in.
- @param[in] Mac The MAC address to search.
- @return The number of the IP6 multicast group that mapped to the same
- multicast group Mac.
- **/
- INTN
- Ip6FindMac (
- IN IP6_MLD_SERVICE_DATA *MldCtrl,
- IN EFI_MAC_ADDRESS *Mac
- )
- {
- LIST_ENTRY *Entry;
- IP6_MLD_GROUP *Group;
- INTN Count;
- Count = 0;
- NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) {
- Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);
- if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {
- Count++;
- }
- }
- return Count;
- }
- /**
- Generate MLD report message and send it out to MulticastAddr.
- @param[in] IpSb The IP service to send the packet.
- @param[in] Interface The IP interface to send the packet.
- If NULL, a system interface will be selected.
- @param[in] MulticastAddr The specific IPv6 multicast address to which
- the message sender is listening.
- @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the
- operation.
- @retval EFI_SUCCESS The MLD report message was successfully sent out.
- **/
- EFI_STATUS
- Ip6SendMldReport (
- IN IP6_SERVICE *IpSb,
- IN IP6_INTERFACE *Interface OPTIONAL,
- IN EFI_IPv6_ADDRESS *MulticastAddr
- )
- {
- IP6_MLD_HEAD *MldHead;
- NET_BUF *Packet;
- EFI_IP6_HEADER Head;
- UINT16 PayloadLen;
- UINTN OptionLen;
- UINT8 *Options;
- EFI_STATUS Status;
- UINT16 HeadChecksum;
- UINT16 PseudoChecksum;
- NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
- ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));
- //
- // Generate the packet to be sent
- // IPv6 basic header + Hop by Hop option + MLD message
- //
- OptionLen = 0;
- Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);
- ASSERT (Status == EFI_BUFFER_TOO_SMALL);
- PayloadLen = (UINT16)(OptionLen + sizeof (IP6_MLD_HEAD));
- Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32)PayloadLen);
- if (Packet == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- //
- // Create the basic IPv6 header.
- // RFC3590: Use link-local address as source address if it is available,
- // otherwise use the unspecified address.
- //
- Head.FlowLabelL = 0;
- Head.FlowLabelH = 0;
- Head.PayloadLength = HTONS (PayloadLen);
- Head.NextHeader = IP6_HOP_BY_HOP;
- Head.HopLimit = 1;
- IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr);
- //
- // If Link-Local address is not ready, we use unspecified address.
- //
- IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);
- NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
- //
- // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header
- //
- Options = NetbufAllocSpace (Packet, (UINT32)OptionLen, FALSE);
- ASSERT (Options != NULL);
- Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);
- if (EFI_ERROR (Status)) {
- NetbufFree (Packet);
- Packet = NULL;
- return Status;
- }
- //
- // Fill in MLD message - Report
- //
- MldHead = (IP6_MLD_HEAD *)NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);
- ASSERT (MldHead != NULL);
- ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));
- MldHead->Head.Type = ICMP_V6_LISTENER_REPORT;
- MldHead->Head.Code = 0;
- IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);
- HeadChecksum = NetblockChecksum ((UINT8 *)MldHead, sizeof (IP6_MLD_HEAD));
- PseudoChecksum = NetIp6PseudoHeadChecksum (
- &Head.SourceAddress,
- &Head.DestinationAddress,
- IP6_ICMP,
- sizeof (IP6_MLD_HEAD)
- );
- MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);
- //
- // Transmit the packet
- //
- return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
- }
- /**
- Generate MLD Done message and send it out to MulticastAddr.
- @param[in] IpSb The IP service to send the packet.
- @param[in] MulticastAddr The specific IPv6 multicast address to which
- the message sender is ceasing to listen.
- @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the
- operation.
- @retval EFI_SUCCESS The MLD report message was successfully sent out.
- **/
- EFI_STATUS
- Ip6SendMldDone (
- IN IP6_SERVICE *IpSb,
- IN EFI_IPv6_ADDRESS *MulticastAddr
- )
- {
- IP6_MLD_HEAD *MldHead;
- NET_BUF *Packet;
- EFI_IP6_HEADER Head;
- UINT16 PayloadLen;
- UINTN OptionLen;
- UINT8 *Options;
- EFI_STATUS Status;
- EFI_IPv6_ADDRESS Destination;
- UINT16 HeadChecksum;
- UINT16 PseudoChecksum;
- NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
- ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));
- //
- // Generate the packet to be sent
- // IPv6 basic header + Hop by Hop option + MLD message
- //
- OptionLen = 0;
- Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);
- ASSERT (Status == EFI_BUFFER_TOO_SMALL);
- PayloadLen = (UINT16)(OptionLen + sizeof (IP6_MLD_HEAD));
- Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32)PayloadLen);
- if (Packet == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- //
- // Create the basic IPv6 header.
- //
- Head.FlowLabelL = 0;
- Head.FlowLabelH = 0;
- Head.PayloadLength = HTONS (PayloadLen);
- Head.NextHeader = IP6_HOP_BY_HOP;
- Head.HopLimit = 1;
- //
- // If Link-Local address is not ready, we use unspecified address.
- //
- IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);
- Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination);
- IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination);
- NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
- //
- // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header
- //
- Options = NetbufAllocSpace (Packet, (UINT32)OptionLen, FALSE);
- ASSERT (Options != NULL);
- Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);
- if (EFI_ERROR (Status)) {
- NetbufFree (Packet);
- Packet = NULL;
- return Status;
- }
- //
- // Fill in MLD message - Done
- //
- MldHead = (IP6_MLD_HEAD *)NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);
- ASSERT (MldHead != NULL);
- ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));
- MldHead->Head.Type = ICMP_V6_LISTENER_DONE;
- MldHead->Head.Code = 0;
- IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);
- HeadChecksum = NetblockChecksum ((UINT8 *)MldHead, sizeof (IP6_MLD_HEAD));
- PseudoChecksum = NetIp6PseudoHeadChecksum (
- &Head.SourceAddress,
- &Head.DestinationAddress,
- IP6_ICMP,
- sizeof (IP6_MLD_HEAD)
- );
- MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);
- //
- // Transmit the packet
- //
- return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
- }
- /**
- Init the MLD data of the IP6 service instance. Configure
- MNP to receive ALL SYSTEM multicast.
- @param[in] IpSb The IP6 service whose MLD is to be initialized.
- @retval EFI_OUT_OF_RESOURCES There are not sufficient resourcet to complete the
- operation.
- @retval EFI_SUCCESS The MLD module successfully initialized.
- **/
- EFI_STATUS
- Ip6InitMld (
- IN IP6_SERVICE *IpSb
- )
- {
- EFI_IPv6_ADDRESS AllNodes;
- IP6_MLD_GROUP *Group;
- EFI_STATUS Status;
- //
- // Join the link-scope all-nodes multicast address (FF02::1).
- // This address is started in Idle Listener state and never transitions to
- // another state, and never sends a Report or Done for that address.
- //
- Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);
- Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32)IP6_INFINIT_LIFETIME);
- if (Group == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac);
- if (EFI_ERROR (Status)) {
- goto ERROR;
- }
- //
- // Configure MNP to receive all-nodes multicast
- //
- Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);
- if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
- goto ERROR;
- }
- return EFI_SUCCESS;
- ERROR:
- RemoveEntryList (&Group->Link);
- FreePool (Group);
- return Status;
- }
- /**
- Add a group address to the array of group addresses.
- The caller should make sure that no duplicated address
- existed in the array.
- @param[in, out] IpInstance Points to an IP6_PROTOCOL instance.
- @param[in] Group The IP6 multicast address to add.
- @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete
- the operation.
- @retval EFI_SUCCESS The address is added to the group address array.
- **/
- EFI_STATUS
- Ip6CombineGroups (
- IN OUT IP6_PROTOCOL *IpInstance,
- IN EFI_IPv6_ADDRESS *Group
- )
- {
- EFI_IPv6_ADDRESS *GroupList;
- NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
- ASSERT (Group != NULL && IP6_IS_MULTICAST (Group));
- IpInstance->GroupCount++;
- GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS));
- if (GroupList == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- if (IpInstance->GroupCount > 1) {
- ASSERT (IpInstance->GroupList != NULL);
- CopyMem (
- GroupList,
- IpInstance->GroupList,
- (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS)
- );
- FreePool (IpInstance->GroupList);
- }
- IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group);
- IpInstance->GroupList = GroupList;
- return EFI_SUCCESS;
- }
- /**
- Remove a group address from the array of group addresses.
- Although the function doesn't assume the byte order of Group,
- the network byte order is used by the caller.
- @param[in, out] IpInstance Points to an IP6_PROTOCOL instance.
- @param[in] Group The IP6 multicast address to remove.
- @retval EFI_NOT_FOUND Cannot find the to be removed group address.
- @retval EFI_SUCCESS The group address was successfully removed.
- **/
- EFI_STATUS
- Ip6RemoveGroup (
- IN OUT IP6_PROTOCOL *IpInstance,
- IN EFI_IPv6_ADDRESS *Group
- )
- {
- UINT32 Index;
- UINT32 Count;
- Count = IpInstance->GroupCount;
- for (Index = 0; Index < Count; Index++) {
- if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) {
- break;
- }
- }
- if (Index == Count) {
- return EFI_NOT_FOUND;
- }
- while (Index < Count - 1) {
- IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1);
- Index++;
- }
- ASSERT (IpInstance->GroupCount > 0);
- IpInstance->GroupCount--;
- return EFI_SUCCESS;
- }
- /**
- Join the multicast group on behalf of this IP6 service binding instance.
- @param[in] IpSb The IP6 service binding instance.
- @param[in] Interface Points to an IP6_INTERFACE structure.
- @param[in] Address The group address to join.
- @retval EFI_SUCCESS Successfully join the multicast group.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
- @retval Others Failed to join the multicast group.
- **/
- EFI_STATUS
- Ip6JoinGroup (
- IN IP6_SERVICE *IpSb,
- IN IP6_INTERFACE *Interface,
- IN EFI_IPv6_ADDRESS *Address
- )
- {
- IP6_MLD_GROUP *Group;
- EFI_STATUS Status;
- Group = Ip6FindMldEntry (IpSb, Address);
- if (Group != NULL) {
- Group->RefCnt++;
- return EFI_SUCCESS;
- }
- //
- // Repeat the report once or twice after short delays [Unsolicited Report Interval] (default:10s)
- // Simulate this operation as a Multicast-Address-Specific Query was received for that address.
- //
- Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL);
- if (Group == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Group->SendByUs = TRUE;
- Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);
- if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
- goto ERROR;
- }
- //
- // Send unsolicited report when a node starts listening to a multicast address
- //
- Status = Ip6SendMldReport (IpSb, Interface, Address);
- if (EFI_ERROR (Status)) {
- goto ERROR;
- }
- return EFI_SUCCESS;
- ERROR:
- RemoveEntryList (&Group->Link);
- FreePool (Group);
- return Status;
- }
- /**
- Leave the IP6 multicast group.
- @param[in] IpSb The IP6 service binding instance.
- @param[in] Address The group address to leave.
- @retval EFI_NOT_FOUND The IP6 service instance isn't in the group.
- @retval EFI_SUCCESS Successfully leave the multicast group..
- @retval Others Failed to leave the multicast group.
- **/
- EFI_STATUS
- Ip6LeaveGroup (
- IN IP6_SERVICE *IpSb,
- IN EFI_IPv6_ADDRESS *Address
- )
- {
- IP6_MLD_GROUP *Group;
- EFI_STATUS Status;
- Group = Ip6FindMldEntry (IpSb, Address);
- if (Group == NULL) {
- return EFI_NOT_FOUND;
- }
- //
- // If more than one instance is in the group, decrease
- // the RefCnt then return.
- //
- if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) {
- return EFI_SUCCESS;
- }
- //
- // If multiple IP6 group addresses are mapped to the same
- // multicast MAC address, don't configure the MNP to leave
- // the MAC.
- //
- if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) {
- Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac);
- if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
- return Status;
- }
- }
- //
- // Send a leave report if we are the last node to report
- //
- if (Group->SendByUs) {
- Status = Ip6SendMldDone (IpSb, Address);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
- RemoveEntryList (&Group->Link);
- FreePool (Group);
- return EFI_SUCCESS;
- }
- /**
- Worker function for EfiIp6Groups(). The caller
- should make sure that the parameters are valid.
- @param[in] IpInstance The IP6 child to change the setting.
- @param[in] JoinFlag TRUE to join the group, otherwise leave it.
- @param[in] GroupAddress The target group address. If NULL, leave all
- the group addresses.
- @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it
- @retval EFI_OUT_OF_RESOURCES Failed to allocate sufficient resources.
- @retval EFI_DEVICE_ERROR Failed to set the group configuration.
- @retval EFI_SUCCESS Successfully updated the group setting.
- @retval EFI_NOT_FOUND Try to leave the group which it isn't a member.
- **/
- EFI_STATUS
- Ip6Groups (
- IN IP6_PROTOCOL *IpInstance,
- IN BOOLEAN JoinFlag,
- IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL
- )
- {
- EFI_STATUS Status;
- IP6_SERVICE *IpSb;
- UINT32 Index;
- EFI_IPv6_ADDRESS *Group;
- IpSb = IpInstance->Service;
- if (JoinFlag) {
- ASSERT (GroupAddress != NULL);
- for (Index = 0; Index < IpInstance->GroupCount; Index++) {
- if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) {
- return EFI_ALREADY_STARTED;
- }
- }
- Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress);
- if (!EFI_ERROR (Status)) {
- return Ip6CombineGroups (IpInstance, GroupAddress);
- }
- return Status;
- }
- //
- // Leave the group. Leave all the groups if GroupAddress is NULL.
- //
- for (Index = IpInstance->GroupCount; Index > 0; Index--) {
- Group = IpInstance->GroupList + (Index - 1);
- if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) {
- Status = Ip6LeaveGroup (IpInstance->Service, Group);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Ip6RemoveGroup (IpInstance, Group);
- if (IpInstance->GroupCount == 0) {
- ASSERT (Index == 1);
- FreePool (IpInstance->GroupList);
- IpInstance->GroupList = NULL;
- }
- if (GroupAddress != NULL) {
- return EFI_SUCCESS;
- }
- }
- }
- return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);
- }
- /**
- Set a random value of the delay timer for the multicast address from the range
- [0, Maximum Response Delay]. If a timer for any address is already
- running, it is reset to the new random value only if the requested
- Maximum Response Delay is less than the remaining value of the
- running timer. If the Query packet specifies a Maximum Response
- Delay of zero, each timer is effectively set to zero, and the action
- specified below for timer expiration is performed immediately.
- @param[in] IpSb The IP6 service binding instance.
- @param[in] MaxRespDelay The Maximum Response Delay, in milliseconds.
- @param[in] MulticastAddr The multicast address.
- @param[in, out] Group Points to a IP6_MLD_GROUP list entry node.
- @retval EFI_SUCCESS The delay timer is successfully updated or
- timer expiration is performed immediately.
- @retval Others Failed to send out MLD report message.
- **/
- EFI_STATUS
- Ip6UpdateDelayTimer (
- IN IP6_SERVICE *IpSb,
- IN UINT16 MaxRespDelay,
- IN EFI_IPv6_ADDRESS *MulticastAddr,
- IN OUT IP6_MLD_GROUP *Group
- )
- {
- UINT32 Delay;
- //
- // If the Query packet specifies a Maximum Response Delay of zero, perform timer
- // expiration immediately.
- //
- if (MaxRespDelay == 0) {
- Group->DelayTimer = 0;
- return Ip6SendMldReport (IpSb, NULL, MulticastAddr);
- }
- Delay = (UINT32)(MaxRespDelay / 1000);
- //
- // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay]
- // If a timer is already running, resets it if the request Maximum Response Delay
- // is less than the remaining value of the running timer.
- //
- if ((Group->DelayTimer == 0) || (Delay < Group->DelayTimer)) {
- Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ());
- }
- return EFI_SUCCESS;
- }
- /**
- Process the Multicast Listener Query message.
- @param[in] IpSb The IP service that received the packet.
- @param[in] Head The IP head of the MLD query packet.
- @param[in] Packet The content of the MLD query packet with IP head
- removed.
- @retval EFI_SUCCESS The MLD query packet processed successfully.
- @retval EFI_INVALID_PARAMETER The packet is invalid.
- @retval Others Failed to process the packet.
- **/
- EFI_STATUS
- Ip6ProcessMldQuery (
- IN IP6_SERVICE *IpSb,
- IN EFI_IP6_HEADER *Head,
- IN NET_BUF *Packet
- )
- {
- EFI_IPv6_ADDRESS AllNodes;
- IP6_MLD_GROUP *Group;
- IP6_MLD_HEAD MldPacket;
- LIST_ENTRY *Entry;
- EFI_STATUS Status;
- Status = EFI_INVALID_PARAMETER;
- //
- // Check the validity of the packet, generic query or specific query
- //
- if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
- goto Exit;
- }
- if ((Head->HopLimit != 1) || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {
- goto Exit;
- }
- //
- // The Packet points to MLD report raw data without Hop-By-Hop option.
- //
- NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *)&MldPacket);
- MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay);
- Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);
- if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) {
- //
- // Receives a Multicast-Address-Specific Query, check it firstly
- //
- if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {
- goto Exit;
- }
- //
- // The node is not listening but it receives the specific query. Just return.
- //
- Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);
- if (Group == NULL) {
- Status = EFI_SUCCESS;
- goto Exit;
- }
- Status = Ip6UpdateDelayTimer (
- IpSb,
- MldPacket.MaxRespDelay,
- &MldPacket.Group,
- Group
- );
- goto Exit;
- }
- //
- // Receives a General Query, sets a delay timer for each multicast address it is listening
- //
- NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {
- Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);
- Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group);
- if (EFI_ERROR (Status)) {
- goto Exit;
- }
- }
- Status = EFI_SUCCESS;
- Exit:
- NetbufFree (Packet);
- return Status;
- }
- /**
- Process the Multicast Listener Report message.
- @param[in] IpSb The IP service that received the packet.
- @param[in] Head The IP head of the MLD report packet.
- @param[in] Packet The content of the MLD report packet with IP head
- removed.
- @retval EFI_SUCCESS The MLD report packet processed successfully.
- @retval EFI_INVALID_PARAMETER The packet is invalid.
- **/
- EFI_STATUS
- Ip6ProcessMldReport (
- IN IP6_SERVICE *IpSb,
- IN EFI_IP6_HEADER *Head,
- IN NET_BUF *Packet
- )
- {
- IP6_MLD_HEAD MldPacket;
- IP6_MLD_GROUP *Group;
- EFI_STATUS Status;
- Status = EFI_INVALID_PARAMETER;
- //
- // Validate the incoming message, if invalid, drop it.
- //
- if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
- goto Exit;
- }
- if ((Head->HopLimit != 1) || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {
- goto Exit;
- }
- //
- // The Packet points to MLD report raw data without Hop-By-Hop option.
- //
- NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *)&MldPacket);
- if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {
- goto Exit;
- }
- Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);
- if (Group == NULL) {
- goto Exit;
- }
- //
- // The report is sent by another node, stop its own timer relates to the multicast address and clear
- //
- if (!Group->SendByUs) {
- Group->DelayTimer = 0;
- }
- Status = EFI_SUCCESS;
- Exit:
- NetbufFree (Packet);
- return Status;
- }
- /**
- The heartbeat timer of MLD module. It sends out a solicited MLD report when
- DelayTimer expires.
- @param[in] IpSb The IP6 service binding instance.
- **/
- VOID
- Ip6MldTimerTicking (
- IN IP6_SERVICE *IpSb
- )
- {
- IP6_MLD_GROUP *Group;
- LIST_ENTRY *Entry;
- //
- // Send solicited report when timer expires
- //
- NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {
- Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);
- if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) {
- Ip6SendMldReport (IpSb, NULL, &Group->Address);
- }
- }
- }
|