mirror of
https://github.com/espressif/openthread.git
synced 2026-07-11 14:50:18 +00:00
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:
committed by
Jonathan Hui
parent
a853ffc784
commit
465affadb9
+26
-9
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user