mirror of
https://github.com/espressif/openthread.git
synced 2026-08-18 16:39:52 +00:00
[netif] support multicast address subscription by SEDs (#2474)
Updates mainly include: 1) Remove explict registration APIs 2) Define a new constant to limit the maximum number of addresses to register 3) Fix duplicate delivery to SED for packet sent from the parent to the realm-local multicast address its sleepy children subscribe (due to not differentiate MPL retransmission) 4) Some other refinements and resolve conflicts when rebase
This commit is contained in:
@@ -335,7 +335,7 @@ public:
|
||||
* This method returns whether or not the message is of MLE subtype.
|
||||
*
|
||||
* @retval TRUE If message is of MLE subtype.
|
||||
* @retval FLASE If message is not of MLE subtype.
|
||||
* @retval FALSE If message is not of MLE subtype.
|
||||
*
|
||||
*/
|
||||
bool IsSubTypeMle(void) const;
|
||||
|
||||
+45
-6
@@ -208,6 +208,23 @@ otError Ip6::InsertMplOption(Message &aMessage, Header &aIp6Header, MessageInfo
|
||||
}
|
||||
else
|
||||
{
|
||||
if (aIp6Header.GetDestination().IsMulticastLargerThanRealmLocal() &&
|
||||
GetInstance().GetThreadNetif().GetMle().HasSleepyChildrenSubscribed(aIp6Header.GetDestination()))
|
||||
{
|
||||
Message *messageCopy = NULL;
|
||||
|
||||
if ((messageCopy = aMessage.Clone()) != NULL)
|
||||
{
|
||||
HandleDatagram(*messageCopy, NULL, aMessageInfo.GetInterfaceId(), NULL, true);
|
||||
otLogInfoIp6(GetInstance(), "Message copy for indirect transmission to sleepy children");
|
||||
}
|
||||
else
|
||||
{
|
||||
otLogWarnIp6(GetInstance(),
|
||||
"No enough buffer for message copy for indirect transmission to sleepy children");
|
||||
}
|
||||
}
|
||||
|
||||
SuccessOrExit(error = AddTunneledMplOption(aMessage, aIp6Header, aMessageInfo));
|
||||
}
|
||||
|
||||
@@ -371,12 +388,6 @@ otError Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, IpProto
|
||||
|
||||
SuccessOrExit(error = aMessage.Prepend(&header, sizeof(header)));
|
||||
|
||||
if (aMessageInfo.GetPeerAddr().IsMulticast() &&
|
||||
aMessageInfo.GetPeerAddr().GetScope() > Address::kRealmLocalScope)
|
||||
{
|
||||
SuccessOrExit(error = AddTunneledMplOption(aMessage, header, aMessageInfo));
|
||||
}
|
||||
|
||||
// compute checksum
|
||||
checksum = ComputePseudoheaderChecksum(header.GetSource(), header.GetDestination(),
|
||||
payloadLength, aIpProto);
|
||||
@@ -395,6 +406,28 @@ otError Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, IpProto
|
||||
break;
|
||||
}
|
||||
|
||||
if (aMessageInfo.GetPeerAddr().IsMulticastLargerThanRealmLocal())
|
||||
{
|
||||
if (GetInstance().GetThreadNetif().GetMle().HasSleepyChildrenSubscribed(header.GetDestination()))
|
||||
{
|
||||
Message *messageCopy = NULL;
|
||||
|
||||
if ((messageCopy = aMessage.Clone()) != NULL)
|
||||
{
|
||||
otLogInfoIp6(GetInstance(), "Message copy for indirect transmission to sleepy children");
|
||||
messageCopy->SetInterfaceId(aMessageInfo.GetInterfaceId());
|
||||
EnqueueDatagram(*messageCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
otLogWarnIp6(GetInstance(),
|
||||
"No enough buffer for message copy for indirect transmission to sleepy children");
|
||||
}
|
||||
}
|
||||
|
||||
SuccessOrExit(error = AddTunneledMplOption(aMessage, header, aMessageInfo));
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
if (error == OT_ERROR_NONE)
|
||||
@@ -728,6 +761,12 @@ otError Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, int8_t aInterfaceI
|
||||
{
|
||||
multicastPromiscuous = true;
|
||||
}
|
||||
|
||||
if (header.GetDestination().IsMulticastLargerThanRealmLocal() &&
|
||||
GetInstance().GetThreadNetif().GetMle().HasSleepyChildrenSubscribed(header.GetDestination()))
|
||||
{
|
||||
forward = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -94,6 +94,11 @@ bool Address::IsRealmLocalMulticast(void) const
|
||||
return IsMulticast() && (GetScope() == kRealmLocalScope);
|
||||
}
|
||||
|
||||
bool Address::IsMulticastLargerThanRealmLocal(void) const
|
||||
{
|
||||
return IsMulticast() && (GetScope() > kRealmLocalScope);
|
||||
}
|
||||
|
||||
bool Address::IsRealmLocalAllNodesMulticast(void) const
|
||||
{
|
||||
return (mFields.m32[0] == HostSwap32(0xff030000) && mFields.m32[1] == 0 &&
|
||||
|
||||
@@ -211,6 +211,15 @@ public:
|
||||
*/
|
||||
bool IsRealmLocalAllMplForwarders(void) const;
|
||||
|
||||
/**
|
||||
* This method indicates whether or not the IPv6 address is multicast larger than realm local.
|
||||
*
|
||||
* @retval TRUE If the IPv6 address is multicast larger than realm local.
|
||||
* @retval FALSE If the IPv6 address is not multicast or the scope is not larger than realm local.
|
||||
*
|
||||
*/
|
||||
bool IsMulticastLargerThanRealmLocal(void) const;
|
||||
|
||||
/**
|
||||
* This method indicates whether or not the IPv6 address is a RLOC address.
|
||||
*
|
||||
|
||||
@@ -249,6 +249,32 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError Netif::GetNextExternalMulticast(uint8_t &aIterator, Address &aAddress)
|
||||
{
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
size_t num = sizeof(mExtMulticastAddresses) / sizeof(mExtMulticastAddresses[0]);
|
||||
NetifMulticastAddress *entry;
|
||||
|
||||
VerifyOrExit(aIterator < num);
|
||||
|
||||
// Find an available entry in the `mExtMulticastAddresses` array.
|
||||
for (uint8_t i = aIterator; i < num; i++)
|
||||
{
|
||||
entry = &mExtMulticastAddresses[i];
|
||||
|
||||
// In an unused/available entry, `mNext` points back to the entry itself.
|
||||
if (entry->mNext != entry)
|
||||
{
|
||||
aAddress = entry->GetAddress();
|
||||
aIterator = i + 1;
|
||||
ExitNow(error = OT_ERROR_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError Netif::SubscribeExternalMulticast(const Address &aAddress)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
@@ -337,6 +337,20 @@ public:
|
||||
*/
|
||||
otError UnsubscribeMulticast(const NetifMulticastAddress &aAddress);
|
||||
|
||||
/**
|
||||
* This method provides the next external multicast address that the network interface subscribed.
|
||||
* It is used to iterate through the entries of the external multicast address table.
|
||||
*
|
||||
* @param[inout] aIterator A reference to the iterator context. To get the first
|
||||
* external multicast address, it should be set to 0.
|
||||
* @param[out] aAddress A reference where to place the external multicast address.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully found the next external multicast address.
|
||||
* @retval OT_ERROR_NOT_FOUND No subsequent external multicast address.
|
||||
*
|
||||
*/
|
||||
otError GetNextExternalMulticast(uint8_t &aIterator, Address &aAddress);
|
||||
|
||||
/**
|
||||
* This method subscribes the network interface to the external (to OpenThread) multicast address.
|
||||
*
|
||||
|
||||
@@ -284,13 +284,23 @@
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD
|
||||
*
|
||||
* The minimum number of supported IPv6 address registrations per child.
|
||||
* The maximum number of supported IPv6 address registrations per child.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD
|
||||
#define OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD 4
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_IP_ADDRS_TO_REGISTER
|
||||
*
|
||||
* The maximum number of IPv6 address registrations for MTD.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_IP_ADDRS_TO_REGISTER
|
||||
#define OPENTHREAD_CONFIG_IP_ADDRS_TO_REGISTER (OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS
|
||||
*
|
||||
|
||||
@@ -59,23 +59,45 @@ otError MeshForwarder::SendMessage(Message &aMessage)
|
||||
|
||||
aMessage.Read(0, sizeof(ip6Header), &ip6Header);
|
||||
|
||||
if (ip6Header.GetDestination() == netif.GetMle().GetLinkLocalAllThreadNodesAddress() ||
|
||||
ip6Header.GetDestination() == netif.GetMle().GetRealmLocalAllThreadNodesAddress())
|
||||
if (ip6Header.GetDestination().IsMulticast())
|
||||
{
|
||||
// schedule direct transmission
|
||||
aMessage.SetDirectTransmission();
|
||||
// For traffic destined to multicast address larger than realm local, generally it uses IP-in-IP
|
||||
// encapsulation (RFC2473), with outer destination as ALL_MPL_FORWARDERS. So here if the destination
|
||||
// is multicast address larger than realm local, it should be for indirection transmission for the
|
||||
// device's sleepy child, thus there should be no direct transmission.
|
||||
if (!ip6Header.GetDestination().IsMulticastLargerThanRealmLocal())
|
||||
{
|
||||
// schedule direct transmission
|
||||
aMessage.SetDirectTransmission();
|
||||
}
|
||||
|
||||
if (aMessage.GetSubType() != Message::kSubTypeMplRetransmission)
|
||||
{
|
||||
// destined for all sleepy children
|
||||
child = netif.GetMle().GetChildren(&numChildren);
|
||||
|
||||
for (uint8_t i = 0; i < numChildren; i++, child++)
|
||||
if (ip6Header.GetDestination() == netif.GetMle().GetLinkLocalAllThreadNodesAddress() ||
|
||||
ip6Header.GetDestination() == netif.GetMle().GetRealmLocalAllThreadNodesAddress())
|
||||
{
|
||||
if (child->IsStateValidOrRestoring() && !child->IsRxOnWhenIdle())
|
||||
// destined for all sleepy children
|
||||
for (uint8_t i = 0; i < numChildren; i++, child++)
|
||||
{
|
||||
aMessage.SetChildMask(i);
|
||||
mSourceMatchController.IncrementMessageCount(*child);
|
||||
if (child->IsStateValidOrRestoring() && !child->IsRxOnWhenIdle())
|
||||
{
|
||||
aMessage.SetChildMask(i);
|
||||
mSourceMatchController.IncrementMessageCount(*child);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// destined for some sleepy children which subscribed the multicast address.
|
||||
for (uint8_t i = 0; i < numChildren; i++, child++)
|
||||
{
|
||||
if (netif.GetMle().IsSleepyChildSubscribed(ip6Header.GetDestination(), *child))
|
||||
{
|
||||
aMessage.SetChildMask(i);
|
||||
mSourceMatchController.IncrementMessageCount(*child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
-4
@@ -1206,11 +1206,12 @@ otError Mle::AppendVersion(Message &aMessage)
|
||||
otError Mle::AppendAddressRegistration(Message &aMessage)
|
||||
{
|
||||
ThreadNetif &netif = GetNetif();
|
||||
otError error;
|
||||
otError error = OT_ERROR_NONE;
|
||||
Tlv tlv;
|
||||
AddressRegistrationEntry entry;
|
||||
Lowpan::Context context;
|
||||
uint8_t length = 0;
|
||||
uint8_t counter = 0;
|
||||
uint16_t startOffset = aMessage.GetLength();
|
||||
|
||||
tlv.SetType(Tlv::kAddressRegistration);
|
||||
@@ -1219,7 +1220,9 @@ otError Mle::AppendAddressRegistration(Message &aMessage)
|
||||
// write entries to message
|
||||
for (const Ip6::NetifUnicastAddress *addr = netif.GetUnicastAddresses(); addr; addr = addr->GetNext())
|
||||
{
|
||||
if (addr->GetAddress().IsLinkLocal() || addr->GetAddress() == mMeshLocal16.GetAddress())
|
||||
if (addr->GetAddress().IsLinkLocal() ||
|
||||
IsRoutingLocator(addr->GetAddress()) ||
|
||||
IsAnycastLocator(addr->GetAddress()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1239,12 +1242,39 @@ otError Mle::AppendAddressRegistration(Message &aMessage)
|
||||
|
||||
SuccessOrExit(error = aMessage.Append(&entry, entry.GetLength()));
|
||||
length += entry.GetLength();
|
||||
counter++;
|
||||
// only continue to append if there is available entry.
|
||||
VerifyOrExit(counter < OPENTHREAD_CONFIG_IP_ADDRS_TO_REGISTER);
|
||||
}
|
||||
|
||||
tlv.SetLength(length);
|
||||
aMessage.Write(startOffset, sizeof(tlv), &tlv);
|
||||
// For sleepy end device, register external multicast addresses to the parent for indirect transmission
|
||||
if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0)
|
||||
{
|
||||
uint8_t iterator = 0;
|
||||
Ip6::Address address;
|
||||
|
||||
// append external multicast address
|
||||
while (netif.GetNextExternalMulticast(iterator, address) == OT_ERROR_NONE)
|
||||
{
|
||||
entry.SetUncompressed();
|
||||
entry.SetIp6Address(address);
|
||||
SuccessOrExit(error = aMessage.Append(&entry, entry.GetLength()));
|
||||
length += entry.GetLength();
|
||||
|
||||
counter++;
|
||||
// only continue to append if there is available entry.
|
||||
VerifyOrExit(counter < OPENTHREAD_CONFIG_IP_ADDRS_TO_REGISTER);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
if (error == OT_ERROR_NONE && length > 0)
|
||||
{
|
||||
tlv.SetLength(length);
|
||||
aMessage.Write(startOffset, sizeof(tlv), &tlv);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -1313,6 +1343,16 @@ void Mle::HandleStateChanged(uint32_t aFlags)
|
||||
}
|
||||
}
|
||||
|
||||
if ((aFlags & (OT_CHANGED_IP6_MULTICAST_SUBSRCRIBED | OT_CHANGED_IP6_MULTICAST_UNSUBSRCRIBED)) != 0)
|
||||
{
|
||||
if (mRole == OT_DEVICE_ROLE_CHILD &&
|
||||
(mDeviceMode & ModeTlv::kModeFFD) == 0 &&
|
||||
(mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0)
|
||||
{
|
||||
mSendChildUpdateRequest.Post();
|
||||
}
|
||||
}
|
||||
|
||||
if ((aFlags & OT_CHANGED_THREAD_NETDATA) != 0)
|
||||
{
|
||||
if (mDeviceMode & ModeTlv::kModeFFD)
|
||||
|
||||
@@ -2130,6 +2130,11 @@ otError MleRouter::UpdateChildAddresses(const AddressRegistrationTlv &aTlv, Chil
|
||||
|
||||
}
|
||||
|
||||
if (address.IsMulticast())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We check if the same address is in-use by another child, if so
|
||||
// remove it. This implements "last-in wins" duplicate address
|
||||
// resolution policy.
|
||||
@@ -4696,18 +4701,19 @@ otError MleRouter::AppendChildAddresses(Message &aMessage, Child &aChild)
|
||||
|
||||
while (aChild.GetNextIp6Address(GetInstance(), iterator, address) == OT_ERROR_NONE)
|
||||
{
|
||||
if (netif.GetNetworkDataLeader().GetContext(address, context) == OT_ERROR_NONE)
|
||||
{
|
||||
// compressed entry
|
||||
entry.SetContextId(context.mContextId);
|
||||
entry.SetIid(address.GetIid());
|
||||
}
|
||||
else
|
||||
if (address.IsMulticast() ||
|
||||
netif.GetNetworkDataLeader().GetContext(address, context) != OT_ERROR_NONE)
|
||||
{
|
||||
// uncompressed entry
|
||||
entry.SetUncompressed();
|
||||
entry.SetIp6Address(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
// compressed entry
|
||||
entry.SetContextId(context.mContextId);
|
||||
entry.SetIid(address.GetIid());
|
||||
}
|
||||
|
||||
SuccessOrExit(error = aMessage.Append(&entry, entry.GetLength()));
|
||||
length += entry.GetLength();
|
||||
@@ -5036,6 +5042,47 @@ void MleRouter::SignalChildUpdated(otThreadChildTableEvent aEvent, Child &aChild
|
||||
}
|
||||
}
|
||||
|
||||
bool MleRouter::HasSleepyChildrenSubscribed(const Ip6::Address &aAddress)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
for (uint8_t i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (!mChildren[i].IsStateValidOrRestoring() || mChildren[i].IsRxOnWhenIdle())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsSleepyChildSubscribed(aAddress, mChildren[i]))
|
||||
{
|
||||
ExitNow(rval = true);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool MleRouter::IsSleepyChildSubscribed(const Ip6::Address &aAddress, Child &aChild)
|
||||
{
|
||||
bool rval = false;
|
||||
Ip6::Address address;
|
||||
Child::Ip6AddressIterator iterator;
|
||||
|
||||
VerifyOrExit(aChild.IsStateValidOrRestoring() && !aChild.IsRxOnWhenIdle());
|
||||
|
||||
while (aChild.GetNextIp6Address(GetInstance(), iterator, address) == OT_ERROR_NONE)
|
||||
{
|
||||
if (address == aAddress)
|
||||
{
|
||||
ExitNow(rval = true);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rval;
|
||||
}
|
||||
|
||||
} // namespace Mle
|
||||
} // namespace ot
|
||||
|
||||
|
||||
@@ -753,6 +753,29 @@ public:
|
||||
*/
|
||||
otThreadChildTableCallback GetChildTableChangedCallback(void) const { return mChildTableChangedCallback; }
|
||||
|
||||
/**
|
||||
* This method returns whether the device has any sleepy children subscribed the address.
|
||||
*
|
||||
* @param[in] aAddress The reference of the address.
|
||||
*
|
||||
* @retval TRUE If the device has any sleepy children subscribed the address @p aAddress.
|
||||
* @retval FALSE If the device doesn't have any sleepy children subscribed the address @p aAddress.
|
||||
*
|
||||
*/
|
||||
bool HasSleepyChildrenSubscribed(const Ip6::Address &aAddress);
|
||||
|
||||
/**
|
||||
* This method returns whether the specific child subscribed the address.
|
||||
*
|
||||
* @param[in] aAddress The reference of the address.
|
||||
* @param[in] aChild The reference of the child.
|
||||
*
|
||||
* @retval TRUE If the sleepy child @p aChild subscribed the address @p aAddress.
|
||||
* @retval FALSE If the sleepy child @p aChild did not subscribe the address @p aAddress.
|
||||
*
|
||||
*/
|
||||
bool IsSleepyChildSubscribed(const Ip6::Address &aAddress, Child &aChild);
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
|
||||
@@ -136,6 +136,10 @@ public:
|
||||
|
||||
otError GetMaxChildTimeout(uint32_t &) { return OT_ERROR_NOT_IMPLEMENTED; }
|
||||
|
||||
bool HasSleepyChildrenSubscribed(const Ip6::Address &) { return false; }
|
||||
|
||||
bool IsSleepyChildSubscribed(const Ip6::Address &, Child &) { return false; }
|
||||
|
||||
private:
|
||||
otError HandleDetachStart(void) { return OT_ERROR_NONE; }
|
||||
otError HandleChildStart(AttachMode) { return OT_ERROR_NONE; }
|
||||
|
||||
@@ -1466,7 +1466,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
AddressRegistrationEntry mAddresses[4];
|
||||
AddressRegistrationEntry mAddresses[OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD];
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user