From 49b98466328136c2e066b5b04de644a187c68307 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 14 Jun 2022 21:58:35 -0700 Subject: [PATCH] [routing-manager] simplify RA message parsing and preparation (#7789) This commit adds a new class `RouterAdvertMessage` representing an RA message. It provides methods to append options (PIO or RIO) to the RA message or parse and iterate over the options in the RA message (using newly added `Option::Iterator` which allows use of range-based `for` loop). These method are used by `RoutingManager` and help simplify the code. --- src/core/border_router/routing_manager.cpp | 208 ++++++--------- src/core/border_router/routing_manager.hpp | 8 +- src/core/net/nd6.cpp | 129 ++++++++-- src/core/net/nd6.hpp | 280 +++++++++++++++------ 4 files changed, 382 insertions(+), 243 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 01bcf2bd4..4dfa0fcd3 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -828,128 +828,76 @@ Error RoutingManager::SendRouterSolicitation(void) return mInfraIf.Send(packet, destAddress); } -// This method sends Router Advertisement messages to advertise on-link prefix and route for OMR prefix. -// @param[in] aNewOmrPrefixes An array of the new OMR prefixes to be advertised. -// Empty array means we should stop advertising OMR prefixes. void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes) { - uint8_t buffer[kMaxRouterAdvMessageLength]; - uint16_t bufferLength = 0; + uint8_t buffer[kMaxRouterAdvMessageLength]; + Ip6::Nd::RouterAdvertMessage raMsg(mRouterAdvertHeader, buffer); - static_assert(sizeof(mRouterAdvMessage) <= sizeof(buffer), "RA buffer too small"); - memcpy(buffer, &mRouterAdvMessage, sizeof(mRouterAdvMessage)); - bufferLength += sizeof(mRouterAdvMessage); + // Append PIO for local on-link prefix. Ensure it is either being + // advertised or deprecated. - if (mIsAdvertisingLocalOnLinkPrefix) + if (mIsAdvertisingLocalOnLinkPrefix || mOnLinkPrefixDeprecateTimer.IsRunning()) { - Ip6::Nd::PrefixInfoOption *pio; + uint32_t validLifetime = kDefaultOnLinkPrefixLifetime; + uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime; - OT_ASSERT(bufferLength + sizeof(Ip6::Nd::PrefixInfoOption) <= sizeof(buffer)); - - pio = reinterpret_cast(buffer + bufferLength); - - pio->Init(); - pio->SetOnLinkFlag(); - pio->SetAutoAddrConfigFlag(); - pio->SetValidLifetime(kDefaultOnLinkPrefixLifetime); - pio->SetPreferredLifetime(kDefaultOnLinkPrefixLifetime); - pio->SetPrefix(mLocalOnLinkPrefix); - - bufferLength += pio->GetSize(); - - LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - mLocalOnLinkPrefix.ToString().AsCString(), pio->GetPreferredLifetime(), pio->GetValidLifetime()); - - mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); - } - else if (mOnLinkPrefixDeprecateTimer.IsRunning()) - { - Ip6::Nd::PrefixInfoOption *pio; - - OT_ASSERT(bufferLength + sizeof(Ip6::Nd::PrefixInfoOption) <= sizeof(buffer)); - - pio = reinterpret_cast(buffer + bufferLength); - - pio->Init(); - pio->SetOnLinkFlag(); - pio->SetAutoAddrConfigFlag(); - pio->SetValidLifetime(TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow())); - - // Set zero preferred lifetime to immediately deprecate the advertised on-link prefix. - pio->SetPreferredLifetime(0); - pio->SetPrefix(mLocalOnLinkPrefix); - - bufferLength += pio->GetSize(); - - LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - mLocalOnLinkPrefix.ToString().AsCString(), pio->GetPreferredLifetime(), pio->GetValidLifetime()); - } - - // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array. - - for (const OmrPrefix &advertisedOmrPrefix : mAdvertisedOmrPrefixes) - { - if (!aNewOmrPrefixes.ContainsMatching(advertisedOmrPrefix.GetPrefix())) + if (mOnLinkPrefixDeprecateTimer.IsRunning()) { - Ip6::Nd::RouteInfoOption *rio; + validLifetime = TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow()); + preferredLifetime = 0; + } - OT_ASSERT(bufferLength + - Ip6::Nd::RouteInfoOption::OptionSizeForPrefix(advertisedOmrPrefix.GetPrefix().GetLength()) <= - sizeof(buffer)); + SuccessOrAssert(raMsg.AppendPrefixInfoOption(mLocalOnLinkPrefix, validLifetime, preferredLifetime)); - rio = reinterpret_cast(buffer + bufferLength); + if (mIsAdvertisingLocalOnLinkPrefix) + { + mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); + } - // Set zero route lifetime to immediately invalidate the advertised OMR prefix. - rio->Init(); - rio->SetRouteLifetime(0); - rio->SetPrefix(advertisedOmrPrefix.GetPrefix()); + LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=%u)", mLocalOnLinkPrefix.ToString().AsCString(), + validLifetime, preferredLifetime); + } - bufferLength += rio->GetSize(); + // Invalidate previously advertised OMR prefixes if they are no + // longer in the new OMR prefix array. - LogInfo("Stop advertising OMR prefix %s on %s", advertisedOmrPrefix.ToString().AsCString(), - mInfraIf.ToString().AsCString()); + for (const OmrPrefix &omrPrefix : mAdvertisedOmrPrefixes) + { + if (!aNewOmrPrefixes.ContainsMatching(omrPrefix.GetPrefix())) + { + SuccessOrAssert( + raMsg.AppendRouteInfoOption(omrPrefix.GetPrefix(), /* aRouteLifetime */ 0, omrPrefix.GetPreference())); + + LogInfo("RouterAdvert: Added RIO for %s (lifetime=0)", omrPrefix.ToString().AsCString()); } } - for (const OmrPrefix &newOmrPrefix : aNewOmrPrefixes) + for (const OmrPrefix &omrPrefix : aNewOmrPrefixes) { - Ip6::Nd::RouteInfoOption *rio; + SuccessOrAssert( + raMsg.AppendRouteInfoOption(omrPrefix.GetPrefix(), kDefaultOmrPrefixLifetime, omrPrefix.GetPreference())); - OT_ASSERT(bufferLength + Ip6::Nd::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetPrefix().GetLength()) <= - sizeof(buffer)); - - rio = reinterpret_cast(buffer + bufferLength); - - rio->Init(); - rio->SetRouteLifetime(kDefaultOmrPrefixLifetime); - rio->SetPreference(newOmrPrefix.GetPreference()); - rio->SetPrefix(newOmrPrefix.GetPrefix()); - - bufferLength += rio->GetSize(); - - LogInfo("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(), + LogInfo("RouterAdvert: Added RIO for %s (lifetime=%u)", omrPrefix.ToString().AsCString(), kDefaultOmrPrefixLifetime); } - // Send the message only when there are options. - if (bufferLength > sizeof(mRouterAdvMessage)) + if (raMsg.ContainsAnyOptions()) { - Error error; - Ip6::Address destAddress; - InfraIf::Icmp6Packet packet; + Error error; + Ip6::Address destAddress; ++mRouterAdvertisementCount; - packet.Init(buffer, bufferLength); destAddress.SetToLinkLocalAllNodesMulticast(); - error = mInfraIf.Send(packet, destAddress); + error = mInfraIf.Send(raMsg.GetAsPacket(), destAddress); if (error == kErrorNone) { mLastRouterAdvertisementSendTime = TimerMilli::GetNow(); LogInfo("Sent Router Advertisement on %s", mInfraIf.ToString().AsCString()); - DumpDebg("[BR-CERT] direction=send | type=RA |", buffer, bufferLength); + DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(), + raMsg.GetAsPacket().GetLength()); } else { @@ -1052,7 +1000,7 @@ void RoutingManager::HandleRouterSolicitTimer(void) // Invalidate the learned RA message if it is not refreshed during Router Solicitation. if (mTimeRouterAdvMessageLastUpdate <= mTimeRouterSolicitStart) { - UpdateRouterAdvMessage(/* aRouterAdvMessage */ nullptr); + UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); } mRouterSolicitCount = 0; @@ -1105,51 +1053,30 @@ void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, co void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { - OT_ASSERT(mIsRunning); OT_UNUSED_VARIABLE(aSrcAddress); - bool needReevaluate = false; - const uint8_t * optionsBegin; - uint16_t optionsLength; - const Ip6::Nd::Option * option; - const Ip6::Nd::RouterAdvMessage *routerAdvMessage; + bool needReevaluate = false; + Ip6::Nd::RouterAdvertMessage routerAdvMessage(aPacket); - VerifyOrExit(aPacket.GetLength() >= sizeof(Ip6::Nd::RouterAdvMessage)); + OT_ASSERT(mIsRunning); + + VerifyOrExit(routerAdvMessage.IsValid()); LogInfo("Received Router Advertisement from %s on %s", aSrcAddress.ToString().AsCString(), mInfraIf.ToString().AsCString()); DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength()); - routerAdvMessage = reinterpret_cast(aPacket.GetBytes()); - optionsBegin = aPacket.GetBytes() + sizeof(Ip6::Nd::RouterAdvMessage); - optionsLength = aPacket.GetLength() - sizeof(Ip6::Nd::RouterAdvMessage); - - option = nullptr; - while ((option = Ip6::Nd::Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr) + for (const Ip6::Nd::Option &option : routerAdvMessage) { - switch (option->GetType()) + switch (option.GetType()) { - case Ip6::Nd::Option::Type::kPrefixInfo: - { - const Ip6::Nd::PrefixInfoOption *pio = static_cast(option); + case Ip6::Nd::Option::kTypePrefixInfo: + needReevaluate |= UpdateDiscoveredOnLinkPrefix(static_cast(option)); + break; - if (pio->IsValid()) - { - needReevaluate |= UpdateDiscoveredOnLinkPrefix(*pio); - } - } - break; - - case Ip6::Nd::Option::Type::kRouteInfo: - { - const Ip6::Nd::RouteInfoOption *rio = static_cast(option); - - if (rio->IsValid()) - { - UpdateDiscoveredOmrPrefix(*rio); - } - } - break; + case Ip6::Nd::Option::kTypeRouteInfo: + UpdateDiscoveredOmrPrefix(static_cast(option)); + break; default: break; @@ -1160,7 +1087,7 @@ void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPack // initiated from the infra interface. if (mInfraIf.HasAddress(aSrcAddress)) { - needReevaluate |= UpdateRouterAdvMessage(routerAdvMessage); + needReevaluate |= UpdateRouterAdvertHeader(&routerAdvMessage); } if (needReevaluate) @@ -1182,6 +1109,8 @@ bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const Ip6::Nd::PrefixInfoOptio ExternalPrefix onLinkPrefix; ExternalPrefix *existingPrefix = nullptr; + VerifyOrExit(aPio.IsValid()); + aPio.GetPrefix(prefix); if (!IsValidOnLinkPrefix(aPio)) @@ -1243,6 +1172,8 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const Ip6::Nd::RouteInfoOption &a ExternalPrefix omrPrefix; ExternalPrefix *existingPrefix = nullptr; + VerifyOrExit(aRio.IsValid()); + aRio.GetPrefix(prefix); if (!IsValidOmrPrefix(prefix)) @@ -1390,35 +1321,36 @@ bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) co return contain; } -// Update the `mRouterAdvMessage` with given Router Advertisement message. -// Returns a boolean which indicates whether there are changes of `mRouterAdvMessage`. -bool RoutingManager::UpdateRouterAdvMessage(const Ip6::Nd::RouterAdvMessage *aRouterAdvMessage) +bool RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage) { - Ip6::Nd::RouterAdvMessage oldRouterAdvMessage; + // Updates the `mRouterAdvertHeader` from the given RA message. + // Returns a boolean which indicates whether there was any changes + // to `mRouterAdvertHeader`. - oldRouterAdvMessage = mRouterAdvMessage; + Ip6::Nd::RouterAdvertMessage::Header oldHeader; + oldHeader = mRouterAdvertHeader; mTimeRouterAdvMessageLastUpdate = TimerMilli::GetNow(); - if (aRouterAdvMessage == nullptr || aRouterAdvMessage->GetRouterLifetime() == 0) + if (aRouterAdvertMessage == nullptr || aRouterAdvertMessage->GetHeader().GetRouterLifetime() == 0) { - mRouterAdvMessage.SetToDefault(); + mRouterAdvertHeader.SetToDefault(); mLearntRouterAdvMessageFromHost = false; } else { - // The checksum is set to zero in `mRouterAdvMessage` + // The checksum is set to zero in `mRouterAdvertHeader` // which indicates to platform that it needs to do the // calculation and update it. - mRouterAdvMessage = *aRouterAdvMessage; - mRouterAdvMessage.SetChecksum(0); + mRouterAdvertHeader = aRouterAdvertMessage->GetHeader(); + mRouterAdvertHeader.SetChecksum(0); mLearntRouterAdvMessageFromHost = true; } ResetDiscoveredPrefixStaleTimer(); - return (mRouterAdvMessage != oldRouterAdvMessage); + return (mRouterAdvertHeader != oldHeader); } void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 2e92485c1..f658eaae8 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -351,7 +351,7 @@ private: void InvalidateDiscoveredPrefixes(void); void InvalidateAllDiscoveredPrefixes(void); bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const; - bool UpdateRouterAdvMessage(const Ip6::Nd::RouterAdvMessage *aRouterAdvMessage); + bool UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage); void ResetDiscoveredPrefixStaleTimer(void); static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix); @@ -408,9 +408,9 @@ private: // The RA header and parameters for the infra interface. // This value is initialized with `RouterAdvMessage::SetToDefault` // and updated with RA messages initiated from infra interface. - Ip6::Nd::RouterAdvMessage mRouterAdvMessage; - TimeMilli mTimeRouterAdvMessageLastUpdate; - bool mLearntRouterAdvMessageFromHost; + Ip6::Nd::RouterAdvertMessage::Header mRouterAdvertHeader; + TimeMilli mTimeRouterAdvMessageLastUpdate; + bool mLearntRouterAdvMessageFromHost; TimerMilli mDiscoveredPrefixInvalidTimer; TimerMilli mDiscoveredPrefixStaleTimer; diff --git a/src/core/net/nd6.cpp b/src/core/net/nd6.cpp index f9d06bcb8..5b57e1be9 100644 --- a/src/core/net/nd6.cpp +++ b/src/core/net/nd6.cpp @@ -28,7 +28,7 @@ /** * @file - * This file includes implementations for IPv6 Neighbor Discovery (ND). + * This file includes implementations for IPv6 Neighbor Discovery (ND6). * */ @@ -41,29 +41,44 @@ namespace ot { namespace Ip6 { namespace Nd { -const Option *Option::GetNextOption(const Option *aCurOption, const uint8_t *aBuffer, uint16_t aBufferLength) +//---------------------------------------------------------------------------------------------------------------------- +// Option::Iterator + +Option::Iterator::Iterator(void) + : mOption(nullptr) + , mEnd(nullptr) { - const uint8_t *nextOption = nullptr; - const uint8_t *bufferEnd = aBuffer + aBufferLength; + // An empty iterator (used to indicate `end()` of list). +} - VerifyOrExit(aBuffer != nullptr, nextOption = nullptr); +Option::Iterator::Iterator(const void *aStart, const void *aEnd) + : mOption(nullptr) + , mEnd(reinterpret_cast(aEnd)) +{ + // Note that `Validate()` uses `mEnd` so can only be called after + // `mEnd` is set. - if (aCurOption == nullptr) - { - nextOption = aBuffer; - } - else - { - nextOption = reinterpret_cast(aCurOption) + aCurOption->GetSize(); - } + mOption = Validate(reinterpret_cast(aStart)); +} - VerifyOrExit(nextOption + sizeof(Option) <= bufferEnd, nextOption = nullptr); - VerifyOrExit(reinterpret_cast(nextOption)->GetSize() > 0, nextOption = nullptr); - VerifyOrExit(nextOption + reinterpret_cast(nextOption)->GetSize() <= bufferEnd, - nextOption = nullptr); +const Option *Option::Iterator::Next(const Option *aOption) +{ + return reinterpret_cast(reinterpret_cast(aOption) + aOption->GetSize()); +} -exit: - return reinterpret_cast(nextOption); +void Option::Iterator::Advance(void) +{ + mOption = (mOption != nullptr) ? Validate(Next(mOption)) : nullptr; +} + +const Option *Option::Iterator::Validate(const Option *aOption) const +{ + // Check if `aOption` is well-formed and fits in the range + // up to `mEnd`. Returns `aOption` if it is valid, `nullptr` + // otherwise. + + return ((aOption != nullptr) && ((aOption + 1) <= mEnd) && aOption->IsValid() && (Next(aOption) <= mEnd)) ? aOption + : nullptr; } //---------------------------------------------------------------------------------------------------------------------- @@ -72,7 +87,7 @@ exit: void PrefixInfoOption::Init(void) { Clear(); - SetType(Type::kPrefixInfo); + SetType(kTypePrefixInfo); SetSize(sizeof(PrefixInfoOption)); OT_UNUSED_VARIABLE(mReserved2); @@ -101,7 +116,7 @@ bool PrefixInfoOption::IsValid(void) const void RouteInfoOption::Init(void) { Clear(); - SetType(Type::kRouteInfo); + SetType(kTypeRouteInfo); } void RouteInfoOption::SetPreference(RoutePreference aPreference) @@ -165,9 +180,9 @@ uint8_t RouteInfoOption::OptionLengthForPrefix(uint8_t aPrefixLength) } //---------------------------------------------------------------------------------------------------------------------- -// RouterAdvMessage +// RouterAdverMessage::Header -void RouterAdvMessage::SetToDefault(void) +void RouterAdvertMessage::Header::SetToDefault(void) { OT_UNUSED_VARIABLE(mCode); OT_UNUSED_VARIABLE(mCurHopLimit); @@ -178,17 +193,81 @@ void RouterAdvMessage::SetToDefault(void) mType = Icmp::Header::kTypeRouterAdvert; } -RoutePreference RouterAdvMessage::GetDefaultRouterPreference(void) const +RoutePreference RouterAdvertMessage::Header::GetDefaultRouterPreference(void) const { return NetworkData::RoutePreferenceFromValue((mFlags & kPreferenceMask) >> kPreferenceOffset); } -void RouterAdvMessage::SetDefaultRouterPreference(RoutePreference aPreference) +void RouterAdvertMessage::Header::SetDefaultRouterPreference(RoutePreference aPreference) { mFlags &= ~kPreferenceMask; mFlags |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; } +//---------------------------------------------------------------------------------------------------------------------- +// RouterAdverMessage + +Option *RouterAdvertMessage::AppendOption(uint16_t aOptionSize) +{ + // This method appends an option with a given size to the RA + // message by reserving space in the data buffer if there is + // room. On success returns pointer to the option, on failure + // returns `nullptr`. The returned option needs to be + // initialized and populated by the caller. + + Option * option = nullptr; + uint32_t newLength = mData.GetLength(); + + newLength += aOptionSize; + VerifyOrExit(newLength <= mMaxLength); + + option = reinterpret_cast