diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index a8ce030e4..2d3b1922b 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -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(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; diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index 6e484ea98..03414ec32 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -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); diff --git a/src/core/net/ip6_headers.cpp b/src/core/net/ip6_headers.cpp index ff9585f6f..6e2262bcc 100644 --- a/src/core/net/ip6_headers.cpp +++ b/src/core/net/ip6_headers.cpp @@ -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; diff --git a/src/core/net/ip6_headers.hpp b/src/core/net/ip6_headers.hpp index 8289c53b0..9e8d151b8 100644 --- a/src/core/net/ip6_headers.hpp +++ b/src/core/net/ip6_headers.hpp @@ -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. diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index 4f3964d7b..3bd873d07 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -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: diff --git a/src/core/net/ip6_mpl.hpp b/src/core/net/ip6_mpl.hpp index 76fe15984..53b8304e7 100644 --- a/src/core/net/ip6_mpl.hpp +++ b/src/core/net/ip6_mpl.hpp @@ -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 /** diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp index 3860f7ba0..e05bbd628 100644 --- a/src/core/thread/lowpan.cpp +++ b/src/core/thread/lowpan.cpp @@ -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; }