mirror of
https://github.com/espressif/openthread.git
synced 2026-08-15 23:27:47 +00:00
[ip6] use OffsetRange for parsing options in extension headers (#10466)
This commit updates `Ip6` and `Lowpan` to use `OffsetRange` when iterating and parsing options in an HBH extension header. It also simplifies the `RemoveMplOption()` method by adding an `Action` enum, which determines how to remove the MPL option: whether to shrink the HBH header, fully remove the HBH header (if it contains no other options), or replace MPL option with padding.
This commit is contained in:
+73
-52
@@ -309,30 +309,35 @@ exit:
|
||||
|
||||
Error Ip6::RemoveMplOption(Message &aMessage)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
enum Action : uint8_t
|
||||
{
|
||||
kNoMplOption,
|
||||
kShrinkHbh,
|
||||
kRemoveHbh,
|
||||
kReplaceMplWithPad,
|
||||
};
|
||||
|
||||
Error error = kErrorNone;
|
||||
Action action = kNoMplOption;
|
||||
Header ip6Header;
|
||||
HopByHopHeader hbh;
|
||||
Option option;
|
||||
uint16_t offset;
|
||||
uint16_t endOffset;
|
||||
uint16_t mplOffset = 0;
|
||||
uint8_t mplLength = 0;
|
||||
bool remove = false;
|
||||
OffsetRange offsetRange;
|
||||
OffsetRange mplOffsetRange;
|
||||
PadOption padOption;
|
||||
|
||||
offsetRange.InitFromMessageFullLength(aMessage);
|
||||
|
||||
IgnoreError(aMessage.Read(offsetRange, ip6Header));
|
||||
offsetRange.AdvanceOffset(sizeof(ip6Header));
|
||||
|
||||
offset = 0;
|
||||
IgnoreError(aMessage.Read(offset, ip6Header));
|
||||
offset += sizeof(ip6Header);
|
||||
VerifyOrExit(ip6Header.GetNextHeader() == kProtoHopOpts);
|
||||
|
||||
IgnoreError(aMessage.Read(offset, hbh));
|
||||
endOffset = offset + hbh.GetSize();
|
||||
VerifyOrExit(aMessage.GetLength() >= endOffset, error = kErrorParse);
|
||||
SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbh));
|
||||
|
||||
offset += sizeof(hbh);
|
||||
|
||||
for (; offset < endOffset; offset += option.GetSize())
|
||||
for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize()))
|
||||
{
|
||||
IgnoreError(option.ParseFrom(aMessage, offset, endOffset));
|
||||
SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange));
|
||||
|
||||
if (option.IsPadding())
|
||||
{
|
||||
@@ -342,44 +347,49 @@ Error Ip6::RemoveMplOption(Message &aMessage)
|
||||
if (option.GetType() == MplOption::kType)
|
||||
{
|
||||
// If multiple MPL options exist, discard packet
|
||||
VerifyOrExit(mplOffset == 0, error = kErrorParse);
|
||||
VerifyOrExit(action == kNoMplOption, error = kErrorParse);
|
||||
|
||||
mplOffset = offset;
|
||||
mplLength = option.GetLength();
|
||||
// `Option::ParseFrom()` already validated that the entire
|
||||
// option is present in the `offsetRange`.
|
||||
|
||||
VerifyOrExit(mplLength <= sizeof(MplOption) - sizeof(Option), error = kErrorParse);
|
||||
mplOffsetRange = offsetRange;
|
||||
mplOffsetRange.ShrinkLength(option.GetSize());
|
||||
|
||||
if (mplOffset == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0)
|
||||
VerifyOrExit(option.GetSize() <= sizeof(MplOption), error = kErrorParse);
|
||||
|
||||
if (mplOffsetRange.GetOffset() == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0)
|
||||
{
|
||||
// First and only IPv6 Option, remove IPv6 HBH Option header
|
||||
remove = true;
|
||||
action = kRemoveHbh;
|
||||
}
|
||||
else if (mplOffset + ExtensionHeader::kLengthUnitSize == endOffset)
|
||||
else if (mplOffsetRange.GetOffset() + ExtensionHeader::kLengthUnitSize == offsetRange.GetEndOffset())
|
||||
{
|
||||
// Last IPv6 Option, remove the last 8 bytes
|
||||
remove = true;
|
||||
// Last IPv6 Option, shrink the last 8 bytes
|
||||
action = kShrinkHbh;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (action != kNoMplOption)
|
||||
{
|
||||
// Encountered another option, now just replace
|
||||
// MPL Option with Pad Option
|
||||
remove = false;
|
||||
action = kReplaceMplWithPad;
|
||||
}
|
||||
}
|
||||
|
||||
// verify that IPv6 Options header is properly formed
|
||||
VerifyOrExit(offset == endOffset, error = kErrorParse);
|
||||
|
||||
if (remove)
|
||||
switch (action)
|
||||
{
|
||||
case kNoMplOption:
|
||||
break;
|
||||
|
||||
case kShrinkHbh:
|
||||
case kRemoveHbh:
|
||||
// Last IPv6 Option, shrink HBH Option header by
|
||||
// 8 bytes (`kLengthUnitSize`)
|
||||
aMessage.RemoveHeader(endOffset - ExtensionHeader::kLengthUnitSize, ExtensionHeader::kLengthUnitSize);
|
||||
aMessage.RemoveHeader(offsetRange.GetEndOffset() - ExtensionHeader::kLengthUnitSize,
|
||||
ExtensionHeader::kLengthUnitSize);
|
||||
|
||||
if (mplOffset == sizeof(ip6Header) + sizeof(hbh))
|
||||
if (action == kRemoveHbh)
|
||||
{
|
||||
// Remove entire HBH header
|
||||
ip6Header.SetNextHeader(hbh.GetNextHeader());
|
||||
}
|
||||
else
|
||||
@@ -393,14 +403,12 @@ Error Ip6::RemoveMplOption(Message &aMessage)
|
||||
|
||||
ip6Header.SetPayloadLength(ip6Header.GetPayloadLength() - ExtensionHeader::kLengthUnitSize);
|
||||
aMessage.Write(0, ip6Header);
|
||||
}
|
||||
else if (mplOffset != 0)
|
||||
{
|
||||
// Replace MPL Option with Pad Option
|
||||
PadOption padOption;
|
||||
break;
|
||||
|
||||
padOption.InitForPadSize(sizeof(Option) + mplLength);
|
||||
aMessage.WriteBytes(mplOffset, &padOption, padOption.GetSize());
|
||||
case kReplaceMplWithPad:
|
||||
padOption.InitForPadSize(static_cast<uint8_t>(mplOffsetRange.GetLength()));
|
||||
aMessage.WriteBytes(mplOffsetRange.GetOffset(), &padOption, padOption.GetSize());
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -501,24 +509,37 @@ void Ip6::HandleSendQueue(void)
|
||||
}
|
||||
}
|
||||
|
||||
Error Ip6::ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange, HopByHopHeader &aHbhHeader) const
|
||||
{
|
||||
// Reads the HBH header from the message at the given offset range.
|
||||
// On success, updates `aOffsetRange` to indicate the location of
|
||||
// options within the HBH header.
|
||||
|
||||
Error error;
|
||||
|
||||
SuccessOrExit(error = aMessage.Read(aOffsetRange, aHbhHeader));
|
||||
VerifyOrExit(aOffsetRange.Contains(aHbhHeader.GetSize()), error = kErrorParse);
|
||||
aOffsetRange.ShrinkLength(aHbhHeader.GetSize());
|
||||
aOffsetRange.AdvanceOffset(sizeof(HopByHopHeader));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool &aReceive)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
HopByHopHeader hbhHeader;
|
||||
Option option;
|
||||
uint16_t offset = aMessage.GetOffset();
|
||||
uint16_t endOffset;
|
||||
OffsetRange offsetRange;
|
||||
|
||||
SuccessOrExit(error = aMessage.Read(offset, hbhHeader));
|
||||
offsetRange.InitFromMessageOffsetToEnd(aMessage);
|
||||
|
||||
endOffset = offset + hbhHeader.GetSize();
|
||||
VerifyOrExit(endOffset <= aMessage.GetLength(), error = kErrorParse);
|
||||
SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbhHeader));
|
||||
|
||||
offset += sizeof(HopByHopHeader);
|
||||
|
||||
for (; offset < endOffset; offset += option.GetSize())
|
||||
for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize()))
|
||||
{
|
||||
SuccessOrExit(error = option.ParseFrom(aMessage, offset, endOffset));
|
||||
SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange));
|
||||
|
||||
if (option.IsPadding())
|
||||
{
|
||||
@@ -527,14 +548,14 @@ Error Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool &aReceive)
|
||||
|
||||
if (option.GetType() == MplOption::kType)
|
||||
{
|
||||
SuccessOrExit(error = mMpl.ProcessOption(aMessage, offset, aHeader.GetSource(), aReceive));
|
||||
SuccessOrExit(error = mMpl.ProcessOption(aMessage, offsetRange, aHeader.GetSource(), aReceive));
|
||||
continue;
|
||||
}
|
||||
|
||||
VerifyOrExit(option.GetAction() == Option::kActionSkip, error = kErrorDrop);
|
||||
}
|
||||
|
||||
aMessage.SetOffset(offset);
|
||||
aMessage.SetOffset(offsetRange.GetEndOffset());
|
||||
|
||||
exit:
|
||||
return error;
|
||||
|
||||
@@ -402,6 +402,7 @@ private:
|
||||
void UpdateReassemblyList(void);
|
||||
void SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode);
|
||||
#endif
|
||||
Error ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange, HopByHopHeader &aHbhHeader) const;
|
||||
Error AddMplOption(Message &aMessage, Header &aHeader);
|
||||
Error PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Header &aHeader);
|
||||
Error InsertMplOption(Message &aMessage, Header &aHeader);
|
||||
|
||||
@@ -69,14 +69,14 @@ bool Header::IsValid(void) const
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// Option
|
||||
|
||||
Error Option::ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t aEndOffset)
|
||||
Error Option::ParseFrom(const Message &aMessage, const OffsetRange &aOffsetRange)
|
||||
{
|
||||
Error error;
|
||||
|
||||
// Read the Type first to check for the Pad1 Option.
|
||||
// If it is not, then we read the full `Option` header.
|
||||
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(mType)));
|
||||
SuccessOrExit(error = aMessage.Read(aOffsetRange, this, sizeof(mType)));
|
||||
|
||||
if (mType == kTypePad1)
|
||||
{
|
||||
@@ -84,9 +84,8 @@ Error Option::ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t aEnd
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, *this));
|
||||
|
||||
VerifyOrExit(aOffset + GetSize() <= aEndOffset, error = kErrorParse);
|
||||
SuccessOrExit(error = aMessage.Read(aOffsetRange, *this));
|
||||
VerifyOrExit(aOffsetRange.Contains(GetSize()), error = kErrorParse);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
|
||||
@@ -517,17 +517,16 @@ public:
|
||||
* Parses and validates the IPv6 Option from a given message.
|
||||
*
|
||||
* The Option is read from @p aOffset in @p aMessage. This method then checks that the entire Option is present
|
||||
* in @p aMessage before the @p aEndOffset.
|
||||
* within @p aOffsetRange.
|
||||
*
|
||||
* @param[in] aMessage The IPv6 message.
|
||||
* @param[in] aOffset The offset in @p aMessage to read the IPv6 Option.
|
||||
* @param[in] aEndOffset The end offset in @p aMessage.
|
||||
* @param[in] aMessage The IPv6 message.
|
||||
* @param[in] aOffsetRange The offset range in @p aMessage to read the IPv6 Option.
|
||||
*
|
||||
* @retval kErrorNone Successfully parsed the IPv6 option from @p aMessage.
|
||||
* @retval kErrorParse Malformed IPv6 Option or Option is not contained within @p aMessage by @p aEndOffset.
|
||||
* @retval kErrorParse Malformed IPv6 Option or Option is not contained within @p aMessage and @p aOffsetRange.
|
||||
*
|
||||
*/
|
||||
Error ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t aEndOffset);
|
||||
Error ParseFrom(const Message &aMessage, const OffsetRange &aOffsetRange);
|
||||
|
||||
protected:
|
||||
static constexpr uint8_t kTypePad1 = 0x00; ///< Pad1 Option Type.
|
||||
|
||||
@@ -90,14 +90,14 @@ void Mpl::InitOption(MplOption &aOption, const Address &aAddress)
|
||||
aOption.SetSequence(mSequence++);
|
||||
}
|
||||
|
||||
Error Mpl::ProcessOption(Message &aMessage, uint16_t aOffset, const Address &aAddress, bool &aReceive)
|
||||
Error Mpl::ProcessOption(Message &aMessage, const OffsetRange &aOffsetRange, const Address &aAddress, bool &aReceive)
|
||||
{
|
||||
Error error;
|
||||
MplOption option;
|
||||
|
||||
// Read the min size bytes first, then check the expected
|
||||
// `SeedIdLength` and read the full `MplOption` if needed.
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, &option, MplOption::kMinSize));
|
||||
SuccessOrExit(error = aMessage.Read(aOffsetRange, &option, MplOption::kMinSize));
|
||||
|
||||
switch (option.GetSeedIdLength())
|
||||
{
|
||||
@@ -108,7 +108,7 @@ Error Mpl::ProcessOption(Message &aMessage, uint16_t aOffset, const Address &aAd
|
||||
break;
|
||||
|
||||
case MplOption::kSeedIdLength2:
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, option));
|
||||
SuccessOrExit(error = aMessage.Read(aOffsetRange, option));
|
||||
break;
|
||||
|
||||
case MplOption::kSeedIdLength8:
|
||||
|
||||
@@ -191,17 +191,17 @@ public:
|
||||
* 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] aOffset The offset in @p aMessage to read the MPL option.
|
||||
* @param[in] aAddress A reference to the IPv6 Source Address.
|
||||
* @param[out] aReceive Set to FALSE if the MPL message is a duplicate and must not
|
||||
* go through the receiving process again, untouched otherwise.
|
||||
* @param[in] aMessage A reference to the message.
|
||||
* @param[in] aOffsetRange The offset range in @p aMessage to read the MPL option.
|
||||
* @param[in] aAddress A reference to the IPv6 Source Address.
|
||||
* @param[out] aReceive Set to FALSE if the MPL message is a duplicate and must not
|
||||
* go through the receiving process again, untouched otherwise.
|
||||
*
|
||||
* @retval kErrorNone Successfully processed the MPL option.
|
||||
* @retval kErrorDrop The MPL message is a duplicate and should be dropped.
|
||||
*
|
||||
*/
|
||||
Error ProcessOption(Message &aMessage, uint16_t aOffset, const Address &aAddress, bool &aReceive);
|
||||
Error ProcessOption(Message &aMessage, const OffsetRange &aOffsetRange, const Address &aAddress, bool &aReceive);
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
/**
|
||||
|
||||
@@ -470,14 +470,15 @@ Error Lowpan::CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBui
|
||||
// Pad1 or PadN option MAY be elided by the compressor."
|
||||
if (aNextHeader == Ip6::kProtoHopOpts || aNextHeader == Ip6::kProtoDstOpts)
|
||||
{
|
||||
uint16_t offset = aMessage.GetOffset();
|
||||
uint16_t endOffset = offset + len;
|
||||
OffsetRange offsetRange;
|
||||
bool hasOption = false;
|
||||
Ip6::Option option;
|
||||
|
||||
for (; offset < endOffset; offset += option.GetSize())
|
||||
offsetRange.Init(aMessage.GetOffset(), len);
|
||||
|
||||
for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize()))
|
||||
{
|
||||
SuccessOrExit(error = option.ParseFrom(aMessage, offset, endOffset));
|
||||
SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange));
|
||||
hasOption = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user