Add NetworkPkg (P.UDK2010.UP3.Network.P1)
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
908
NetworkPkg/Ip6Dxe/Ip6Mld.c
Normal file
908
NetworkPkg/Ip6Dxe/Ip6Mld.c
Normal file
@@ -0,0 +1,908 @@
|
||||
/** @file
|
||||
Multicast Listener Discovery support routines.
|
||||
|
||||
Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
|
||||
|
||||
This program and the accompanying materials
|
||||
are licensed and made available under the terms and conditions of the BSD License
|
||||
which accompanies this distribution. The full text of the license may be found at
|
||||
http://opensource.org/licenses/bsd-license.php.
|
||||
|
||||
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
|
||||
**/
|
||||
|
||||
#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_SUCESS 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 twcie after short delays [Unsolicited Report Interval] (default:10s)
|
||||
// Simulate this operation as a Multicast-Address-Specific Query was received for that addresss.
|
||||
//
|
||||
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 configuraton.
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user