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 18:06:27 +02: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;
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<uint8_t>(header.GetNextHeader());
SuccessOrExit(error = HandleExtensionHeaders(message, nextHeader, receive));
SuccessOrExit(error = HandleExtensionHeaders(message, header, nextHeader, receive));
// process IPv6 Payload
if (receive)
+3 -3
View File
@@ -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;
+66
View File
@@ -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.
*
+28 -7
View File
@@ -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)
+42 -5
View File
@@ -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
{
+66 -2
View File
@@ -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<uint8_t>(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<uint8_t>(extHeader.GetNextHeader());
cur[0] = len;
cur++;
aMessage.Read(aMessage.GetOffset(), len, cur);
aMessage.MoveOffset(len);
aMessage.MoveOffset(len + padLength);
cur += len;
return static_cast<int>(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<int>(cur - aBuf) : -1;
+4
View File
@@ -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;
}