diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 2007a4713..636d27543 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -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; diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 1afa1517c..8b31eedfd 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -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 { diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index a60e0082a..1debc7c0e 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -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 && diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index 2333f6dbd..a4a8c252f 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -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. * diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index a51706248..ab708212e 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -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; diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index b350dc51a..68be50351 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -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. * diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index f564f0c0b..fc9e54488 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -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 * diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index a9e74c56f..3e34cfda4 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -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); + } } } } diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 18bce086f..088407540 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -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) diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index cb9aadea6..f88b1d2c5 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -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 diff --git a/src/core/thread/mle_router_ftd.hpp b/src/core/thread/mle_router_ftd.hpp index 041d9b751..060058bde 100644 --- a/src/core/thread/mle_router_ftd.hpp +++ b/src/core/thread/mle_router_ftd.hpp @@ -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 { diff --git a/src/core/thread/mle_router_mtd.hpp b/src/core/thread/mle_router_mtd.hpp index 5080f3b40..80be08cd5 100644 --- a/src/core/thread/mle_router_mtd.hpp +++ b/src/core/thread/mle_router_mtd.hpp @@ -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; } diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp index 3d2fd1ff0..f66b2af97 100644 --- a/src/core/thread/mle_tlvs.hpp +++ b/src/core/thread/mle_tlvs.hpp @@ -1466,7 +1466,7 @@ public: } private: - AddressRegistrationEntry mAddresses[4]; + AddressRegistrationEntry mAddresses[OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD]; } OT_TOOL_PACKED_END; /**