[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.
This commit is contained in:
Abtin Keshavarzian
2022-06-14 21:58:35 -07:00
committed by GitHub
parent b7c2c52d5c
commit 49b9846632
4 changed files with 382 additions and 243 deletions
+70 -138
View File
@@ -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<Ip6::Nd::PrefixInfoOption *>(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<Ip6::Nd::PrefixInfoOption *>(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<Ip6::Nd::RouteInfoOption *>(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<Ip6::Nd::RouteInfoOption *>(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<const Ip6::Nd::RouterAdvMessage *>(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<const Ip6::Nd::PrefixInfoOption *>(option);
case Ip6::Nd::Option::kTypePrefixInfo:
needReevaluate |= UpdateDiscoveredOnLinkPrefix(static_cast<const Ip6::Nd::PrefixInfoOption &>(option));
break;
if (pio->IsValid())
{
needReevaluate |= UpdateDiscoveredOnLinkPrefix(*pio);
}
}
break;
case Ip6::Nd::Option::Type::kRouteInfo:
{
const Ip6::Nd::RouteInfoOption *rio = static_cast<const Ip6::Nd::RouteInfoOption *>(option);
if (rio->IsValid())
{
UpdateDiscoveredOmrPrefix(*rio);
}
}
break;
case Ip6::Nd::Option::kTypeRouteInfo:
UpdateDiscoveredOmrPrefix(static_cast<const Ip6::Nd::RouteInfoOption &>(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)
+4 -4
View File
@@ -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;
+104 -25
View File
@@ -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<const Option *>(aEnd))
{
// Note that `Validate()` uses `mEnd` so can only be called after
// `mEnd` is set.
if (aCurOption == nullptr)
{
nextOption = aBuffer;
}
else
{
nextOption = reinterpret_cast<const uint8_t *>(aCurOption) + aCurOption->GetSize();
}
mOption = Validate(reinterpret_cast<const Option *>(aStart));
}
VerifyOrExit(nextOption + sizeof(Option) <= bufferEnd, nextOption = nullptr);
VerifyOrExit(reinterpret_cast<const Option *>(nextOption)->GetSize() > 0, nextOption = nullptr);
VerifyOrExit(nextOption + reinterpret_cast<const Option *>(nextOption)->GetSize() <= bufferEnd,
nextOption = nullptr);
const Option *Option::Iterator::Next(const Option *aOption)
{
return reinterpret_cast<const Option *>(reinterpret_cast<const uint8_t *>(aOption) + aOption->GetSize());
}
exit:
return reinterpret_cast<const Option *>(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<Option *>(AsNonConst(GetDataEnd()));
mData.SetLength(static_cast<uint16_t>(newLength));
exit:
return option;
}
Error RouterAdvertMessage::AppendPrefixInfoOption(const Prefix &aPrefix,
uint32_t aValidLifetime,
uint32_t aPreferredLifetime)
{
Error error = kErrorNone;
PrefixInfoOption *pio;
pio = static_cast<PrefixInfoOption *>(AppendOption(sizeof(PrefixInfoOption)));
VerifyOrExit(pio != nullptr, error = kErrorNoBufs);
pio->Init();
pio->SetOnLinkFlag();
pio->SetAutoAddrConfigFlag();
pio->SetValidLifetime(aValidLifetime);
pio->SetPreferredLifetime(aPreferredLifetime);
pio->SetPrefix(aPrefix);
exit:
return error;
}
Error RouterAdvertMessage::AppendRouteInfoOption(const Prefix & aPrefix,
uint32_t aRouteLifetime,
RoutePreference aPreference)
{
Error error = kErrorNone;
RouteInfoOption *rio;
rio = static_cast<RouteInfoOption *>(AppendOption(RouteInfoOption::OptionSizeForPrefix(aPrefix.GetLength())));
VerifyOrExit(rio != nullptr, error = kErrorNoBufs);
rio->Init();
rio->SetRouteLifetime(aRouteLifetime);
rio->SetPreference(aPreference);
rio->SetPrefix(aPrefix);
exit:
return error;
}
//----------------------------------------------------------------------------------------------------------------------
// RouterAdvMessage
+204 -76
View File
@@ -70,11 +70,13 @@ typedef NetworkData::RoutePreference RoutePreference; ///< Route Preference
OT_TOOL_PACKED_BEGIN
class Option
{
friend class RouterAdvertMessage;
public:
enum class Type : uint8_t
enum Type : uint8_t
{
kPrefixInfo = 3, ///< Prefix Information Option.
kRouteInfo = 24, ///< Route Information Option.
kTypePrefixInfo = 3, ///< Prefix Information Option.
kTypeRouteInfo = 24, ///< Route Information Option.
};
static constexpr uint16_t kLengthUnit = 8; ///< The unit of length in octets.
@@ -85,7 +87,7 @@ public:
* @returns The option type.
*
*/
Type GetType(void) const { return mType; }
uint8_t GetType(void) const { return mType; }
/**
* This method sets the option type.
@@ -130,18 +132,6 @@ public:
*/
uint16_t GetLength(void) const { return mLength; }
/**
* This helper method returns a pointer to the next valid option in the buffer.
*
* @param[in] aCurOption The current option. Use `nullptr` to get the first option.
* @param[in] aBuffer The buffer within which the options are held.
* @param[in] aBufferLength The length of the buffer.
*
* @returns A pointer to the next option if there are a valid one. Otherwise, `nullptr`.
*
*/
static const Option *GetNextOption(const Option *aCurOption, const uint8_t *aBuffer, uint16_t aBufferLength);
/**
* This method indicates whether or not this option is valid.
*
@@ -152,7 +142,27 @@ public:
bool IsValid(void) const { return mLength > 0; }
private:
Type mType; // Type of the option.
class Iterator : public Unequatable<Iterator>
{
public:
Iterator(void);
Iterator(const void *aStart, const void *aEnd);
const Option &operator*(void) { return *mOption; }
void operator++(void) { Advance(); }
void operator++(int) { Advance(); }
bool operator==(const Iterator &aOther) const { return mOption == aOther.mOption; }
private:
static const Option *Next(const Option *aOption);
void Advance(void);
const Option * Validate(const Option *aOption) const;
const Option *mOption;
const Option *mEnd;
};
uint8_t mType; // Type of the option.
uint8_t mLength; // Length of the option in unit of 8 octets, including the `mType` and `mLength` fields.
} OT_TOOL_PACKED_END;
@@ -166,7 +176,7 @@ OT_TOOL_PACKED_BEGIN
class PrefixInfoOption : public Option, private Clearable<PrefixInfoOption>
{
public:
static constexpr Type kType = Type::kPrefixInfo; ///< Prefix Information Option Type.
static constexpr Type kType = kTypePrefixInfo; ///< Prefix Information Option Type.
/**
* This method initializes the Prefix Info option with proper type and length and sets all other fields to zero.
@@ -321,8 +331,8 @@ OT_TOOL_PACKED_BEGIN
class RouteInfoOption : public Option, private Clearable<RouteInfoOption>
{
public:
static constexpr uint16_t kMinSize = kLengthUnit; ///< Minimum size (in bytes) of a Route Info Option
static constexpr Type kType = Type::kRouteInfo; ///< Route Information Option Type.
static constexpr uint16_t kMinSize = kLengthUnit; ///< Minimum size (in bytes) of a Route Info Option
static constexpr Type kType = kTypeRouteInfo; ///< Route Information Option Type.
/**
* This method initializes the option setting the type and clearing (setting to zero) all other fields.
@@ -445,101 +455,219 @@ private:
static_assert(sizeof(RouteInfoOption) == 8, "invalid RouteInfoOption structure");
/**
* This class implements the Router Advertisement message header.
*
* See section 2.2 of RFC 4191 [https://datatracker.ietf.org/doc/html/rfc4191]
* This class represents a Router Advertisement message.
*
*/
OT_TOOL_PACKED_BEGIN
class RouterAdvMessage : public Equatable<RouterAdvMessage>, private Clearable<RouterAdvMessage>
class RouterAdvertMessage
{
public:
/**
* This constructor initializes the Router Advertisement message with
* zero router lifetime, reachable time and retransmission timer.
* This class implements the RA message header.
*
* See section 2.2 of RFC 4191 [https://datatracker.ietf.org/doc/html/rfc4191]
*
*/
RouterAdvMessage(void) { SetToDefault(); }
OT_TOOL_PACKED_BEGIN
class Header : public Equatable<Header>, private Clearable<Header>
{
public:
/**
* This constructor initializes the Router Advertisement message with
* zero router lifetime, reachable time and retransmission timer.
*
*/
Header(void) { SetToDefault(); }
/**
* This method sets the RA message to default values.
*
*/
void SetToDefault(void);
/**
* This method sets the checksum value.
*
* @param[in] aChecksum The checksum value.
*
*/
void SetChecksum(uint16_t aChecksum) { mChecksum = HostSwap16(aChecksum); }
/**
* This method sets the Router Lifetime in seconds.
*
* @param[in] aRouterLifetime The router lifetime in seconds.
*
*/
void SetRouterLifetime(uint16_t aRouterLifetime) { mRouterLifetime = HostSwap16(aRouterLifetime); }
/**
* This method gets the Router Lifetime (in seconds).
*
* Router Lifetime set to zero indicates that the sender is not a default router.
*
* @returns The router lifetime in seconds.
*
*/
uint16_t GetRouterLifetime(void) const { return HostSwap16(mRouterLifetime); }
/**
* This method sets the default router preference.
*
* @param[in] aPreference The router preference.
*
*/
void SetDefaultRouterPreference(RoutePreference aPreference);
/**
* This method gets the default router preference.
*
* @returns The router preference.
*
*/
RoutePreference GetDefaultRouterPreference(void) const;
private:
// Router Advertisement Message
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type | Code | Checksum |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Cur Hop Limit |M|O|H|Prf|Resvd| Router Lifetime |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Reachable Time |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Retrans Timer |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Options ...
// +-+-+-+-+-+-+-+-+-+-+-+-
static constexpr uint8_t kPreferenceOffset = 3;
static constexpr uint8_t kPreferenceMask = 3 << kPreferenceOffset;
uint8_t mType;
uint8_t mCode;
uint16_t mChecksum;
uint8_t mCurHopLimit;
uint8_t mFlags;
uint16_t mRouterLifetime;
uint32_t mReachableTime;
uint32_t mRetransTimer;
} OT_TOOL_PACKED_END;
static_assert(sizeof(Header) == 16, "Invalid RA `Header`");
typedef Data<kWithUint16Length> Icmp6Packet; ///< A data buffer containing an ICMPv6 packet.
/**
* This method sets the RA message to default values.
* This constructor initializes the RA message from a received packet data buffer.
*
* @param[in] aPacket A received packet data.
*
*/
void SetToDefault(void);
explicit RouterAdvertMessage(const Icmp6Packet &aPacket)
: mData(aPacket)
, mMaxLength(0)
{
}
/**
* This method sets the checksum value.
* This template constructor initializes the RA message with a given header using a given buffer to store the RA
* message.
*
* @param[in] aChecksum The checksum value.
* @tparam kBufferSize The size of the buffer used to store the RA message.
*
* @param[in] aHeader The RA message header.
* @param[in] aBuffer The data buffer to store the RA message in.
*
*/
void SetChecksum(uint16_t aChecksum) { mChecksum = HostSwap16(aChecksum); }
template <uint16_t kBufferSize>
RouterAdvertMessage(const Header &aHeader, uint8_t (&aBuffer)[kBufferSize])
: mMaxLength(kBufferSize)
{
static_assert(kBufferSize >= sizeof(Header), "Buffer for RA msg is too small");
memcpy(aBuffer, &aHeader, sizeof(Header));
mData.Init(aBuffer, sizeof(Header));
}
/**
* This method sets the Router Lifetime in seconds.
* This method gets the RA message as an `Icmp6Packet`.
*
* @param[in] aRouterLifetime The router lifetime in seconds.
* @returns The RA message as an `Icmp6Packet`.
*
*/
void SetRouterLifetime(uint16_t aRouterLifetime) { mRouterLifetime = HostSwap16(aRouterLifetime); }
const Icmp6Packet &GetAsPacket(void) const { return mData; }
/**
* This method gets the Router Lifetime (in seconds).
* This method indicates whether or not the RA message is valid.
*
* Router Lifetime set to zero indicates that the sender is not a default router.
*
* @returns The router lifetime in seconds.
* @retval TRUE If the RA message is valid.
* @retval FALSE If the RA message is not valid.
*
*/
uint16_t GetRouterLifetime(void) const { return HostSwap16(mRouterLifetime); }
bool IsValid(void) const { return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)); }
/**
* This method sets the default router preference.
* This method gets the RA message's header.
*
* @param[in] aPreference The router preference.
* @returns The RA message's header.
*
*/
void SetDefaultRouterPreference(RoutePreference aPreference);
const Header &GetHeader(void) const { return *reinterpret_cast<const Header *>(mData.GetBytes()); }
/**
* This method gets the default router preference.
* This method appends a Prefix Info Option to the RA message.
*
* @returns The router preference.
* The appended Prefix Info Option will have both on-link (L) and autonomous address-configuration (A) flags set.
*
* @param[in] aPrefix The prefix.
* @param[in] aValidLifetime The valid lifetime in seconds.
* @param[in] aPreferredLifetime The preferred lifetime in seconds.
*
* @retval kErrorNone Option is appended successfully.
* @retval kErrorNoBufs No more space in the buffer to append the option.
*
*/
RoutePreference GetDefaultRouterPreference(void) const;
Error AppendPrefixInfoOption(const Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime);
/**
* This method appends a Route Info Option to the RA message.
*
* @param[in] aPrefix The prefix.
* @param[in] aRouteLifetime The route lifetime in seconds.
* @param[in] aPreference The route preference.
*
* @retval kErrorNone Option is appended successfully.
* @retval kErrorNoBufs No more space in the buffer to append the option.
*
*/
Error AppendRouteInfoOption(const Prefix &aPrefix, uint32_t aRouteLifetime, RoutePreference aPreference);
/**
* This method indicates whether or not the RA message contains any options.
*
* @retval TRUE If the RA message contains at least one option.
* @retval FALSE If the RA message contains no options.
*
*/
bool ContainsAnyOptions(void) const { return (mData.GetLength() > sizeof(Header)); }
// The following methods are intended to support range-based `for`
// loop iteration over `Option`s in the RA message.
Option::Iterator begin(void) const { return Option::Iterator(GetOptionStart(), GetDataEnd()); }
Option::Iterator end(void) const { return Option::Iterator(); }
private:
// Router Advertisement Message
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type | Code | Checksum |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Cur Hop Limit |M|O|H|Prf|Resvd| Router Lifetime |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Reachable Time |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Retrans Timer |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Options ...
// +-+-+-+-+-+-+-+-+-+-+-+-
const uint8_t *GetOptionStart(void) const { return (mData.GetBytes() + sizeof(Header)); }
const uint8_t *GetDataEnd(void) const { return mData.GetBytes() + mData.GetLength(); }
Option * AppendOption(uint16_t aOptionSize);
static constexpr uint8_t kPreferenceOffset = 3;
static constexpr uint8_t kPreferenceMask = 3 << kPreferenceOffset;
uint8_t mType;
uint8_t mCode;
uint16_t mChecksum;
uint8_t mCurHopLimit;
uint8_t mFlags;
uint16_t mRouterLifetime;
uint32_t mReachableTime;
uint32_t mRetransTimer;
} OT_TOOL_PACKED_END;
static_assert(sizeof(RouterAdvMessage) == 16, "invalid RouterAdvMessage structure");
Data<kWithUint16Length> mData;
uint16_t mMaxLength;
};
/**
* This class implements the Router Solicitation message.