[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:
rongli
2018-01-29 16:11:25 +00:00
committed by Jonathan Hui
parent 20fd57f6a4
commit b12cb9e41f
13 changed files with 268 additions and 29 deletions
+1 -1
View File
@@ -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
View File
@@ -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
{
+5
View File
@@ -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 &&
+9
View File
@@ -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.
*
+26
View File
@@ -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;
+14
View File
@@ -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.
*
+11 -1
View File
@@ -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
*
+31 -9
View File
@@ -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
View File
@@ -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)
+54 -7
View File
@@ -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
+23
View File
@@ -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
{
+4
View File
@@ -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; }
+1 -1
View File
@@ -1466,7 +1466,7 @@ public:
}
private:
AddressRegistrationEntry mAddresses[4];
AddressRegistrationEntry mAddresses[OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD];
} OT_TOOL_PACKED_END;
/**