From 78d4b678e3fb2568b5b017490658611c008fa2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Duda?= Date: Mon, 24 Oct 2016 20:34:15 +0200 Subject: [PATCH] MPL: Introduce multicast forwarding. (#827) * Message: Add a new optional parameter to Clone method. * MPL: Introduce MPL Forwarding This commit introduces MPL Forwarding for Routers. All references to MPL Seed have been replaced with MPL Seed Id. --- src/core/common/message.cpp | 6 +- src/core/common/message.hpp | 14 +- src/core/net/ip6.cpp | 12 +- src/core/net/ip6.hpp | 10 +- src/core/net/ip6_mpl.cpp | 292 ++++++++++++++--- src/core/net/ip6_mpl.hpp | 374 +++++++++++++++++++--- src/core/openthread-core-default-config.h | 21 +- src/core/thread/mle.cpp | 4 +- src/core/thread/mle_constants.hpp | 10 + src/core/thread/mle_router.cpp | 2 + 10 files changed, 635 insertions(+), 110 deletions(-) diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index 2dfdfa2cb..21f82f9f5 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -474,14 +474,14 @@ int Message::CopyTo(uint16_t aSourceOffset, uint16_t aDestinationOffset, uint16_ return bytesCopied; } -Message *Message::Clone(void) const +Message *Message::Clone(uint16_t aLength) const { ThreadError error = kThreadError_None; Message *messageCopy; VerifyOrExit((messageCopy = GetMessagePool()->New(GetType(), GetReserved())) != NULL, error = kThreadError_NoBufs); - SuccessOrExit(error = messageCopy->SetLength(GetLength())); - CopyTo(0, 0, GetLength(), *messageCopy); + SuccessOrExit(error = messageCopy->SetLength(aLength)); + CopyTo(0, 0, aLength, *messageCopy); // Copy selected message information. messageCopy->SetOffset(GetOffset()); diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index fda272dd1..bb3de492a 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -386,11 +386,21 @@ public: /** * This method creates a copy of the current Message. It allocates the new one - * from the same Message Poll as the original Message. + * from the same Message Poll as the original Message and copies @p aLength octets of a payload. + * + * @param[in] aLength Number of payload bytes to copy. * * @returns A pointer to the message or NULL if insufficient message buffers are available. */ - Message *Clone(void) const; + Message *Clone(uint16_t aLength) const; + + /** + * This method creates a copy of the current Message. It allocates the new one + * from the same Message Poll as the original Message and copies a full payload. + * + * @returns A pointer to the message or NULL if insufficient message buffers are available. + */ + Message *Clone(void) const { return Clone(GetLength()); }; /** * This method returns the datagram tag used for 6LoWPAN fragmentation. diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 407ca5399..5e10835a1 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -151,6 +151,12 @@ exit: return error; } +void Ip6::EnqueueDatagram(Message &aMessage) +{ + mSendQueue.Enqueue(aMessage); + mSendQueueTask.Post(); +} + ThreadError Ip6::SendDatagram(Message &message, MessageInfo &messageInfo, IpProto ipproto) { ThreadError error = kThreadError_None; @@ -211,8 +217,7 @@ exit: if (error == kThreadError_None) { message.SetInterfaceId(messageInfo.mInterfaceId); - mSendQueue.Enqueue(message); - mSendQueueTask.Post(); + EnqueueDatagram(message); } return error; @@ -308,7 +313,7 @@ exit: return error; } -ThreadError Ip6::HandleExtensionHeaders(Message &message, Header &header, uint8_t &nextHeader, bool &forward, +ThreadError Ip6::HandleExtensionHeaders(Message &message, Header &header, uint8_t &nextHeader, bool forward, bool receive) { ThreadError error = kThreadError_None; @@ -488,6 +493,7 @@ ThreadError Ip6::HandleDatagram(Message &message, Netif *netif, int8_t interface } } + message.SetInterfaceId(interfaceId); message.SetOffset(sizeof(header)); // process IPv6 Extension Headers diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index d89cc3e79..0696bc5bd 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -146,6 +146,14 @@ public: ThreadError HandleDatagram(Message &aMessage, Netif *aNetif, int8_t aInterfaceId, const void *aLinkMessageInfo, bool aFromNcpHost); + + /** + * This methods adds a full IPv6 packet to the transmit queue. + * + * @param aMessage A reference to the message. + */ + void EnqueueDatagram(Message &aMessage); + /** * This static method updates a checksum. * @@ -335,7 +343,7 @@ private: void HandleSendQueue(void); ThreadError ProcessReceiveCallback(const Message &aMessage, const MessageInfo &aMessageInfo, uint8_t aIpProto); - ThreadError HandleExtensionHeaders(Message &message, Header &header, uint8_t &nextHeader, bool &forward, + ThreadError HandleExtensionHeaders(Message &message, Header &header, uint8_t &nextHeader, bool forward, bool receive); ThreadError HandleFragment(Message &message); ThreadError AddMplOption(Message &message, Header &header, IpProto nextHeader, uint16_t payloadLength); diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index 884ea6b7f..f485060a8 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -35,17 +35,31 @@ #include #include #include +#include namespace Thread { namespace Ip6 { +void MplBufferedMessageMetadata::GenerateNextTransmissionTime(uint32_t aCurrentTime, uint8_t aInterval) +{ + // Emulate Trickle timer behavior and set up the next retransmission within [0,I) range. + uint8_t t = otPlatRandomGet() % aInterval; + + // Set transmission time at the beginning of the next interval. + SetTransmissionTime(aCurrentTime + GetIntervalOffset() + t); + SetIntervalOffset(aInterval - t); +} + Mpl::Mpl(Ip6 &aIp6): - mTimer(aIp6.mTimerScheduler, &Mpl::HandleTimer, this), + mIp6(aIp6), + mSeedSetTimer(aIp6.mTimerScheduler, &Mpl::HandleSeedSetTimer, this), + mRetransmissionTimer(aIp6.mTimerScheduler, &Mpl::HandleRetransmissionTimer, this), + mTimerExpirations(0), + mSequence(0), + mSeedId(0), mMatchingAddress(NULL) { - memset(mEntries, 0, sizeof(mEntries)); - mSequence = 0; - mSeed = 0; + memset(mSeedSet, 0, sizeof(mSeedSet)); } void Mpl::InitOption(OptionMpl &aOption, const Address &aAddress) @@ -53,49 +67,41 @@ void Mpl::InitOption(OptionMpl &aOption, const Address &aAddress) aOption.Init(); aOption.SetSequence(mSequence++); - // Check if Seed can be elided. + // Check if Seed Id can be elided. if (mMatchingAddress && aAddress == *mMatchingAddress) { - aOption.SetSeedLength(OptionMpl::kSeedLength0); + aOption.SetSeedIdLength(OptionMpl::kSeedIdLength0); // Decrease default option length. - aOption.SetLength(aOption.GetLength() - sizeof(mSeed)); + aOption.SetLength(aOption.GetLength() - sizeof(mSeedId)); } else { - aOption.SetSeedLength(OptionMpl::kSeedLength2); - aOption.SetSeed(mSeed); + aOption.SetSeedIdLength(OptionMpl::kSeedIdLength2); + aOption.SetSeedId(mSeedId); } } -ThreadError Mpl::ProcessOption(const Message &aMessage, const Address &aAddress, bool &aForward) +ThreadError Mpl::UpdateSeedSet(uint16_t aSeedId, uint8_t aSequence) { ThreadError error = kThreadError_None; - OptionMpl option; - MplEntry *entry = NULL; + MplSeedEntry *entry = NULL; int8_t diff; - VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(option), &option) >= OptionMpl::kMinLength && - (option.GetSeedLength() == OptionMpl::kSeedLength0 || - option.GetSeedLength() == OptionMpl::kSeedLength2), - error = kThreadError_Drop); - - if (option.GetSeedLength() == OptionMpl::kSeedLength0) + for (uint32_t i = 0; i < kNumSeedEntries; i++) { - // Retrieve Seed from the IPv6 Source Address. - option.SetSeed(HostSwap16(aAddress.mFields.m16[7])); - } - - for (int i = 0; i < kNumEntries; i++) - { - if (mEntries[i].mLifetime == 0) + if (mSeedSet[i].GetLifetime() == 0) { - entry = &mEntries[i]; + // Start allocating from the first possible entry to speed up process of searching. + if (entry == NULL) + { + entry = &mSeedSet[i]; + } } - else if (mEntries[i].mSeed == option.GetSeed()) + else if (mSeedSet[i].GetSeedId() == aSeedId) { - entry = &mEntries[i]; - diff = static_cast(option.GetSequence() - entry->mSequence); + entry = &mSeedSet[i]; + diff = static_cast(aSequence - entry->GetSequence()); VerifyOrExit(diff > 0, error = kThreadError_Drop); @@ -105,38 +111,240 @@ ThreadError Mpl::ProcessOption(const Message &aMessage, const Address &aAddress, VerifyOrExit(entry != NULL, error = kThreadError_Drop); - aForward = true; - - entry->mSeed = option.GetSeed(); - entry->mSequence = option.GetSequence(); - entry->mLifetime = kLifetime; - mTimer.Start(1000); + entry->SetSeedId(aSeedId); + entry->SetSequence(aSequence); + entry->SetLifetime(kSeedEntryLifetime); + mSeedSetTimer.Start(kSeedEntryLifetimeDt); exit: return error; } -void Mpl::HandleTimer(void *aContext) +void Mpl::UpdateBufferedSet(uint16_t aSeedId, uint8_t aSequence) { - static_cast(aContext)->HandleTimer(); + int8_t diff; + MplBufferedMessageMetadata messageMetadata; + + Message *message = mBufferedMessageSet.GetHead(); + Message *nextMessage = NULL; + + // Check if multicast forwarding is enabled. + VerifyOrExit(GetTimerExpirations() > 0, ;); + + while (message != NULL) + { + nextMessage = message->GetNext(); + messageMetadata.ReadFrom(*message); + + if (messageMetadata.GetSeedId() == aSeedId) + { + diff = static_cast(aSequence - messageMetadata.GetSequence()); + + if (diff > 0) + { + // Stop retransmitting MPL Data Message that is consider to be old. + mBufferedMessageSet.Dequeue(*message); + message->Free(); + } + + break; + } + + message = nextMessage; + } + +exit: + return; } -void Mpl::HandleTimer() +void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSequence, bool aIsOutbound) +{ + uint32_t now = Timer::GetNow(); + ThreadError error = kThreadError_None; + Message *messageCopy = NULL; + MplBufferedMessageMetadata messageMetadata; + uint32_t nextTransmissionTime; + uint8_t hopLimit; + + VerifyOrExit((messageCopy = aMessage.Clone()) != NULL, error = kThreadError_NoBufs); + + if (!aIsOutbound) + { + aMessage.Read(Header::GetHopLimitOffset(), Header::GetHopLimitSize(), &hopLimit); + VerifyOrExit(--hopLimit > 0, error = kThreadError_Drop); + messageCopy->Write(Header::GetHopLimitOffset(), Header::GetHopLimitSize(), &hopLimit); + } + + messageMetadata.SetSeedId(aSeedId); + messageMetadata.SetSequence(aSequence); + messageMetadata.SetTransmissionCount(aIsOutbound ? 1 : 0); + messageMetadata.GenerateNextTransmissionTime(now, kDataMessageInterval); + + // Append the message with MplBufferedMessageMetadata and add it to the queue. + SuccessOrExit(error = messageMetadata.AppendTo(*messageCopy)); + mBufferedMessageSet.Enqueue(*messageCopy); + + if (mRetransmissionTimer.IsRunning()) + { + // If timer is already running, check if it should be restarted with earlier fire time. + nextTransmissionTime = mRetransmissionTimer.Gett0() + mRetransmissionTimer.Getdt(); + + if (messageMetadata.IsEarlier(nextTransmissionTime)) + { + mRetransmissionTimer.Start(messageMetadata.GetTransmissionTime() - now); + } + } + else + { + // Otherwise just set the timer. + mRetransmissionTimer.Start(messageMetadata.GetTransmissionTime() - now); + } + +exit: + + if (error != kThreadError_None && messageCopy != NULL) + { + messageCopy->Free(); + } + + return; +} + +ThreadError Mpl::ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound) +{ + ThreadError error; + OptionMpl option; + + VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(option), &option) >= OptionMpl::kMinLength && + (option.GetSeedIdLength() == OptionMpl::kSeedIdLength0 || + option.GetSeedIdLength() == OptionMpl::kSeedIdLength2), + error = kThreadError_Drop); + + if (option.GetSeedIdLength() == OptionMpl::kSeedIdLength0) + { + // Retrieve MPL Seed Id from the IPv6 Source Address. + option.SetSeedId(HostSwap16(aAddress.mFields.m16[7])); + } + + // Check MPL Data Messages in the MPL Buffered Set against sequence number. + UpdateBufferedSet(option.GetSeedId(), option.GetSequence()); + + // Check if the MPL Data Message is new. + error = UpdateSeedSet(option.GetSeedId(), option.GetSequence()); + + if (error == kThreadError_None) + { + AddBufferedMessage(aMessage, option.GetSeedId(), option.GetSequence(), aIsOutbound); + } + else if (aIsOutbound) + { + // In case MPL Data Message is generated locally, ignore potential error of the MPL Seed Set + // to allow subsequent retransmissions with the same sequence number. + ExitNow(error = kThreadError_None); + } + +exit: + return error; +} + +void Mpl::HandleRetransmissionTimer(void *aContext) +{ + static_cast(aContext)->HandleRetransmissionTimer(); +} + +void Mpl::HandleRetransmissionTimer() +{ + uint32_t now = Timer::GetNow(); + uint32_t nextDelta = 0xffffffff; + MplBufferedMessageMetadata messageMetadata; + + Message *message = mBufferedMessageSet.GetHead(); + Message *nextMessage = NULL; + + while (message != NULL) + { + nextMessage = message->GetNext(); + messageMetadata.ReadFrom(*message); + + if (messageMetadata.IsLater(now)) + { + // Calculate the next retransmission time and choose the lowest. + if (messageMetadata.GetTransmissionTime() - now < nextDelta) + { + nextDelta = messageMetadata.GetTransmissionTime() - now; + } + } + else + { + // Update the number of transmission timer expirations. + messageMetadata.SetTransmissionCount(messageMetadata.GetTransmissionCount() + 1); + + if (messageMetadata.GetTransmissionCount() < GetTimerExpirations()) + { + Message *messageCopy = message->Clone(message->GetLength() - sizeof(MplBufferedMessageMetadata)); + + if (messageCopy != NULL) + { + mIp6.EnqueueDatagram(*messageCopy); + } + + messageMetadata.GenerateNextTransmissionTime(now, kDataMessageInterval); + messageMetadata.UpdateIn(*message); + + // Check if retransmission time is lower than the current lowest one. + if (messageMetadata.GetTransmissionTime() - now < nextDelta) + { + nextDelta = messageMetadata.GetTransmissionTime() - now; + } + } + else + { + mBufferedMessageSet.Dequeue(*message); + + if (messageMetadata.GetTransmissionCount() == GetTimerExpirations()) + { + // Remove the extra metadata from the MPL Data Message. + messageMetadata.RemoveFrom(*message); + mIp6.EnqueueDatagram(*message); + } + else + { + // Stop retransmitting if the number of timer expirations is already exceeded. + message->Free(); + } + } + } + + message = nextMessage; + } + + if (nextDelta != 0xffffffff) + { + mRetransmissionTimer.Start(nextDelta); + } +} + +void Mpl::HandleSeedSetTimer(void *aContext) +{ + static_cast(aContext)->HandleSeedSetTimer(); +} + +void Mpl::HandleSeedSetTimer() { bool startTimer = false; - for (int i = 0; i < kNumEntries; i++) + for (int i = 0; i < kNumSeedEntries; i++) { - if (mEntries[i].mLifetime > 0) + if (mSeedSet[i].GetLifetime() > 0) { - mEntries[i].mLifetime--; + mSeedSet[i].SetLifetime(mSeedSet[i].GetLifetime() - 1); startTimer = true; } } if (startTimer) { - mTimer.Start(1000); + mSeedSetTimer.Start(kSeedEntryLifetimeDt); } } diff --git a/src/core/net/ip6_mpl.hpp b/src/core/net/ip6_mpl.hpp index 39d53d8d1..6faa9afbf 100644 --- a/src/core/net/ip6_mpl.hpp +++ b/src/core/net/ip6_mpl.hpp @@ -86,31 +86,31 @@ public: uint8_t GetTotalLength() const { return OptionHeader::GetLength() + sizeof(OptionHeader); } /** - * MPL Seed lengths. + * MPL Seed Id lengths. */ - enum SeedLength + enum SeedIdLength { - kSeedLength0 = 0 << 6, ///< 0-byte MPL Seed Length. - kSeedLength2 = 1 << 6, ///< 2-byte MPL Seed Length. - kSeedLength8 = 2 << 6, ///< 8-byte MPL Seed Length. - kSeedLength16 = 3 << 6, ///< 16-byte MPL Seed Length. + kSeedIdLength0 = 0 << 6, ///< 0-byte MPL Seed Id Length. + kSeedIdLength2 = 1 << 6, ///< 2-byte MPL Seed Id Length. + kSeedIdLength8 = 2 << 6, ///< 8-byte MPL Seed Id Length. + kSeedIdLength16 = 3 << 6, ///< 16-byte MPL Seed Id Length. }; /** - * This method returns the MPL Seed Length value. + * This method returns the MPL Seed Id Length value. * - * @returns The MPL Seed Length value. + * @returns The MPL Seed Id Length value. * */ - SeedLength GetSeedLength() { return static_cast(mControl & kSeedLengthMask); } + SeedIdLength GetSeedIdLength() { return static_cast(mControl & kSeedIdLengthMask); } /** - * This method sets the MPL Seed Length value. + * This method sets the MPL Seed Id Length value. * - * @param[in] aSeedLength The MPL Seed Length. + * @param[in] aSeedIdLength The MPL Seed Length. * */ - void SetSeedLength(SeedLength aSeedLength) { mControl = static_cast((mControl & ~kSeedLengthMask) | aSeedLength); } + void SetSeedIdLength(SeedIdLength aSeedIdLength) { mControl = static_cast((mControl & ~kSeedIdLengthMask) | aSeedIdLength); } /** * This method indicates whether or not the MPL M flag is set. @@ -145,33 +145,281 @@ public: * This method sets the MPL Sequence value. * * @param[in] aSequence The MPL Sequence value. + * */ void SetSequence(uint8_t aSequence) { mSequence = aSequence; } /** - * This method returns the MPL Seed value. + * This method returns the MPL Seed Id value. * - * @returns The MPL Seed value. + * @returns The MPL Seed Id value. * */ - uint16_t GetSeed() const { return HostSwap16(mSeed); } + uint16_t GetSeedId() const { return HostSwap16(mSeedId); } /** - * This method sets the MPL Seed value. + * This method sets the MPL Seed Id value. + * + * @param[in] aSeedId The MPL Seed Id value. * - * @param[in] aSeed The MPL Seed value. */ - void SetSeed(uint16_t aSeed) { mSeed = HostSwap16(aSeed); } + void SetSeedId(uint16_t aSeedId) { mSeedId = HostSwap16(aSeedId); } private: enum { - kSeedLengthMask = 3 << 6, + kSeedIdLengthMask = 3 << 6, kMaxFlag = 1 << 5 }; uint8_t mControl; uint8_t mSequence; - uint16_t mSeed; + uint16_t mSeedId; +} OT_TOOL_PACKED_END; + +/** + * This class represents an MPL's Seed Set entry. + * + */ +class MplSeedEntry +{ +public: + /** + * This method returns the MPL Seed Id value. + * + * @returns The MPL Seed Id value. + * + */ + uint16_t GetSeedId() const { return mSeedId; } + + /** + * This method sets the MPL Seed Id value. + * + * @param[in] aSeedId The MPL Seed Id value. + * + */ + void SetSeedId(uint16_t aSeedId) { mSeedId = aSeedId; } + + /** + * This method returns the MPL Sequence value. + * + * @returns The MPL Sequence value. + * + */ + uint8_t GetSequence() const { return mSequence; } + + /** + * This method sets the MPL Sequence value. + * + * @param[in] aSequence The MPL Sequence value. + * + */ + void SetSequence(uint8_t aSequence) { mSequence = aSequence; } + + /** + * This method returns the MPL Seed Set entry's remaining lifetime. + * + * @returns The MPL Seed Set entry's remaining lifetime. + * + */ + uint8_t GetLifetime() const { return mLifetime; } + + /** + * This method sets the remaining lifetime of the Seed Set entry. + * + * @param[in] aLifetime The remaining lifetime of the Seed Set entry. + * + */ + void SetLifetime(uint8_t aLifetime) { mLifetime = aLifetime; } + +private: + uint16_t mSeedId; + uint8_t mSequence; + uint8_t mLifetime; +}; + +/** + * This class represents metadata required for MPL retransmissions. + * + */ +OT_TOOL_PACKED_BEGIN +class MplBufferedMessageMetadata +{ +public: + /** + * Default constructor for the object. + * + */ + MplBufferedMessageMetadata(void): + mSeedId(0), + mSequence(0), + mTransmissionCount(0), + mTransmissionTime(0), + mIntervalOffset(0) { + }; + + /** + * This method appends MPL Buffered Message metadata to the message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully appended the bytes. + * @retval kThreadError_NoBufs Insufficient available buffers to grow the message. + * + */ + ThreadError AppendTo(Message &aMessage) const { + return aMessage.Append(this, sizeof(*this)); + }; + + /** + * This method reads request data from the message. + * + * @param[in] aMessage A reference to the message. + * + * @returns The number of bytes that have been read. + * + */ + uint16_t ReadFrom(const Message &aMessage) { + return aMessage.Read(aMessage.GetLength() - sizeof(*this), sizeof(*this), this); + }; + + /** + * This method removes MPL Buffered Messsage metadata from the message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully removed the header. + * + */ + ThreadError RemoveFrom(Message &aMessage) { + return aMessage.SetLength(aMessage.GetLength() - sizeof(*this)); + }; + + /** + * This method updates MPL Buffered Messsage metadata in the message. + * + * @param[in] aMessage A reference to the message. + * + * @returns The number of bytes that have been updated. + * + */ + int UpdateIn(Message &aMessage) const { + return aMessage.Write(aMessage.GetLength() - sizeof(*this), sizeof(*this), this); + } + + /** + * This method checks if the message shall be sent before the given time. + * + * @param[in] aTime A time to compare. + * + * @retval TRUE If the message shall be sent before the given time. + * @retval FALSE Otherwise. + */ + bool IsEarlier(uint32_t aTime) const { return (static_cast(aTime - mTransmissionTime) > 0); }; + + /** + * This method checks if the message shall be sent after the given time. + * + * @param[in] aTime A time to compare. + * + * @retval TRUE If the message shall be sent after the given time. + * @retval FALSE Otherwise. + */ + bool IsLater(uint32_t aTime) const { return (static_cast(aTime - mTransmissionTime) < 0); }; + + /** + * This method returns the MPL Seed Id value. + * + * @returns The MPL Seed Id value. + * + */ + uint16_t GetSeedId() const { return mSeedId; } + + /** + * This method sets the MPL Seed Id value. + * + * @param[in] aSeedId The MPL Seed Id value. + * + */ + void SetSeedId(uint16_t aSeedId) { mSeedId = aSeedId; } + + /** + * This method returns the MPL Sequence value. + * + * @returns The MPL Sequence value. + * + */ + uint8_t GetSequence() const { return mSequence; } + + /** + * This method sets the MPL Sequence value. + * + * @param[in] aSequence The MPL Sequence value. + * + */ + void SetSequence(uint8_t aSequence) { mSequence = aSequence; } + + /** + * This method returns the number of already preformed transmissions. + * + * @returns The number of already preformed transmissions. + * + */ + uint8_t GetTransmissionCount() const { return mTransmissionCount; } + + /** + * This method sets the number of already performed transmissions. + * + * @param[in] aTransmissionCount The number of already performed transmissions. + * + */ + void SetTransmissionCount(uint8_t aTransmissionCount) { mTransmissionCount = aTransmissionCount; } + + /** + * This method returns the transmission timestamp of the message. + * + * @returns The transmission timestamp of the message. + * + */ + uint32_t GetTransmissionTime() const { return mTransmissionTime; } + + /** + * This method sets the transmission timestamp of the message. + * + * @param[in] aTransmissionTime The transmission timestamp of the message. + * + */ + void SetTransmissionTime(uint32_t aTransmissionTime) { mTransmissionTime = aTransmissionTime; } + + /** + * This method returns the offset from the transmission time to the end of trickle interval. + * + * @returns The offset from the the transmission time to the end of trickle interval. + * + */ + uint32_t GetIntervalOffset() const { return mIntervalOffset; } + + /** + * This method sets the offset from the transmission time to the end of trickle interval. + * + * @param[in] aIntervalOffset The offset from the the transmission time to the end of trickle interval. + * + */ + void SetIntervalOffset(uint8_t aIntervalOffset) { mIntervalOffset = aIntervalOffset; } + + /** + * This method generates the next transmission time for the MPL Data Message. + * + * @param[in] aCurrentTime Current time (in milliseconds). + * @param[in] aInterval The current interval size (in milliseconds). + */ + void GenerateNextTransmissionTime(uint32_t aCurrentTime, uint8_t aInterval); + +private: + uint16_t mSeedId; + uint8_t mSequence; + uint8_t mTransmissionCount; + uint32_t mTransmissionTime; + uint8_t mIntervalOffset; } OT_TOOL_PACKED_END; /** @@ -199,65 +447,97 @@ public: void InitOption(OptionMpl &aOption, const Address &aAddress); /** - * This method processes an MPL option. + * This method processes an MPL option. When the MPL module acts as an MPL Forwarder + * it disseminates MPL Data Message using Trickle timer expirations. When acts as an + * MPL Seed it allows to send the first MPL Data Message directly, then sets up Trickle + * timer expirations for subsequent retransmissions. * - * @param[in] aMessage A reference to the message. - * @param[in] aAddress A reference to the IPv6 Source Address. - * @param[out] aForward TRUE if this message should be forwarded, FALSE otherwise. + * @param[in] aMessage A reference to the message. + * @param[in] aAddress A reference to the IPv6 Source Address. + * @param[in] aIsOutbound TRUE if this message was locally generated, FALSE otherwise. * * @retval kThreadError_None Successfully processed the MPL option. * @retval kThreadError_Drop The MPL message is a duplicate and should be dropped. * */ - ThreadError ProcessOption(const Message &aMessage, const Address &aAddress, bool &aForward); + ThreadError ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound); /** - * This method returns the MPL Seed value. + * This method returns the MPL Seed Id value. * - * @returns The MPL Seed value. + * @returns The MPL Seed Id value. * */ - uint16_t GetSeed() const { return mSeed; } + uint16_t GetSeedId() const { return mSeedId; } /** - * This method sets the MPL Seed value. + * This method sets the MPL Seed Id value. + * + * @param[in] aSeedId The MPL Seed Id value. * - * @param[in] aSeed The MPL Seed value. */ - void SetSeed(uint16_t aSeed) { mSeed = aSeed; } + void SetSeedId(uint16_t aSeedId) { mSeedId = aSeedId; } /** - * This method sets IPv6 matching address, that allows to elide seed-id. + * This method gets the MPL number of Trickle timer expirations that occur before + * terminating the Trickle algorithm's retransmission of a given MPL Data Message. + * + * @returns The MPL number of Trickle timer expirations. + * + */ + uint8_t GetTimerExpirations() const { return mTimerExpirations; } + + /** + * This method sets the MPL number of Trickle timer expirations that occur before + * terminating the Trickle algorithm's retransmission of a given MPL Data Message. + * + * @param[in] aTimerExpirations The number of Trickle timer expirations. + * + */ + void SetTimerExpirations(uint8_t aTimerExpirations) { mTimerExpirations = aTimerExpirations; } + + /** + * This method sets the IPv6 matching address, that allows to elide MPL Seed Id. + * + * @param[in] aAddress The reference to the IPv6 matching address. * - * @param[in] aAddress The reference to IPv6 matching address. */ void SetMatchingAddress(const Address &aAddress) { mMatchingAddress = &aAddress; } - private: enum { - kNumEntries = OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES, - kLifetime = OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME, + kNumSeedEntries = OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES, + kSeedEntryLifetime = OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRY_LIFETIME, + kSeedEntryLifetimeDt = 1000, + kDataMessageInterval = 64 }; - static void HandleTimer(void *context); - void HandleTimer(); + ThreadError UpdateSeedSet(uint16_t aSeedId, uint8_t aSequence); + void UpdateBufferedSet(uint16_t aSeedId, uint8_t aSequence); + void AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSequence, bool aIsOutbound); - Timer mTimer; + static void HandleSeedSetTimer(void *aContext); + void HandleSeedSetTimer(); + + static void HandleRetransmissionTimer(void *aContext); + void HandleRetransmissionTimer(); + + Ip6 &mIp6; + + Timer mSeedSetTimer; + Timer mRetransmissionTimer; + + uint8_t mTimerExpirations; uint8_t mSequence; - uint16_t mSeed; + uint16_t mSeedId; const Address *mMatchingAddress; - struct MplEntry - { - uint16_t mSeed; - uint8_t mSequence; - uint8_t mLifetime; - }; - MplEntry mEntries[kNumEntries]; + MplSeedEntry mSeedSet[kNumSeedEntries]; + MessageQueue mBufferedMessageSet; }; + /** * @} * diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 203c815f6..7648a1c11 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -136,24 +136,24 @@ #endif // OPENTHREAD_CONFIG_6LOWPAN_REASSEMBLY_TIMEOUT /** - * @def OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES + * @def OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES * - * The number of MPL cache entries for duplicate detection. + * The number of MPL Seed Set entries for duplicate detection. * */ -#ifndef OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES -#define OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES 32 -#endif // OPENTHREAD_CONFIG_MPL_CACHE_ENTRIES +#ifndef OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES +#define OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES 32 +#endif // OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES /** - * @def OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME + * @def OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRY_LIFETIME * - * The MPL cache entry lifetime in seconds. + * The MPL Seed Set entry lifetime in seconds. * */ -#ifndef OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME -#define OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME 5 -#endif // OPENTHREAD_CONFIG_MPL_CACHE_ENTRY_LIFETIME +#ifndef OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRY_LIFETIME +#define OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRY_LIFETIME 5 +#endif // OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRY_LIFETIME /** * @def OPENTHREAD_CONFIG_JOINER_UDP_PORT @@ -360,4 +360,3 @@ #endif // OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM #endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_ - diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index da05dac93..4c7bb1da2 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -390,6 +390,7 @@ ThreadError Mle::SetStateDetached(void) mMesh.SetRxOnWhenIdle(true); mMleRouter.HandleDetachStart(); mNetif.GetIp6().SetForwardingEnabled(false); + mNetif.GetIp6().mMpl.SetTimerExpirations(0); otLogInfoMle("Mode -> Detached"); return kThreadError_None; @@ -423,6 +424,7 @@ ThreadError Mle::SetStateChild(uint16_t aRloc16) mNetif.GetNetworkDataLocal().ClearResubmitDelayTimer(); mNetif.GetIp6().SetForwardingEnabled(false); + mNetif.GetIp6().mMpl.SetTimerExpirations(kMplChildDataMessageTimerExpirations); if (mPreviousPanId != Mac::kPanIdBroadcast && (mDeviceMode & ModeTlv::kModeFFD)) { @@ -589,7 +591,7 @@ ThreadError Mle::SetRloc16(uint16_t aRloc16) } mMac.SetShortAddress(aRloc16); - mNetif.GetIp6().mMpl.SetSeed(aRloc16); + mNetif.GetIp6().mMpl.SetSeedId(aRloc16); return kThreadError_None; } diff --git a/src/core/thread/mle_constants.hpp b/src/core/thread/mle_constants.hpp index 851b2c097..67b81d899 100644 --- a/src/core/thread/mle_constants.hpp +++ b/src/core/thread/mle_constants.hpp @@ -118,6 +118,16 @@ enum kMinAssignedLinkMargin0 = 0x00, ///< minimal link margin for LQI 0 (0 - 2) }; +/** + * Multicast Forwarding Constants + * + */ +enum +{ + kMplChildDataMessageTimerExpirations = 0, ///< Number of MPL retransmissions for Children. + kMplRouterDataMessageTimerExpirations = 2, ///< Number of MPL retransmissions for Routers. +}; + } // namespace Mle /** diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 4dfa3b11b..185383b60 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -359,6 +359,7 @@ ThreadError MleRouter::SetStateRouter(uint16_t aRloc16) mNetworkData.Stop(); mStateUpdateTimer.Start(kStateUpdatePeriod); mNetif.GetIp6().SetForwardingEnabled(true); + mNetif.GetIp6().mMpl.SetTimerExpirations(kMplRouterDataMessageTimerExpirations); otLogInfoMle("Mode -> Router"); return kThreadError_None; @@ -386,6 +387,7 @@ ThreadError MleRouter::SetStateLeader(uint16_t aRloc16) mCoapServer.AddResource(mAddressSolicit); mCoapServer.AddResource(mAddressRelease); mNetif.GetIp6().SetForwardingEnabled(true); + mNetif.GetIp6().mMpl.SetTimerExpirations(kMplRouterDataMessageTimerExpirations); otLogInfoMle("Mode -> Leader %d", mLeaderData.GetPartitionId()); return kThreadError_None;