diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index b08788486..87cb61f7b 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -128,11 +128,20 @@ ThreadError Ip6::AddMplOption(Message &message, Header &header, IpProto nextHead ThreadError error = kThreadError_None; HopByHopHeader hbhHeader; OptionMpl mplOption; + OptionPadN padOption; hbhHeader.SetNextHeader(nextHeader); hbhHeader.SetLength(0); - mMpl.InitOption(mplOption, HostSwap16(header.GetSource().mFields.m16[7])); - SuccessOrExit(error = message.Prepend(&mplOption, sizeof(mplOption))); + mMpl.InitOption(mplOption, header.GetSource()); + + // Mpl option may require two bytes padding. + if ((mplOption.GetTotalLength() + sizeof(hbhHeader)) % 8) + { + padOption.Init(2); + SuccessOrExit(error = message.Prepend(&padOption, padOption.GetTotalLength())); + } + + SuccessOrExit(error = message.Prepend(&mplOption, mplOption.GetTotalLength())); SuccessOrExit(error = message.Prepend(&hbhHeader, sizeof(hbhHeader))); header.SetPayloadLength(sizeof(hbhHeader) + sizeof(mplOption) + payloadLength); header.SetNextHeader(kProtoHopOpts); @@ -222,7 +231,7 @@ void Ip6::HandleSendQueue(void) } } -ThreadError Ip6::HandleOptions(Message &message) +ThreadError Ip6::HandleOptions(Message &message, Header &header) { ThreadError error = kThreadError_None; HopByHopHeader hbhHeader; @@ -241,7 +250,7 @@ ThreadError Ip6::HandleOptions(Message &message) switch (optionHeader.GetType()) { case OptionMpl::kType: - SuccessOrExit(error = mMpl.ProcessOption(message)); + SuccessOrExit(error = mMpl.ProcessOption(message, header.GetSource())); break; default: @@ -266,7 +275,15 @@ ThreadError Ip6::HandleOptions(Message &message) break; } - message.MoveOffset(sizeof(optionHeader) + optionHeader.GetLength()); + if (optionHeader.GetType() == OptionPad1::kType) + { + message.MoveOffset(sizeof(OptionPad1)); + } + else + { + message.MoveOffset(sizeof(optionHeader) + optionHeader.GetLength()); + } + } exit: @@ -289,7 +306,7 @@ exit: return error; } -ThreadError Ip6::HandleExtensionHeaders(Message &message, uint8_t &nextHeader, bool receive) +ThreadError Ip6::HandleExtensionHeaders(Message &message, Header &header, uint8_t &nextHeader, bool receive) { ThreadError error = kThreadError_None; ExtensionHeader extensionHeader; @@ -303,7 +320,7 @@ ThreadError Ip6::HandleExtensionHeaders(Message &message, uint8_t &nextHeader, b switch (nextHeader) { case kProtoHopOpts: - SuccessOrExit(error = HandleOptions(message)); + SuccessOrExit(error = HandleOptions(message, header)); break; case kProtoFragment: @@ -311,7 +328,7 @@ ThreadError Ip6::HandleExtensionHeaders(Message &message, uint8_t &nextHeader, b break; case kProtoDstOpts: - SuccessOrExit(error = HandleOptions(message)); + SuccessOrExit(error = HandleOptions(message, header)); break; case kProtoIp6: @@ -489,7 +506,7 @@ ThreadError Ip6::HandleDatagram(Message &message, Netif *netif, int8_t interface // process IPv6 Extension Headers nextHeader = static_cast(header.GetNextHeader()); - SuccessOrExit(error = HandleExtensionHeaders(message, nextHeader, receive)); + SuccessOrExit(error = HandleExtensionHeaders(message, header, nextHeader, receive)); // process IPv6 Payload if (receive) diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index e64788b7a..d99f1612a 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -326,6 +326,7 @@ public: Routes mRoutes; Icmp mIcmp; Udp mUdp; + Mpl mMpl; MessagePool mMessagePool; TaskletScheduler mTaskletScheduler; @@ -336,14 +337,13 @@ private: void HandleSendQueue(void); ThreadError ProcessReceiveCallback(const Message &aMessage, const MessageInfo &aMessageInfo, uint8_t aIpProto); - ThreadError HandleExtensionHeaders(Message &message, uint8_t &nextHeader, bool receive); + ThreadError HandleExtensionHeaders(Message &message, Header &header, uint8_t &nextHeader, bool receive); ThreadError HandleFragment(Message &message); ThreadError AddMplOption(Message &message, Header &header, IpProto nextHeader, uint16_t payloadLength); - ThreadError HandleOptions(Message &message); + ThreadError HandleOptions(Message &message, Header &header); ThreadError HandlePayload(Message &message, MessageInfo &messageInfo, uint8_t ipproto); ThreadError ForwardMessage(Message &message, MessageInfo &messageInfo, uint8_t ipproto); - Mpl mMpl; bool mForwardingEnabled; MessageQueue mSendQueue; diff --git a/src/core/net/ip6_headers.hpp b/src/core/net/ip6_headers.hpp index 16ff0f8cc..6cfae04cd 100644 --- a/src/core/net/ip6_headers.hpp +++ b/src/core/net/ip6_headers.hpp @@ -387,6 +387,72 @@ private: uint8_t mLength; } OT_TOOL_PACKED_END; +/** + * This class implements IPv6 PadN Option generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class OptionPadN : public OptionHeader +{ +public: + enum + { + kType = 0x01, ///< PadN type + kData = 0x00, ///< PadN specific data + kMaxLength = 0x05 ///< Maximum length of PadN option data + }; + + /** + * This method initializes the PadN header. + * + * @param[in] aPadLength The length of needed padding. Allowed value from + * range 2-7. + * + */ + void Init(uint8_t aPadLength) { + OptionHeader::SetType(kType); + OptionHeader::SetLength(aPadLength - sizeof(OptionHeader)); + memset(mPad, kData, aPadLength - sizeof(OptionHeader)); + } + + /** + * This method returns the total IPv6 Option Length value including option + * header. + * + * @returns The total IPv6 Option Length. + * + */ + uint8_t GetTotalLength() const { return OptionHeader::GetLength() + sizeof(OptionHeader); } + +private: + uint8_t mPad[kMaxLength]; +} OT_TOOL_PACKED_END; + +/** + * This class implements IPv6 Pad1 Option generation and parsing. Pad1 does not + * followdefault option header structure. + * + */ +OT_TOOL_PACKED_BEGIN +class OptionPad1 +{ +public: + enum + { + kType = 0x00 + }; + + /** + * This method initializes the Pad1 header. + * + */ + void Init() { mType = kType; } + +private: + uint8_t mType; +} OT_TOOL_PACKED_END; + + /** * This class implements IPv6 Fragment Header generation and parsing. * diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index 2c09b9a7f..f8022cf72 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -40,31 +40,52 @@ namespace Thread { namespace Ip6 { Mpl::Mpl(Ip6 &aIp6): - mTimer(aIp6.mTimerScheduler, &Mpl::HandleTimer, this) + mTimer(aIp6.mTimerScheduler, &Mpl::HandleTimer, this), + mMatchingAddress(NULL) { memset(mEntries, 0, sizeof(mEntries)); mSequence = 0; + mSeed = 0; } -void Mpl::InitOption(OptionMpl &aOption, uint16_t aSeed) +void Mpl::InitOption(OptionMpl &aOption, const Address &aAddress) { aOption.Init(); - aOption.SetSeedLength(OptionMpl::kSeedLength2); aOption.SetSequence(mSequence++); - aOption.SetSeed(aSeed); + + // Check if Seed can be elided. + if (mMatchingAddress && aAddress == *mMatchingAddress) + { + aOption.SetSeedLength(OptionMpl::kSeedLength0); + + // Decrease default option length. + aOption.SetLength(aOption.GetLength() - sizeof(mSeed)); + } + else + { + aOption.SetSeedLength(OptionMpl::kSeedLength2); + aOption.SetSeed(mSeed); + } } -ThreadError Mpl::ProcessOption(const Message &aMessage) +ThreadError Mpl::ProcessOption(const Message &aMessage, const Address &aAddress) { ThreadError error = kThreadError_None; OptionMpl option; MplEntry *entry = NULL; int8_t diff; - VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(option), &option) == sizeof(option) && - option.GetLength() == sizeof(OptionMpl) - sizeof(OptionHeader), + 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) + { + // 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) diff --git a/src/core/net/ip6_mpl.hpp b/src/core/net/ip6_mpl.hpp index 40755cb27..19e0ac190 100644 --- a/src/core/net/ip6_mpl.hpp +++ b/src/core/net/ip6_mpl.hpp @@ -63,6 +63,7 @@ public: enum { kType = 0x6d, /* 01 1 01101 */ + kMinLength = 2 }; /** @@ -72,8 +73,18 @@ public: void Init() { OptionHeader::SetType(kType); OptionHeader::SetLength(sizeof(*this) - sizeof(OptionHeader)); + mControl = 0; } + /** + * This method returns the total MPL Option length value including option + * header. + * + * @returns The total IPv6 Option Length. + * + */ + uint8_t GetTotalLength() const { return OptionHeader::GetLength() + sizeof(OptionHeader); } + /** * MPL Seed lengths. */ @@ -156,7 +167,7 @@ private: enum { kSeedLengthMask = 3 << 6, - kMaxFlag = 1 << 5, + kMaxFlag = 1 << 5 }; uint8_t mControl; uint8_t mSequence; @@ -181,22 +192,46 @@ public: /** * This method initializes the MPL option. * - * @param[in] aOption A reference to the MPL header to initialize. - * @param[in] aSeed The MPL Seed value to use. + * @param[in] aOption A reference to the MPL header to initialize. + * @param[in] aAddress A reference to the IPv6 Source Address. * */ - void InitOption(OptionMpl &aOption, uint16_t aSeed); + void InitOption(OptionMpl &aOption, const Address &aAddress); /** * This method processes an MPL option. * * @param[in] aMessage A reference to the message. + * @param[in] aAddress A reference to the IPv6 Source Address. * * @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); + ThreadError ProcessOption(const Message &aMessage, const Address &aAddress); + + /** + * This method returns the MPL Seed value. + * + * @returns The MPL Seed value. + * + */ + uint16_t GetSeed() const { return mSeed; } + + /** + * This method sets the MPL Seed value. + * + * @param[in] aSeed The MPL Seed value. + */ + void SetSeed(uint16_t aSeed) { mSeed = aSeed; } + + /** + * This method sets IPv6 matching address, that allows to elide seed-id. + * + * @param[in] aAddress The reference to IPv6 matching address. + */ + void SetMatchingAddress(const Address &aAddress) { mMatchingAddress = &aAddress; } + private: enum @@ -210,6 +245,8 @@ private: Timer mTimer; uint8_t mSequence; + uint16_t mSeed; + const Address *mMatchingAddress; struct MplEntry { diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp index be572cdb5..22f900dee 100644 --- a/src/core/thread/lowpan.cpp +++ b/src/core/thread/lowpan.cpp @@ -384,14 +384,16 @@ exit: int Lowpan::CompressExtensionHeader(Message &aMessage, uint8_t *aBuf, uint8_t &aNextHeader) { Ip6::ExtensionHeader extHeader; + Ip6::OptionHeader optionHeader; uint8_t *cur = aBuf; uint8_t len; + uint8_t padLength = 0; + uint16_t offset; aMessage.Read(aMessage.GetOffset(), sizeof(extHeader), &extHeader); aMessage.MoveOffset(sizeof(extHeader)); cur[0] = kExtHdrDispatch | kExtHdrEidHbh; - aNextHeader = static_cast(extHeader.GetNextHeader()); switch (extHeader.GetNextHeader()) { @@ -408,11 +410,49 @@ int Lowpan::CompressExtensionHeader(Message &aMessage, uint8_t *aBuf, uint8_t &a cur++; len = (extHeader.GetLength() + 1) * 8 - sizeof(extHeader); + + // RFC 6282 says: "IPv6 Hop-by-Hop and Destination Options Headers may use a trailing + // Pad1 or PadN to achieve 8-octet alignment. When there is a single trailing Pad1 or PadN + // option of 7 octets or less and the containing header is a multiple of 8 octets, the trailing + // Pad1 or PadN option MAY be elided by the compressor." + if (aNextHeader == Ip6::kProtoHopOpts || aNextHeader == Ip6::kProtoDstOpts) + { + offset = aMessage.GetOffset(); + + while (offset < len + aMessage.GetOffset()) + { + aMessage.Read(offset, sizeof(optionHeader), &optionHeader); + + if (optionHeader.GetType() == Ip6::OptionPad1::kType) + { + offset += sizeof(Ip6::OptionPad1); + } + else + { + offset += sizeof(optionHeader) + optionHeader.GetLength(); + } + } + + // Check if the last option can be compressed. + if (optionHeader.GetType() == Ip6::OptionPad1::kType) + { + padLength = sizeof(Ip6::OptionPad1); + } + else if (optionHeader.GetType() == Ip6::OptionPadN::kType) + { + padLength = sizeof(optionHeader) + optionHeader.GetLength(); + } + + len -= padLength; + } + + aNextHeader = static_cast(extHeader.GetNextHeader()); + cur[0] = len; cur++; aMessage.Read(aMessage.GetOffset(), len, cur); - aMessage.MoveOffset(len); + aMessage.MoveOffset(len + padLength); cur += len; return static_cast(cur - aBuf); @@ -756,6 +796,9 @@ int Lowpan::DecompressExtensionHeader(Message &aMessage, const uint8_t *aBuf, ui uint8_t len; Ip6::IpProto nextHeader; uint8_t ctl = cur[0]; + uint8_t padLength; + Ip6::OptionPad1 optionPad1; + Ip6::OptionPadN optionPadN; cur++; @@ -788,6 +831,27 @@ int Lowpan::DecompressExtensionHeader(Message &aMessage, const uint8_t *aBuf, ui aMessage.MoveOffset(len); cur += len; + // The RFC6282 says: "The trailing Pad1 or PadN option MAY be elided by the compressor. + // A decompressor MUST ensure that the containing header is padded out to a multiple of 8 octets + // in length, using a Pad1 or PadN option if necessary." + padLength = 8 - ((len + sizeof(hdr)) & 0x07); + + if (padLength != 8) + { + if (padLength == 1) + { + optionPad1.Init(); + SuccessOrExit(error = aMessage.Append(&optionPad1, padLength)); + } + else + { + optionPadN.Init(padLength); + SuccessOrExit(error = aMessage.Append(&optionPadN, padLength)); + } + + aMessage.MoveOffset(padLength); + } + exit: (void)aBufLength; return (error == kThreadError_None) ? static_cast(cur - aBuf) : -1; diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 1b10b0624..204b5086d 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -131,6 +131,9 @@ Mle::Mle(ThreadNetif &aThreadNetif) : mMeshLocal16.mPreferredLifetime = 0xffffffff; mMeshLocal16.mValidLifetime = 0xffffffff; + // Store RLOC address reference in MPL module. + mNetif.GetIp6().mMpl.SetMatchingAddress(mMeshLocal16.GetAddress()); + // link-local all thread nodes mLinkLocalAllThreadNodes.GetAddress().mFields.m16[0] = HostSwap16(0xff32); mLinkLocalAllThreadNodes.GetAddress().mFields.m16[6] = HostSwap16(0x0000); @@ -522,6 +525,7 @@ ThreadError Mle::SetRloc16(uint16_t aRloc16) } mMac.SetShortAddress(aRloc16); + mNetif.GetIp6().mMpl.SetSeed(aRloc16); return kThreadError_None; }