diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index c8b75c8a7..8e571cca9 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -508,15 +508,22 @@ exit: Error Ip6::HandleOptions(Message &aMessage, const Header &aHeader, bool &aReceive) { - Error error = kErrorNone; + Error error = kErrorNone; + bool hasMplOption = false; HopByHopHeader hbhHeader; Option option; OffsetRange offsetRange; + MplOption mplOption; offsetRange.InitFromMessageOffsetToEnd(aMessage); SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbhHeader)); + // `ReadHopByHopHeader()` updates `offsetRange` to refer to the + // location of the options within the HBH header. We first + // validate all options, ensuring there is at most one MPL + // option, before processing it. + for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize())) { SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange)); @@ -528,13 +535,22 @@ Error Ip6::HandleOptions(Message &aMessage, const Header &aHeader, bool &aReceiv if (option.GetType() == MplOption::kType) { - SuccessOrExit(error = mMpl.ProcessOption(aMessage, offsetRange, aHeader.GetSource(), aReceive)); + VerifyOrExit(!hasMplOption, error = kErrorDrop); + hasMplOption = true; + + SuccessOrExit(error = mMpl.ReadAndValidateOption(aMessage, offsetRange, aHeader.GetSource(), mplOption)); + continue; } VerifyOrExit(option.GetAction() == Option::kActionSkip, error = kErrorDrop); } + if (hasMplOption) + { + SuccessOrExit(error = mMpl.ProcessOption(aMessage, mplOption, aReceive)); + } + aMessage.SetOffset(offsetRange.GetEndOffset()); exit: diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index f36ffd35c..0163395b0 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -83,25 +83,27 @@ void Mpl::InitOption(MplOption &aOption, const Address &aAddress) aOption.SetSequence(mSequence++); } -Error Mpl::ProcessOption(Message &aMessage, const OffsetRange &aOffsetRange, const Address &aAddress, bool &aReceive) +Error Mpl::ReadAndValidateOption(Message &aMessage, + const OffsetRange &aOffsetRange, + const Address &aAddress, + MplOption &aOption) { - Error error; - MplOption option; + Error error; // Read the min size bytes first, then check the expected // `SeedIdLength` and read the full `MplOption` if needed. - SuccessOrExit(error = aMessage.Read(aOffsetRange, &option, MplOption::kMinSize)); + SuccessOrExit(error = aMessage.Read(aOffsetRange, &aOption, MplOption::kMinSize)); - switch (option.GetSeedIdLength()) + switch (aOption.GetSeedIdLength()) { case MplOption::kSeedIdLength0: // Retrieve Seed ID from the IPv6 Source Address RLOC. - VerifyOrExit(aAddress.GetIid().IsLocator(), error = kErrorDrop); - option.SetSeedId(aAddress.GetIid().GetLocator()); + VerifyOrExit(aAddress.GetIid().IsLocator(), error = kErrorParse); + aOption.SetSeedId(aAddress.GetIid().GetLocator()); break; case MplOption::kSeedIdLength2: - SuccessOrExit(error = aMessage.Read(aOffsetRange, option)); + SuccessOrExit(error = aMessage.Read(aOffsetRange, aOption)); break; case MplOption::kSeedIdLength8: @@ -109,13 +111,21 @@ Error Mpl::ProcessOption(Message &aMessage, const OffsetRange &aOffsetRange, con ExitNow(error = kErrorParse); } +exit: + return error; +} + +Error Mpl::ProcessOption(Message &aMessage, const MplOption &aOption, bool &aReceive) +{ + Error error; + // Check if the MPL Data Message is new. - error = UpdateSeedSet(option.GetSeedId(), option.GetSequence()); + error = UpdateSeedSet(aOption.GetSeedId(), aOption.GetSequence()); if (error == kErrorNone) { #if OPENTHREAD_FTD - AddBufferedMessage(aMessage, option.GetSeedId(), option.GetSequence()); + AddBufferedMessage(aMessage, aOption.GetSeedId(), aOption.GetSequence()); #endif } else if (!aMessage.IsOriginThreadNetif()) @@ -123,10 +133,9 @@ Error Mpl::ProcessOption(Message &aMessage, const OffsetRange &aOffsetRange, con aReceive = false; // In case MPL Data Message is generated locally, ignore potential error of the MPL Seed Set // to allow subsequent retransmissions with the same sequence number. - ExitNow(error = kErrorNone); + error = kErrorNone; } -exit: return error; } diff --git a/src/core/net/ip6_mpl.hpp b/src/core/net/ip6_mpl.hpp index 38d930a6d..856f8ed62 100644 --- a/src/core/net/ip6_mpl.hpp +++ b/src/core/net/ip6_mpl.hpp @@ -171,21 +171,33 @@ public: void InitOption(MplOption &aOption, const Address &aAddress); /** - * Processes an MPL option. When the MPL module acts as an MPL Forwarder - * it disseminates MPL Data Message using Trickle timer expirations. When acts as an - * MPL Seed it allows to send the first MPL Data Message directly, then sets up Trickle - * timer expirations for subsequent retransmissions. + * Reads and validates an MPL option from a given message. * - * @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[in] aMessage The message from which to read the option. + * @param[in] aOffsetRange The offset range within @p aMessage to read from. + * @param[in] aAddress A reference to the IPv6 source address. + * @param[out] aOption An `MplOption` object to populate with the read option. + * + * @retval kErrorNone Successfully read and validated the MPL option. + * @retval kErrorParse Failed to parse the option. Invalid format. + */ + Error ReadAndValidateOption(Message &aMessage, + const OffsetRange &aOffsetRange, + const Address &aAddress, + MplOption &aOption); + + /** + * Processes a previously read and validated MPL option. + * + * @param[in] aMessage The message. + * @param[in] aOption The MPL option. * @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, const OffsetRange &aOffsetRange, const Address &aAddress, bool &aReceive); + Error ProcessOption(Message &aMessage, const MplOption &aOption, bool &aReceive); #if OPENTHREAD_FTD /**