MPL: Seed-id field handling (#663)

* MPL: Fix seed-id field handling. Add Pad1 and PadN options.

This commit fixes issue #599 by allowing MPL module to elide its seed-id
as well as retrieve elided seed-id from RLOC.

Additionally Pad options have been introduced and bug with incorrect
parsing of Pad1 option has been fixed.

* Lowpan: Introduce compression and decompression of Pad1 and PadN options.

This commit fixes issue #599.
This commit is contained in:
Łukasz Duda
2016-09-23 09:06:27 -07:00
committed by Jonathan Hui
parent a853ffc784
commit 465affadb9
7 changed files with 235 additions and 26 deletions
+26 -9
View File
@@ -128,11 +128,20 @@ ThreadError Ip6::AddMplOption(Message &message, Header &header, IpProto nextHead
ThreadError error = kThreadError_None; ThreadError error = kThreadError_None;
HopByHopHeader hbhHeader; HopByHopHeader hbhHeader;
OptionMpl mplOption; OptionMpl mplOption;
OptionPadN padOption;
hbhHeader.SetNextHeader(nextHeader); hbhHeader.SetNextHeader(nextHeader);
hbhHeader.SetLength(0); hbhHeader.SetLength(0);
mMpl.InitOption(mplOption, HostSwap16(header.GetSource().mFields.m16[7])); mMpl.InitOption(mplOption, header.GetSource());
SuccessOrExit(error = message.Prepend(&mplOption, sizeof(mplOption)));
// 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))); SuccessOrExit(error = message.Prepend(&hbhHeader, sizeof(hbhHeader)));
header.SetPayloadLength(sizeof(hbhHeader) + sizeof(mplOption) + payloadLength); header.SetPayloadLength(sizeof(hbhHeader) + sizeof(mplOption) + payloadLength);
header.SetNextHeader(kProtoHopOpts); 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; ThreadError error = kThreadError_None;
HopByHopHeader hbhHeader; HopByHopHeader hbhHeader;
@@ -241,7 +250,7 @@ ThreadError Ip6::HandleOptions(Message &message)
switch (optionHeader.GetType()) switch (optionHeader.GetType())
{ {
case OptionMpl::kType: case OptionMpl::kType:
SuccessOrExit(error = mMpl.ProcessOption(message)); SuccessOrExit(error = mMpl.ProcessOption(message, header.GetSource()));
break; break;
default: default:
@@ -266,7 +275,15 @@ ThreadError Ip6::HandleOptions(Message &message)
break; break;
} }
message.MoveOffset(sizeof(optionHeader) + optionHeader.GetLength()); if (optionHeader.GetType() == OptionPad1::kType)
{
message.MoveOffset(sizeof(OptionPad1));
}
else
{
message.MoveOffset(sizeof(optionHeader) + optionHeader.GetLength());
}
} }
exit: exit:
@@ -289,7 +306,7 @@ exit:
return error; 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; ThreadError error = kThreadError_None;
ExtensionHeader extensionHeader; ExtensionHeader extensionHeader;
@@ -303,7 +320,7 @@ ThreadError Ip6::HandleExtensionHeaders(Message &message, uint8_t &nextHeader, b
switch (nextHeader) switch (nextHeader)
{ {
case kProtoHopOpts: case kProtoHopOpts:
SuccessOrExit(error = HandleOptions(message)); SuccessOrExit(error = HandleOptions(message, header));
break; break;
case kProtoFragment: case kProtoFragment:
@@ -311,7 +328,7 @@ ThreadError Ip6::HandleExtensionHeaders(Message &message, uint8_t &nextHeader, b
break; break;
case kProtoDstOpts: case kProtoDstOpts:
SuccessOrExit(error = HandleOptions(message)); SuccessOrExit(error = HandleOptions(message, header));
break; break;
case kProtoIp6: case kProtoIp6:
@@ -489,7 +506,7 @@ ThreadError Ip6::HandleDatagram(Message &message, Netif *netif, int8_t interface
// process IPv6 Extension Headers // process IPv6 Extension Headers
nextHeader = static_cast<uint8_t>(header.GetNextHeader()); nextHeader = static_cast<uint8_t>(header.GetNextHeader());
SuccessOrExit(error = HandleExtensionHeaders(message, nextHeader, receive)); SuccessOrExit(error = HandleExtensionHeaders(message, header, nextHeader, receive));
// process IPv6 Payload // process IPv6 Payload
if (receive) if (receive)
+3 -3
View File
@@ -326,6 +326,7 @@ public:
Routes mRoutes; Routes mRoutes;
Icmp mIcmp; Icmp mIcmp;
Udp mUdp; Udp mUdp;
Mpl mMpl;
MessagePool mMessagePool; MessagePool mMessagePool;
TaskletScheduler mTaskletScheduler; TaskletScheduler mTaskletScheduler;
@@ -336,14 +337,13 @@ private:
void HandleSendQueue(void); void HandleSendQueue(void);
ThreadError ProcessReceiveCallback(const Message &aMessage, const MessageInfo &aMessageInfo, uint8_t aIpProto); 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 HandleFragment(Message &message);
ThreadError AddMplOption(Message &message, Header &header, IpProto nextHeader, uint16_t payloadLength); 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 HandlePayload(Message &message, MessageInfo &messageInfo, uint8_t ipproto);
ThreadError ForwardMessage(Message &message, MessageInfo &messageInfo, uint8_t ipproto); ThreadError ForwardMessage(Message &message, MessageInfo &messageInfo, uint8_t ipproto);
Mpl mMpl;
bool mForwardingEnabled; bool mForwardingEnabled;
MessageQueue mSendQueue; MessageQueue mSendQueue;
+66
View File
@@ -387,6 +387,72 @@ private:
uint8_t mLength; uint8_t mLength;
} OT_TOOL_PACKED_END; } 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. * This class implements IPv6 Fragment Header generation and parsing.
* *
+28 -7
View File
@@ -40,31 +40,52 @@ namespace Thread {
namespace Ip6 { namespace Ip6 {
Mpl::Mpl(Ip6 &aIp6): Mpl::Mpl(Ip6 &aIp6):
mTimer(aIp6.mTimerScheduler, &Mpl::HandleTimer, this) mTimer(aIp6.mTimerScheduler, &Mpl::HandleTimer, this),
mMatchingAddress(NULL)
{ {
memset(mEntries, 0, sizeof(mEntries)); memset(mEntries, 0, sizeof(mEntries));
mSequence = 0; mSequence = 0;
mSeed = 0;
} }
void Mpl::InitOption(OptionMpl &aOption, uint16_t aSeed) void Mpl::InitOption(OptionMpl &aOption, const Address &aAddress)
{ {
aOption.Init(); aOption.Init();
aOption.SetSeedLength(OptionMpl::kSeedLength2);
aOption.SetSequence(mSequence++); 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; ThreadError error = kThreadError_None;
OptionMpl option; OptionMpl option;
MplEntry *entry = NULL; MplEntry *entry = NULL;
int8_t diff; int8_t diff;
VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(option), &option) == sizeof(option) && VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(option), &option) >= OptionMpl::kMinLength &&
option.GetLength() == sizeof(OptionMpl) - sizeof(OptionHeader), (option.GetSeedLength() == OptionMpl::kSeedLength0 ||
option.GetSeedLength() == OptionMpl::kSeedLength2),
error = kThreadError_Drop); 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++) for (int i = 0; i < kNumEntries; i++)
{ {
if (mEntries[i].mLifetime == 0) if (mEntries[i].mLifetime == 0)
+42 -5
View File
@@ -63,6 +63,7 @@ public:
enum enum
{ {
kType = 0x6d, /* 01 1 01101 */ kType = 0x6d, /* 01 1 01101 */
kMinLength = 2
}; };
/** /**
@@ -72,8 +73,18 @@ public:
void Init() { void Init() {
OptionHeader::SetType(kType); OptionHeader::SetType(kType);
OptionHeader::SetLength(sizeof(*this) - sizeof(OptionHeader)); 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. * MPL Seed lengths.
*/ */
@@ -156,7 +167,7 @@ private:
enum enum
{ {
kSeedLengthMask = 3 << 6, kSeedLengthMask = 3 << 6,
kMaxFlag = 1 << 5, kMaxFlag = 1 << 5
}; };
uint8_t mControl; uint8_t mControl;
uint8_t mSequence; uint8_t mSequence;
@@ -181,22 +192,46 @@ public:
/** /**
* This method initializes the MPL option. * This method initializes the MPL option.
* *
* @param[in] aOption A reference to the MPL header to initialize. * @param[in] aOption A reference to the MPL header to initialize.
* @param[in] aSeed The MPL Seed value to use. * @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. * This method processes an MPL option.
* *
* @param[in] aMessage A reference to the message. * @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_None Successfully processed the MPL option.
* @retval kThreadError_Drop The MPL message is a duplicate and should be dropped. * @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: private:
enum enum
@@ -210,6 +245,8 @@ private:
Timer mTimer; Timer mTimer;
uint8_t mSequence; uint8_t mSequence;
uint16_t mSeed;
const Address *mMatchingAddress;
struct MplEntry struct MplEntry
{ {
+66 -2
View File
@@ -384,14 +384,16 @@ exit:
int Lowpan::CompressExtensionHeader(Message &aMessage, uint8_t *aBuf, uint8_t &aNextHeader) int Lowpan::CompressExtensionHeader(Message &aMessage, uint8_t *aBuf, uint8_t &aNextHeader)
{ {
Ip6::ExtensionHeader extHeader; Ip6::ExtensionHeader extHeader;
Ip6::OptionHeader optionHeader;
uint8_t *cur = aBuf; uint8_t *cur = aBuf;
uint8_t len; uint8_t len;
uint8_t padLength = 0;
uint16_t offset;
aMessage.Read(aMessage.GetOffset(), sizeof(extHeader), &extHeader); aMessage.Read(aMessage.GetOffset(), sizeof(extHeader), &extHeader);
aMessage.MoveOffset(sizeof(extHeader)); aMessage.MoveOffset(sizeof(extHeader));
cur[0] = kExtHdrDispatch | kExtHdrEidHbh; cur[0] = kExtHdrDispatch | kExtHdrEidHbh;
aNextHeader = static_cast<uint8_t>(extHeader.GetNextHeader());
switch (extHeader.GetNextHeader()) switch (extHeader.GetNextHeader())
{ {
@@ -408,11 +410,49 @@ int Lowpan::CompressExtensionHeader(Message &aMessage, uint8_t *aBuf, uint8_t &a
cur++; cur++;
len = (extHeader.GetLength() + 1) * 8 - sizeof(extHeader); 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<uint8_t>(extHeader.GetNextHeader());
cur[0] = len; cur[0] = len;
cur++; cur++;
aMessage.Read(aMessage.GetOffset(), len, cur); aMessage.Read(aMessage.GetOffset(), len, cur);
aMessage.MoveOffset(len); aMessage.MoveOffset(len + padLength);
cur += len; cur += len;
return static_cast<int>(cur - aBuf); return static_cast<int>(cur - aBuf);
@@ -756,6 +796,9 @@ int Lowpan::DecompressExtensionHeader(Message &aMessage, const uint8_t *aBuf, ui
uint8_t len; uint8_t len;
Ip6::IpProto nextHeader; Ip6::IpProto nextHeader;
uint8_t ctl = cur[0]; uint8_t ctl = cur[0];
uint8_t padLength;
Ip6::OptionPad1 optionPad1;
Ip6::OptionPadN optionPadN;
cur++; cur++;
@@ -788,6 +831,27 @@ int Lowpan::DecompressExtensionHeader(Message &aMessage, const uint8_t *aBuf, ui
aMessage.MoveOffset(len); aMessage.MoveOffset(len);
cur += 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: exit:
(void)aBufLength; (void)aBufLength;
return (error == kThreadError_None) ? static_cast<int>(cur - aBuf) : -1; return (error == kThreadError_None) ? static_cast<int>(cur - aBuf) : -1;
+4
View File
@@ -131,6 +131,9 @@ Mle::Mle(ThreadNetif &aThreadNetif) :
mMeshLocal16.mPreferredLifetime = 0xffffffff; mMeshLocal16.mPreferredLifetime = 0xffffffff;
mMeshLocal16.mValidLifetime = 0xffffffff; mMeshLocal16.mValidLifetime = 0xffffffff;
// Store RLOC address reference in MPL module.
mNetif.GetIp6().mMpl.SetMatchingAddress(mMeshLocal16.GetAddress());
// link-local all thread nodes // link-local all thread nodes
mLinkLocalAllThreadNodes.GetAddress().mFields.m16[0] = HostSwap16(0xff32); mLinkLocalAllThreadNodes.GetAddress().mFields.m16[0] = HostSwap16(0xff32);
mLinkLocalAllThreadNodes.GetAddress().mFields.m16[6] = HostSwap16(0x0000); mLinkLocalAllThreadNodes.GetAddress().mFields.m16[6] = HostSwap16(0x0000);
@@ -522,6 +525,7 @@ ThreadError Mle::SetRloc16(uint16_t aRloc16)
} }
mMac.SetShortAddress(aRloc16); mMac.SetShortAddress(aRloc16);
mNetif.GetIp6().mMpl.SetSeed(aRloc16);
return kThreadError_None; return kThreadError_None;
} }