mirror of
https://github.com/espressif/openthread.git
synced 2026-08-06 10:47:46 +00:00
[border-router] add external route for on-link prefix and external OMR prefixes (#6008)
This commit includes the enhancement that 1. Adds non-default external route for on-link prefixes. 2. Add external route for OMR prefixes advertised by BRs in other Thread Networks. 3. A new API to disable/enable the Border Routing manager at run time. CLI commands are added for testing. 4. A new platform API to get the link-local address of the infra interface so that we can filter out the ICMPv6 packets from myself by the Border Routing Manager rather than the platform implementation.
This commit is contained in:
@@ -254,7 +254,7 @@ jobs:
|
||||
MULTIPLY: 1
|
||||
PYTHONUNBUFFERED: 1
|
||||
VERBOSE: 1
|
||||
# The border routing and DUA feature can coexist, but current wireshark
|
||||
# The Border Routing and DUA feature can coexist, but current wireshark
|
||||
# packet verification can't handle it because of the order of context ID
|
||||
# of OMR prefix and Domain prefix is not deterministic.
|
||||
BORDER_ROUTING: 0
|
||||
|
||||
@@ -55,16 +55,32 @@ extern "C" {
|
||||
/**
|
||||
* This method initializes the Border Routing Manager on given infrastructure interface.
|
||||
*
|
||||
* @note This method MUST be called before any other otBorderRouting* APIs.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aInfraIfIndex The infrastructure interface index.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully started the border routing manager on given infrastructure.
|
||||
* @retval OT_ERROR_NONE Successfully started the Border Routing manager on given infrastructure.
|
||||
* @retval OT_ERROR_INVALID_ARGS The index of the infra interface is not valid.
|
||||
* @retval OT_ERROR_FAILED Internal failure. This is usually failed to generate random prefixes.
|
||||
*
|
||||
*/
|
||||
otError otBorderRoutingInit(otInstance *aInstance, uint32_t aInfraIfIndex);
|
||||
|
||||
/**
|
||||
* This method enables/disables the Border Routing Manager.
|
||||
*
|
||||
* @note The Border Routing Manager is enabled by default.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aEnabled A boolean to enable/disable the routing manager.
|
||||
*
|
||||
* @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet.
|
||||
* @retval OT_ERROR_NONE Successfully enabled/disabled the Border Routing Manager.
|
||||
*
|
||||
*/
|
||||
otError otBorderRoutingSetEnabled(otInstance *aInstance, bool aEnabled);
|
||||
|
||||
/**
|
||||
* This method provides a full or stable copy of the local Thread Network Data.
|
||||
*
|
||||
|
||||
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (63)
|
||||
#define OPENTHREAD_API_VERSION (64)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -46,6 +46,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This method returns the IPv6 link-local address of given infrastructure interface.
|
||||
*
|
||||
* @param[in] aInfraIfIndex The index of the infrastructure interface.
|
||||
*
|
||||
* @returns A pointer to the IPv6 link-local address. NULL if no valid IPv6 link-local address found.
|
||||
*
|
||||
*/
|
||||
const otIp6Address *otPlatInfraIfGetLinkLocalAddress(uint32_t aInfraIfIndex);
|
||||
|
||||
/**
|
||||
* This method sends an ICMPv6 Neighbor Discovery message on given infrastructure interface.
|
||||
*
|
||||
@@ -83,8 +93,6 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
|
||||
* @note Per RFC 4861, the caller should enforce that the source address MUST be a IPv6 link-local
|
||||
* address and the IP Hop Limit MUST be 255.
|
||||
*
|
||||
* @note ICMPv6 message received from @p aInfraIfIndex via multicast loopback should not be passed in.
|
||||
*
|
||||
*/
|
||||
extern void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance,
|
||||
uint32_t aInfraIfIndex,
|
||||
|
||||
@@ -22,6 +22,7 @@ Done
|
||||
## OpenThread Command List
|
||||
|
||||
- [bbr](#bbr)
|
||||
- [br](#br)
|
||||
- [bufferinfo](#bufferinfo)
|
||||
- [ccathreshold](#ccathreshold)
|
||||
- [channel](#channel)
|
||||
@@ -312,6 +313,20 @@ Set jitter (in seconds) for Backbone Router registration for Thread 1.2 FTD.
|
||||
Done
|
||||
```
|
||||
|
||||
### br
|
||||
|
||||
Enbale/disable the Border Routing functionality.
|
||||
|
||||
```bash
|
||||
> br enable
|
||||
Done
|
||||
```
|
||||
|
||||
```bash
|
||||
> br disable
|
||||
Done
|
||||
```
|
||||
|
||||
### bufferinfo
|
||||
|
||||
Show the current message buffer information.
|
||||
|
||||
@@ -271,6 +271,34 @@ otError Interpreter::ProcessHelp(uint8_t aArgsLength, char *aArgs[])
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
otError Interpreter::ProcessBorderRouting(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
bool enable = false;
|
||||
|
||||
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
if (strcmp(aArgs[0], "enable") == 0)
|
||||
{
|
||||
enable = true;
|
||||
}
|
||||
else if (strcmp(aArgs[0], "disable") == 0)
|
||||
{
|
||||
enable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExitNow(error = OT_ERROR_INVALID_COMMAND);
|
||||
}
|
||||
|
||||
SuccessOrExit(error = otBorderRoutingSetEnabled(mInstance, enable));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
otError Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
|
||||
@@ -294,6 +294,9 @@ private:
|
||||
otError ProcessCcaThreshold(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError ProcessBufferInfo(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError ProcessChannel(uint8_t aArgsLength, char *aArgs[]);
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
otError ProcessBorderRouting(uint8_t aArgsLength, char *aArgs[]);
|
||||
#endif
|
||||
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
otError ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[]);
|
||||
|
||||
@@ -597,6 +600,9 @@ private:
|
||||
static constexpr Command sCommands[] = {
|
||||
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
{"bbr", &Interpreter::ProcessBackboneRouter},
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
{"br", &Interpreter::ProcessBorderRouting},
|
||||
#endif
|
||||
{"bufferinfo", &Interpreter::ProcessBufferInfo},
|
||||
{"ccathreshold", &Interpreter::ProcessCcaThreshold},
|
||||
|
||||
@@ -50,6 +50,13 @@ otError otBorderRoutingInit(otInstance *aInstance, uint32_t aInfraIfIndex)
|
||||
|
||||
return instance.Get<BorderRouter::RoutingManager>().Init(aInfraIfIndex);
|
||||
}
|
||||
|
||||
otError otBorderRoutingSetEnabled(otInstance *aInstance, bool aEnabled)
|
||||
{
|
||||
Instance &instance = *static_cast<Instance *>(aInstance);
|
||||
|
||||
return instance.Get<BorderRouter::RoutingManager>().SetEnabled(aEnabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
otError otBorderRouterGetNetData(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength)
|
||||
|
||||
@@ -55,12 +55,12 @@ const Option *Option::GetNextOption(const Option *aCurOption, const uint8_t *aBu
|
||||
}
|
||||
else
|
||||
{
|
||||
nextOption = reinterpret_cast<const uint8_t *>(aCurOption) + aCurOption->GetLength();
|
||||
nextOption = reinterpret_cast<const uint8_t *>(aCurOption) + aCurOption->GetSize();
|
||||
}
|
||||
|
||||
VerifyOrExit(nextOption + sizeof(Option) <= bufferEnd, nextOption = nullptr);
|
||||
VerifyOrExit(reinterpret_cast<const Option *>(nextOption)->GetLength() > 0, nextOption = nullptr);
|
||||
VerifyOrExit(nextOption + reinterpret_cast<const Option *>(nextOption)->GetLength() <= bufferEnd,
|
||||
VerifyOrExit(reinterpret_cast<const Option *>(nextOption)->GetSize() > 0, nextOption = nullptr);
|
||||
VerifyOrExit(nextOption + reinterpret_cast<const Option *>(nextOption)->GetSize() <= bufferEnd,
|
||||
nextOption = nullptr);
|
||||
|
||||
exit:
|
||||
@@ -110,9 +110,12 @@ void PrefixInfoOption::SetPrefix(const Ip6::Prefix &aPrefix)
|
||||
mPrefix = static_cast<const Ip6::Address &>(aPrefix.mPrefix);
|
||||
}
|
||||
|
||||
void PrefixInfoOption::GetPrefix(Ip6::Prefix &aPrefix) const
|
||||
Ip6::Prefix PrefixInfoOption::GetPrefix(void) const
|
||||
{
|
||||
aPrefix.Set(mPrefix.GetBytes(), mPrefixLength);
|
||||
Ip6::Prefix prefix;
|
||||
|
||||
prefix.Set(mPrefix.GetBytes(), mPrefixLength);
|
||||
return prefix;
|
||||
}
|
||||
|
||||
RouteInfoOption::RouteInfoOption(void)
|
||||
@@ -126,18 +129,64 @@ RouteInfoOption::RouteInfoOption(void)
|
||||
mPrefix.Clear();
|
||||
}
|
||||
|
||||
void RouteInfoOption::SetPreference(otRoutePreference aPreference)
|
||||
{
|
||||
mReserved &= ~kPreferenceMask;
|
||||
mReserved |= (static_cast<uint8_t>(aPreference) << kPreferenceOffset) & kPreferenceMask;
|
||||
}
|
||||
|
||||
otRoutePreference RouteInfoOption::GetPreference(void) const
|
||||
{
|
||||
otRoutePreference preference;
|
||||
|
||||
switch ((mReserved & kPreferenceMask) >> kPreferenceOffset)
|
||||
{
|
||||
case kPreferenceLow:
|
||||
preference = OT_ROUTE_PREFERENCE_LOW;
|
||||
break;
|
||||
case kPreferenceMed:
|
||||
preference = OT_ROUTE_PREFERENCE_MED;
|
||||
break;
|
||||
case kPreferenceHigh:
|
||||
preference = OT_ROUTE_PREFERENCE_HIGH;
|
||||
break;
|
||||
default:
|
||||
preference = OT_ROUTE_PREFERENCE_LOW;
|
||||
break;
|
||||
}
|
||||
|
||||
return preference;
|
||||
}
|
||||
|
||||
void RouteInfoOption::SetPrefix(const Ip6::Prefix &aPrefix)
|
||||
{
|
||||
// The total length (in bytes) of a Router Information Option
|
||||
// is: (8 bytes fixed option header) + (0, 8, or 16 bytes prefix).
|
||||
// Because the length of the option must be padded with 8 bytes,
|
||||
// the length of the prefix (in bits) must be padded with 64 bits.
|
||||
SetLength(((aPrefix.mLength + kLengthUnit * CHAR_BIT - 1) / (kLengthUnit * CHAR_BIT) + 1) * kLengthUnit);
|
||||
SetLength((aPrefix.mLength + kLengthUnit * CHAR_BIT - 1) / (kLengthUnit * CHAR_BIT) + 1);
|
||||
|
||||
mPrefixLength = aPrefix.mLength;
|
||||
mPrefix = static_cast<const Ip6::Address &>(aPrefix.mPrefix);
|
||||
}
|
||||
|
||||
Ip6::Prefix RouteInfoOption::GetPrefix(void) const
|
||||
{
|
||||
Ip6::Prefix prefix;
|
||||
|
||||
prefix.Set(mPrefix.GetBytes(), mPrefixLength);
|
||||
return prefix;
|
||||
}
|
||||
|
||||
bool RouteInfoOption::IsValid(void) const
|
||||
{
|
||||
otRoutePreference pref = GetPreference();
|
||||
|
||||
return (GetLength() == 1 || GetLength() == 2 || GetLength() == 3) &&
|
||||
(mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT) &&
|
||||
(pref == OT_ROUTE_PREFERENCE_LOW || pref == OT_ROUTE_PREFERENCE_MED || pref == OT_ROUTE_PREFERENCE_HIGH);
|
||||
}
|
||||
|
||||
RouterAdvMessage::RouterAdvMessage(void)
|
||||
: mReachableTime(0)
|
||||
, mRetransTimer(0)
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <openthread/netdata.h>
|
||||
#include <openthread/platform/toolchain.h>
|
||||
|
||||
#include "common/encoding.hpp"
|
||||
@@ -103,23 +104,39 @@ public:
|
||||
Type GetType(void) const { return mType; }
|
||||
|
||||
/**
|
||||
* This method sets the length of the option (in bytes).
|
||||
* This method sets the size of the option (in bytes).
|
||||
*
|
||||
* Since the option must end on their natural 64-bits boundaries,
|
||||
* the actual length set to the option is padded to (aLength + 7) / 8 * 8.
|
||||
* the actual length set to the option is padded to (aSize + 7) / 8 * 8.
|
||||
*
|
||||
* @param[in] aLength The length of the option in unit of 1 byte.
|
||||
* @param[in] aSize The size of the option in unit of 1 byte.
|
||||
*
|
||||
*/
|
||||
void SetLength(uint16_t aLength) { mLength = (aLength + kLengthUnit - 1) / kLengthUnit; }
|
||||
void SetSize(uint16_t aSize) { mLength = (aSize + kLengthUnit - 1) / kLengthUnit; }
|
||||
|
||||
/**
|
||||
* This method returns the length of the option (in bytes).
|
||||
* This method returns the size of the option (in bytes).
|
||||
*
|
||||
* @returns The length of the option.
|
||||
* @returns The size of the option in unit of 1 byte.
|
||||
*
|
||||
*/
|
||||
uint16_t GetLength(void) const { return mLength * 8; }
|
||||
uint16_t GetSize(void) const { return mLength * kLengthUnit; }
|
||||
|
||||
/**
|
||||
* This method sets the length of the option (in unit of 8 bytes).
|
||||
*
|
||||
* @param[in] aLength The length of the option in unit of 8 bytes.
|
||||
*
|
||||
*/
|
||||
void SetLength(uint8_t aLength) { mLength = aLength; }
|
||||
|
||||
/**
|
||||
* This method returns the length of the option (in unit of 8 bytes).
|
||||
*
|
||||
* @returns The length of the option in unit of 8 bytes.
|
||||
*
|
||||
*/
|
||||
uint16_t GetLength(void) const { return mLength; }
|
||||
|
||||
/**
|
||||
* This helper method returns a pointer to the next valid option in the buffer.
|
||||
@@ -215,12 +232,12 @@ public:
|
||||
void SetPrefix(const Ip6::Prefix &aPrefix);
|
||||
|
||||
/**
|
||||
* THis method returns the prefix in this option.
|
||||
* This method returns the prefix in this option.
|
||||
*
|
||||
* @param[out] aPrefix The prefix to output to.
|
||||
* @returns The IPv6 prefix in this option.
|
||||
*
|
||||
*/
|
||||
void GetPrefix(Ip6::Prefix &aPrefix) const;
|
||||
Ip6::Prefix GetPrefix(void) const;
|
||||
|
||||
/**
|
||||
* This method tells whether this option is valid.
|
||||
@@ -230,7 +247,7 @@ public:
|
||||
*/
|
||||
bool IsValid(void) const
|
||||
{
|
||||
return (GetLength() == sizeof(*this)) && (mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT);
|
||||
return (GetSize() == sizeof(*this)) && (mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -267,6 +284,22 @@ public:
|
||||
*/
|
||||
RouteInfoOption(void);
|
||||
|
||||
/**
|
||||
* This method sets the route preference.
|
||||
*
|
||||
* @param[in] aPreference The route preference.
|
||||
*
|
||||
*/
|
||||
void SetPreference(otRoutePreference aPreference);
|
||||
|
||||
/**
|
||||
* This method returns the route preference.
|
||||
*
|
||||
* @returns The route preference.
|
||||
*
|
||||
*/
|
||||
otRoutePreference GetPreference(void) const;
|
||||
|
||||
/**
|
||||
* This method sets the lifetime of the route in seconds.
|
||||
*
|
||||
@@ -275,6 +308,14 @@ public:
|
||||
*/
|
||||
void SetRouteLifetime(uint32_t aLifetime) { mRouteLifetime = HostSwap32(aLifetime); }
|
||||
|
||||
/**
|
||||
* This method returns Route Lifetime in seconds.
|
||||
*
|
||||
* @returns The Route Lifetime in seconds.
|
||||
*
|
||||
*/
|
||||
uint32_t GetRouteLifetime(void) const { return HostSwap32(mRouteLifetime); }
|
||||
|
||||
/**
|
||||
* This method sets the prefix.
|
||||
*
|
||||
@@ -283,19 +324,36 @@ public:
|
||||
*/
|
||||
void SetPrefix(const Ip6::Prefix &aPrefix);
|
||||
|
||||
/**
|
||||
* This method returns the prefix in this option.
|
||||
*
|
||||
* @returns The IPv6 prefix in this option.
|
||||
*
|
||||
*/
|
||||
Ip6::Prefix GetPrefix(void) const;
|
||||
|
||||
/**
|
||||
* This method tells whether this option is valid.
|
||||
*
|
||||
* @returns A boolean indicates whether this option is valid.
|
||||
*
|
||||
*/
|
||||
bool IsValid(void) const
|
||||
{
|
||||
return (GetLength() == kLengthUnit || GetLength() == 2 * kLengthUnit || GetLength() == 3 * kLengthUnit) &&
|
||||
(mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT);
|
||||
}
|
||||
bool IsValid(void) const;
|
||||
|
||||
private:
|
||||
enum : uint8_t
|
||||
{
|
||||
kPreferenceMask = 0x18u,
|
||||
kPreferenceOffset = 3u,
|
||||
};
|
||||
|
||||
enum : uint8_t
|
||||
{
|
||||
kPreferenceLow = 0x03,
|
||||
kPreferenceMed = 0x00,
|
||||
kPreferenceHigh = 0x01,
|
||||
};
|
||||
|
||||
uint8_t mPrefixLength; // The prefix length in bits.
|
||||
uint8_t mReserved; // The reserved field.
|
||||
uint32_t mRouteLifetime; // The lifetime in seconds.
|
||||
|
||||
@@ -61,26 +61,29 @@ RoutingManager::RoutingManager(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mIsRunning(false)
|
||||
, mInfraIfIndex(0)
|
||||
, mEnabled(true) // The routing manager is by default enabled.
|
||||
, mAdvertisedOmrPrefixNum(0)
|
||||
, mAdvertisedOnLinkPrefix(nullptr)
|
||||
, mDiscoveredPrefixNum(0)
|
||||
, mDiscoveredPrefixInvalidTimer(aInstance, HandleDiscoveredPrefixInvalidTimer, this)
|
||||
, mRouterAdvertisementTimer(aInstance, HandleRouterAdvertisementTimer, this)
|
||||
, mRouterAdvertisementCount(0)
|
||||
, mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer, this)
|
||||
, mRouterSolicitCount(0)
|
||||
, mDiscoveredOnLinkPrefixInvalidTimer(aInstance, HandleDiscoveredOnLinkPrefixInvalidTimer, this)
|
||||
{
|
||||
mLocalOmrPrefix.Clear();
|
||||
memset(mAdvertisedOmrPrefixes, 0, sizeof(mAdvertisedOmrPrefixes));
|
||||
|
||||
mLocalOnLinkPrefix.Clear();
|
||||
mDiscoveredOnLinkPrefix.Clear();
|
||||
|
||||
memset(mDiscoveredPrefixes, 0, sizeof(mDiscoveredPrefixes));
|
||||
}
|
||||
|
||||
otError RoutingManager::Init(uint32_t aInfraIfIndex)
|
||||
{
|
||||
otError error;
|
||||
|
||||
OT_ASSERT(!IsInitialized() && !Get<Mle::MleRouter>().IsAttached());
|
||||
OT_ASSERT(!IsInitialized());
|
||||
VerifyOrExit(aInfraIfIndex > 0, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
SuccessOrExit(error = LoadOrGenerateRandomOmrPrefix());
|
||||
@@ -92,6 +95,29 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError RoutingManager::SetEnabled(bool aEnabled)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(IsInitialized(), error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
VerifyOrExit(aEnabled != mEnabled);
|
||||
|
||||
mEnabled = aEnabled;
|
||||
|
||||
if (!mEnabled)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
else if (Get<Mle::MleRouter>().IsAttached())
|
||||
{
|
||||
Start();
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError RoutingManager::LoadOrGenerateRandomOmrPrefix(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
@@ -150,6 +176,8 @@ void RoutingManager::Start(void)
|
||||
{
|
||||
if (!mIsRunning)
|
||||
{
|
||||
otLogInfoBr("Border Routing manager started");
|
||||
|
||||
mIsRunning = true;
|
||||
StartRouterSolicitation();
|
||||
}
|
||||
@@ -160,6 +188,11 @@ void RoutingManager::Stop(void)
|
||||
VerifyOrExit(mIsRunning);
|
||||
|
||||
UnpublishLocalOmrPrefix();
|
||||
if (mAdvertisedOnLinkPrefix != nullptr)
|
||||
{
|
||||
RemoveExternalRoute(*mAdvertisedOnLinkPrefix);
|
||||
}
|
||||
InvalidateAllDiscoveredPrefixes();
|
||||
|
||||
// Use empty OMR & on-link prefixes to invalidate possible advertised prefixes.
|
||||
SendRouterAdvertisement(nullptr, 0, nullptr);
|
||||
@@ -169,8 +202,9 @@ void RoutingManager::Stop(void)
|
||||
|
||||
mAdvertisedOnLinkPrefix = nullptr;
|
||||
|
||||
mDiscoveredOnLinkPrefix.Clear();
|
||||
mDiscoveredOnLinkPrefixInvalidTimer.Stop();
|
||||
memset(mDiscoveredPrefixes, 0, sizeof(mDiscoveredPrefixes));
|
||||
mDiscoveredPrefixNum = 0;
|
||||
mDiscoveredPrefixInvalidTimer.Stop();
|
||||
|
||||
mRouterAdvertisementTimer.Stop();
|
||||
mRouterAdvertisementCount = 0;
|
||||
@@ -178,6 +212,8 @@ void RoutingManager::Stop(void)
|
||||
mRouterSolicitTimer.Stop();
|
||||
mRouterSolicitCount = 0;
|
||||
|
||||
otLogInfoBr("Border Routing manager stopped");
|
||||
|
||||
mIsRunning = false;
|
||||
|
||||
exit:
|
||||
@@ -189,12 +225,19 @@ void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex,
|
||||
const uint8_t * aBuffer,
|
||||
uint16_t aBufferLength)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
const Ip6::Icmp::Header *icmp6Header;
|
||||
const Ip6::Address * infraLinkLocalAddr;
|
||||
|
||||
VerifyOrExit(mIsRunning);
|
||||
VerifyOrExit(IsInitialized() && mIsRunning, error = OT_ERROR_DROP);
|
||||
|
||||
VerifyOrExit(aInfraIfIndex == mInfraIfIndex);
|
||||
VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(*icmp6Header));
|
||||
VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP);
|
||||
infraLinkLocalAddr = static_cast<const Ip6::Address *>(otPlatInfraIfGetLinkLocalAddress(mInfraIfIndex));
|
||||
|
||||
// Drop any ICMPv6 messages sent from myself.
|
||||
VerifyOrExit(infraLinkLocalAddr != nullptr && aSrcAddress != *infraLinkLocalAddr, error = OT_ERROR_DROP);
|
||||
|
||||
VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(*icmp6Header), error = OT_ERROR_PARSE);
|
||||
|
||||
icmp6Header = reinterpret_cast<const Ip6::Icmp::Header *>(aBuffer);
|
||||
|
||||
@@ -211,12 +254,15 @@ void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex,
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otLogDebgBr("drop ICMPv6 message: %s", otThreadErrorToString(error));
|
||||
}
|
||||
}
|
||||
|
||||
void RoutingManager::HandleNotifierEvents(Events aEvents)
|
||||
{
|
||||
VerifyOrExit(IsInitialized());
|
||||
VerifyOrExit(IsInitialized() && IsEnabled());
|
||||
|
||||
if (aEvents.Contains(kEventThreadRoleChanged))
|
||||
{
|
||||
@@ -253,8 +299,7 @@ uint8_t RoutingManager::EvaluateOmrPrefix(Ip6::Prefix *aNewOmrPrefixes, uint8_t
|
||||
{
|
||||
uint8_t newPrefixIndex;
|
||||
|
||||
if (!IsValidOmrPrefix(onMeshPrefixConfig.GetPrefix()) || !onMeshPrefixConfig.mDefaultRoute ||
|
||||
!onMeshPrefixConfig.mSlaac || onMeshPrefixConfig.mDp)
|
||||
if (!IsValidOmrPrefix(onMeshPrefixConfig.GetPrefix()) || !onMeshPrefixConfig.mSlaac || onMeshPrefixConfig.mDp)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -327,12 +372,13 @@ otError RoutingManager::PublishLocalOmrPrefix(void)
|
||||
omrPrefixConfig.mSlaac = true;
|
||||
omrPrefixConfig.mPreferred = true;
|
||||
omrPrefixConfig.mOnMesh = true;
|
||||
omrPrefixConfig.mDefaultRoute = true;
|
||||
omrPrefixConfig.mDefaultRoute = false;
|
||||
omrPrefixConfig.mPreference = OT_ROUTE_PREFERENCE_MED;
|
||||
|
||||
error = Get<NetworkData::Local>().AddOnMeshPrefix(omrPrefixConfig);
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otLogInfoBr("failed to publish local OMR prefix %s in Thread network: %s",
|
||||
otLogWarnBr("failed to publish local OMR prefix %s in Thread network: %s",
|
||||
mLocalOmrPrefix.ToString().AsCString(), otThreadErrorToString(error));
|
||||
}
|
||||
else
|
||||
@@ -346,15 +392,67 @@ otError RoutingManager::PublishLocalOmrPrefix(void)
|
||||
|
||||
void RoutingManager::UnpublishLocalOmrPrefix(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(mIsRunning);
|
||||
|
||||
IgnoreError(Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalOmrPrefix));
|
||||
Get<NetworkData::Notifier>().HandleServerDataUpdated();
|
||||
SuccessOrExit(error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalOmrPrefix));
|
||||
|
||||
otLogInfoBr("unpubished local OMR prefix %s from Thread network", mLocalOmrPrefix.ToString().AsCString());
|
||||
Get<NetworkData::Notifier>().HandleServerDataUpdated();
|
||||
otLogInfoBr("unpublished local OMR prefix %s from Thread network", mLocalOmrPrefix.ToString().AsCString());
|
||||
|
||||
exit:
|
||||
return;
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otLogWarnBr("failed to unpublish local OMR prefix %s from Thread network: %s",
|
||||
mLocalOmrPrefix.ToString().AsCString(), otThreadErrorToString(error));
|
||||
}
|
||||
}
|
||||
|
||||
otError RoutingManager::AddExternalRoute(const Ip6::Prefix &aPrefix, otRoutePreference aRoutePreference)
|
||||
{
|
||||
otError error;
|
||||
NetworkData::ExternalRouteConfig routeConfig;
|
||||
|
||||
OT_ASSERT(mIsRunning);
|
||||
|
||||
routeConfig.Clear();
|
||||
routeConfig.SetPrefix(aPrefix);
|
||||
routeConfig.mStable = true;
|
||||
routeConfig.mPreference = aRoutePreference;
|
||||
|
||||
error = Get<NetworkData::Local>().AddHasRoutePrefix(routeConfig);
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otLogWarnBr("failed to add external route %s: %s", aPrefix.ToString().AsCString(),
|
||||
otThreadErrorToString(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
Get<NetworkData::Notifier>().HandleServerDataUpdated();
|
||||
otLogInfoBr("added external route %s", aPrefix.ToString().AsCString());
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void RoutingManager::RemoveExternalRoute(const Ip6::Prefix &aPrefix)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(mIsRunning);
|
||||
|
||||
SuccessOrExit(error = Get<NetworkData::Local>().RemoveHasRoutePrefix(aPrefix));
|
||||
|
||||
Get<NetworkData::Notifier>().HandleServerDataUpdated();
|
||||
otLogInfoBr("removed external route %s", aPrefix.ToString().AsCString());
|
||||
|
||||
exit:
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otLogWarnBr("failed to remove external route %s: %s", aPrefix.ToString().AsCString(),
|
||||
otThreadErrorToString(error));
|
||||
}
|
||||
}
|
||||
|
||||
bool RoutingManager::ContainsPrefix(const Ip6::Prefix &aPrefix, const Ip6::Prefix *aPrefixList, uint8_t aPrefixNum)
|
||||
@@ -373,27 +471,74 @@ bool RoutingManager::ContainsPrefix(const Ip6::Prefix &aPrefix, const Ip6::Prefi
|
||||
return ret;
|
||||
}
|
||||
|
||||
const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) const
|
||||
const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void)
|
||||
{
|
||||
const Ip6::Prefix *newOnLinkPrefix = nullptr;
|
||||
const Ip6::Prefix *newOnLinkPrefix = nullptr;
|
||||
const Ip6::Prefix *smallestOnLinkPrefix = nullptr;
|
||||
|
||||
// We don't evaluate on-link prefix if we are doing
|
||||
// Router Discovery or we have already discovered some
|
||||
// on-link prefixes.
|
||||
// We don't evaluate on-link prefix if we are doing Router Solicitation.
|
||||
VerifyOrExit(!mRouterSolicitTimer.IsRunning());
|
||||
if (IsValidOnLinkPrefix(mDiscoveredOnLinkPrefix))
|
||||
|
||||
for (uint8_t i = 0; i < mDiscoveredPrefixNum; ++i)
|
||||
{
|
||||
otLogInfoBr("EvaluateOnLinkPrefix: there is already on-link prefix %s on interface %u",
|
||||
mDiscoveredOnLinkPrefix.ToString().AsCString(), mInfraIfIndex);
|
||||
ExitNow();
|
||||
ExternalPrefix &prefix = mDiscoveredPrefixes[i];
|
||||
|
||||
if (!prefix.mIsOnLinkPrefix)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (smallestOnLinkPrefix == nullptr || IsPrefixSmallerThan(prefix.mPrefix, *smallestOnLinkPrefix))
|
||||
{
|
||||
smallestOnLinkPrefix = &prefix.mPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
newOnLinkPrefix = &mLocalOnLinkPrefix;
|
||||
// We start advertising our local on-link prefix if there is no existing one.
|
||||
if (smallestOnLinkPrefix == nullptr)
|
||||
{
|
||||
if (mAdvertisedOnLinkPrefix != nullptr)
|
||||
{
|
||||
newOnLinkPrefix = mAdvertisedOnLinkPrefix;
|
||||
}
|
||||
else if (AddExternalRoute(mLocalOnLinkPrefix, OT_ROUTE_PREFERENCE_MED) == OT_ERROR_NONE)
|
||||
{
|
||||
newOnLinkPrefix = &mLocalOnLinkPrefix;
|
||||
}
|
||||
}
|
||||
// When an application-specific on-link prefix is received and it is bigger than the
|
||||
// advertised prefix, we will not remove the advertised prefix. In this case, there
|
||||
// will be two on-link prefixes on the infra link. But all BRs will still converge to
|
||||
// the same smallest on-link prefix and the application-specific prefix is not used.
|
||||
else if (mAdvertisedOnLinkPrefix != nullptr)
|
||||
{
|
||||
if (IsPrefixSmallerThan(*mAdvertisedOnLinkPrefix, *smallestOnLinkPrefix))
|
||||
{
|
||||
newOnLinkPrefix = mAdvertisedOnLinkPrefix;
|
||||
}
|
||||
else
|
||||
{
|
||||
otLogInfoBr("EvaluateOnLinkPrefix: there is already smaller on-link prefix %s on interface %u",
|
||||
smallestOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
|
||||
|
||||
// TODO: we removes the advertised on-link prefix by setting valid lifetime for PIO.
|
||||
// But SLAAC addresses configured by PIO prefix will not be removed immediately (
|
||||
// https://tools.ietf.org/html/rfc4862#section-5.5.3). This leads to a situation that
|
||||
// a WiFi device keeps using the old SLAAC address but a Thread device cannot reach to
|
||||
// it. One solution is to delay removing external route until the SLAAC addresses are
|
||||
// actually expired/deprecated.
|
||||
RemoveExternalRoute(*mAdvertisedOnLinkPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return newOnLinkPrefix;
|
||||
}
|
||||
|
||||
// This method evaluate the routing policy depends on prefix and route
|
||||
// information on Thread Network and infra link. As a result, this
|
||||
// method May send RA messages on infra link and publish/unpublish
|
||||
// OMR prefix in the Thread network.
|
||||
void RoutingManager::EvaluateRoutingPolicy(void)
|
||||
{
|
||||
const Ip6::Prefix *newOnLinkPrefix = nullptr;
|
||||
@@ -430,7 +575,7 @@ void RoutingManager::EvaluateRoutingPolicy(void)
|
||||
}
|
||||
|
||||
otLogInfoBr("router advertisement scheduled in %u seconds", nextSendTime);
|
||||
mRouterAdvertisementTimer.Start(nextSendTime * 1000);
|
||||
mRouterAdvertisementTimer.Start(Time::SecToMsec(nextSendTime));
|
||||
}
|
||||
|
||||
// 3. Update advertised on-link & OMR prefixes information.
|
||||
@@ -443,10 +588,19 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
// starts sending Router Solicitations in random delay
|
||||
// between 0 and kMaxRtrSolicitationDelay.
|
||||
void RoutingManager::StartRouterSolicitation(void)
|
||||
{
|
||||
uint32_t randomDelay;
|
||||
|
||||
mRouterSolicitCount = 0;
|
||||
mRouterSolicitTimer.Start(Random::NonCrypto::GetUint32InRange(0, kMaxRtrSolicitationDelay * 1000));
|
||||
|
||||
static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay");
|
||||
randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay));
|
||||
|
||||
otLogInfoBr("start Router Solicitation, scheduled in %u milliseconds", randomDelay);
|
||||
mRouterSolicitTimer.Start(randomDelay);
|
||||
}
|
||||
|
||||
otError RoutingManager::SendRouterSolicitation(void)
|
||||
@@ -461,6 +615,13 @@ otError RoutingManager::SendRouterSolicitation(void)
|
||||
sizeof(routerSolicit));
|
||||
}
|
||||
|
||||
// This method sends Router Advertisement messages to advertise on-link prefix and route for OMR prefix.
|
||||
// @param[in] aNewOmrPrefixes A pointer to an array of the new OMR prefixes to be advertised.
|
||||
// @p aNewOmrPrefixNum must be zero if this argument is nullptr.
|
||||
// @param[in] aNewOmrPrefixNum The number of the new OMR prefixes to be advertised.
|
||||
// Zero means we should stop advertising OMR prefixes.
|
||||
// @param[in] aOnLinkPrefix A pointer to the new on-link prefix to be advertised.
|
||||
// nullptr means we should stop advertising on-link prefix.
|
||||
void RoutingManager::SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
|
||||
uint8_t aNewOmrPrefixNum,
|
||||
const Ip6::Prefix *aNewOnLinkPrefix)
|
||||
@@ -488,11 +649,11 @@ void RoutingManager::SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
|
||||
pio.SetPreferredLifetime(kDefaultOnLinkPrefixLifetime);
|
||||
pio.SetPrefix(*aNewOnLinkPrefix);
|
||||
|
||||
OT_ASSERT(bufferLength + pio.GetLength() <= sizeof(buffer));
|
||||
memcpy(buffer + bufferLength, &pio, pio.GetLength());
|
||||
bufferLength += pio.GetLength();
|
||||
OT_ASSERT(bufferLength + pio.GetSize() <= sizeof(buffer));
|
||||
memcpy(buffer + bufferLength, &pio, pio.GetSize());
|
||||
bufferLength += pio.GetSize();
|
||||
|
||||
if (mAdvertisedOnLinkPrefix != nullptr)
|
||||
if (mAdvertisedOnLinkPrefix == nullptr)
|
||||
{
|
||||
otLogInfoBr("start advertising new on-link prefix %s on interface %u",
|
||||
aNewOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
|
||||
@@ -505,14 +666,17 @@ void RoutingManager::SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
|
||||
{
|
||||
RouterAdv::PrefixInfoOption pio;
|
||||
|
||||
pio.SetOnLink(true);
|
||||
pio.SetAutoAddrConfig(true);
|
||||
|
||||
// Set zero valid lifetime to immediately invalidate the advertised on-link prefix.
|
||||
pio.SetValidLifetime(0);
|
||||
pio.SetPreferredLifetime(0);
|
||||
pio.SetPrefix(*mAdvertisedOnLinkPrefix);
|
||||
|
||||
OT_ASSERT(bufferLength + pio.GetLength() <= sizeof(buffer));
|
||||
memcpy(buffer + bufferLength, &pio, pio.GetLength());
|
||||
bufferLength += pio.GetLength();
|
||||
OT_ASSERT(bufferLength + pio.GetSize() <= sizeof(buffer));
|
||||
memcpy(buffer + bufferLength, &pio, pio.GetSize());
|
||||
bufferLength += pio.GetSize();
|
||||
|
||||
otLogInfoBr("stop advertising on-link prefix %s on interface %u",
|
||||
mAdvertisedOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
|
||||
@@ -531,9 +695,9 @@ void RoutingManager::SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
|
||||
rio.SetRouteLifetime(0);
|
||||
rio.SetPrefix(advertisedOmrPrefix);
|
||||
|
||||
OT_ASSERT(bufferLength + rio.GetLength() <= sizeof(buffer));
|
||||
memcpy(buffer + bufferLength, &rio, rio.GetLength());
|
||||
bufferLength += rio.GetLength();
|
||||
OT_ASSERT(bufferLength + rio.GetSize() <= sizeof(buffer));
|
||||
memcpy(buffer + bufferLength, &rio, rio.GetSize());
|
||||
bufferLength += rio.GetSize();
|
||||
|
||||
otLogInfoBr("stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(),
|
||||
mInfraIfIndex);
|
||||
@@ -548,9 +712,9 @@ void RoutingManager::SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
|
||||
rio.SetRouteLifetime(kDefaultOmrPrefixLifetime);
|
||||
rio.SetPrefix(newOmrPrefix);
|
||||
|
||||
OT_ASSERT(bufferLength + rio.GetLength() <= sizeof(buffer));
|
||||
memcpy(buffer + bufferLength, &rio, rio.GetLength());
|
||||
bufferLength += rio.GetLength();
|
||||
OT_ASSERT(bufferLength + rio.GetSize() <= sizeof(buffer));
|
||||
memcpy(buffer + bufferLength, &rio, rio.GetSize());
|
||||
bufferLength += rio.GetSize();
|
||||
|
||||
otLogInfoBr("send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(),
|
||||
kDefaultOmrPrefixLifetime);
|
||||
@@ -649,7 +813,7 @@ void RoutingManager::HandleRouterSolicitTimer(void)
|
||||
(mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval;
|
||||
|
||||
otLogDebgBr("router solicitation timer scheduled in %u seconds", nextSolicitationDelay);
|
||||
mRouterSolicitTimer.Start(nextSolicitationDelay * 1000);
|
||||
mRouterSolicitTimer.Start(Time::SecToMsec(nextSolicitationDelay));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -658,33 +822,38 @@ void RoutingManager::HandleRouterSolicitTimer(void)
|
||||
}
|
||||
}
|
||||
|
||||
void RoutingManager::HandleDiscoveredOnLinkPrefixInvalidTimer(Timer &aTimer)
|
||||
void RoutingManager::HandleDiscoveredPrefixInvalidTimer(Timer &aTimer)
|
||||
{
|
||||
aTimer.GetOwner<RoutingManager>().HandleDiscoveredOnLinkPrefixInvalidTimer();
|
||||
aTimer.GetOwner<RoutingManager>().HandleDiscoveredPrefixInvalidTimer();
|
||||
}
|
||||
|
||||
void RoutingManager::HandleDiscoveredOnLinkPrefixInvalidTimer(void)
|
||||
void RoutingManager::HandleDiscoveredPrefixInvalidTimer(void)
|
||||
{
|
||||
otLogInfoBr("invalidate discovered on-link prefix: %s", mDiscoveredOnLinkPrefix.ToString().AsCString());
|
||||
mDiscoveredOnLinkPrefix.Clear();
|
||||
|
||||
// The discovered on-link prefix becomes invalid, start Router Solicitation
|
||||
// to discover new one.
|
||||
StartRouterSolicitation();
|
||||
InvalidateDiscoveredPrefixes();
|
||||
}
|
||||
|
||||
void RoutingManager::HandleRouterSolicit(const Ip6::Address &aSrcAddress,
|
||||
const uint8_t * aBuffer,
|
||||
uint16_t aBufferLength)
|
||||
{
|
||||
uint32_t randomDelay;
|
||||
|
||||
OT_UNUSED_VARIABLE(aSrcAddress);
|
||||
OT_UNUSED_VARIABLE(aBuffer);
|
||||
OT_UNUSED_VARIABLE(aBufferLength);
|
||||
|
||||
VerifyOrExit(!mRouterSolicitTimer.IsRunning());
|
||||
|
||||
otLogInfoBr("received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(),
|
||||
mInfraIfIndex);
|
||||
|
||||
mRouterAdvertisementTimer.Start(Random::NonCrypto::GetUint32InRange(0, kMaxRaDelayTime));
|
||||
randomDelay = Random::NonCrypto::GetUint32InRange(0, kMaxRaDelayTime);
|
||||
|
||||
otLogInfoBr("Router Advertisement scheduled in %u milliseconds", randomDelay);
|
||||
mRouterAdvertisementTimer.Start(randomDelay);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t RoutingManager::GetPrefixExpireDelay(uint32_t aValidLifetime)
|
||||
@@ -711,6 +880,7 @@ void RoutingManager::HandleRouterAdvertisement(const Ip6::Address &aSrcAddress,
|
||||
|
||||
using RouterAdv::Option;
|
||||
using RouterAdv::PrefixInfoOption;
|
||||
using RouterAdv::RouteInfoOption;
|
||||
using RouterAdv::RouterAdvMessage;
|
||||
|
||||
bool needReevaluate = false;
|
||||
@@ -729,53 +899,32 @@ void RoutingManager::HandleRouterAdvertisement(const Ip6::Address &aSrcAddress,
|
||||
option = nullptr;
|
||||
while ((option = Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr)
|
||||
{
|
||||
const PrefixInfoOption *pio;
|
||||
Ip6::Prefix prefix;
|
||||
|
||||
if (option->GetType() != Option::Type::kPrefixInfo)
|
||||
switch (option->GetType())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pio = static_cast<const PrefixInfoOption *>(option);
|
||||
|
||||
if (!pio->IsValid())
|
||||
case Option::Type::kPrefixInfo:
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const PrefixInfoOption *pio = static_cast<const PrefixInfoOption *>(option);
|
||||
|
||||
pio->GetPrefix(prefix);
|
||||
if (!IsValidOnLinkPrefix(prefix))
|
||||
{
|
||||
otLogInfoBr("ignore invalid prefix in PIO: %s", prefix.ToString().AsCString());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pio->GetValidLifetime() == 0)
|
||||
{
|
||||
if (mDiscoveredOnLinkPrefix == prefix)
|
||||
if (pio->IsValid())
|
||||
{
|
||||
otLogInfoBr("invalidate discovered on-link prefix %s", prefix.ToString().AsCString());
|
||||
mDiscoveredOnLinkPrefixInvalidTimer.Stop();
|
||||
mDiscoveredOnLinkPrefix.Clear();
|
||||
needReevaluate = true;
|
||||
needReevaluate |= UpdateDiscoveredPrefixes(*pio);
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
case Option::Type::kRouteInfo:
|
||||
{
|
||||
otLogInfoBr("set discovered on-link prefix to %s, valid lifetime: %u seconds",
|
||||
prefix.ToString().AsCString(), pio->GetValidLifetime());
|
||||
const RouteInfoOption *rio = static_cast<const RouteInfoOption *>(option);
|
||||
|
||||
// We keep tracking the latest on-link prefix.
|
||||
mDiscoveredOnLinkPrefixInvalidTimer.Start(GetPrefixExpireDelay(pio->GetValidLifetime()));
|
||||
mDiscoveredOnLinkPrefix = prefix;
|
||||
if (rio->IsValid())
|
||||
{
|
||||
needReevaluate |= UpdateDiscoveredPrefixes(*rio);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Stop Router Solicitation if we found a valid on-link prefix.
|
||||
// Otherwise, we wait till the Router Solicitation process times out.
|
||||
// So the maximum delay before the Border Router starts advertising
|
||||
// its own on-link prefix is 9 (4 + 4 + 1) seconds.
|
||||
mRouterSolicitTimer.Stop();
|
||||
needReevaluate = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -788,6 +937,186 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
bool RoutingManager::UpdateDiscoveredPrefixes(const RouterAdv::PrefixInfoOption &aPio)
|
||||
{
|
||||
Ip6::Prefix prefix = aPio.GetPrefix();
|
||||
bool needReevaluate = false;
|
||||
|
||||
if (!IsValidOnLinkPrefix(prefix))
|
||||
{
|
||||
otLogInfoBr("ignore invalid prefix in PIO: %s", prefix.ToString().AsCString());
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
otLogInfoBr("discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
|
||||
aPio.GetValidLifetime(), mInfraIfIndex);
|
||||
|
||||
if (aPio.GetValidLifetime() == 0)
|
||||
{
|
||||
needReevaluate = InvalidateDiscoveredPrefixes(&prefix, /* aIsOnLinkPrefix */ true) > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
needReevaluate = AddDiscoveredPrefix(prefix, /* aIsOnLinkPrefix */ true, aPio.GetValidLifetime());
|
||||
}
|
||||
|
||||
exit:
|
||||
return needReevaluate;
|
||||
}
|
||||
|
||||
bool RoutingManager::UpdateDiscoveredPrefixes(const RouterAdv::RouteInfoOption &aRio)
|
||||
{
|
||||
Ip6::Prefix prefix = aRio.GetPrefix();
|
||||
bool needReevaluate = false;
|
||||
|
||||
if (!IsValidOmrPrefix(prefix))
|
||||
{
|
||||
otLogInfoBr("ignore invalid prefix in RIO: %s", prefix.ToString().AsCString());
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
// Ignore the OMR prefix that matches what we have advertised.
|
||||
VerifyOrExit(!ContainsPrefix(prefix, mAdvertisedOmrPrefixes, mAdvertisedOmrPrefixNum));
|
||||
|
||||
otLogInfoBr("discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
|
||||
aRio.GetRouteLifetime(), mInfraIfIndex);
|
||||
|
||||
if (aRio.GetRouteLifetime() == 0)
|
||||
{
|
||||
needReevaluate = (InvalidateDiscoveredPrefixes(&prefix, /* aIsOnLinkPrefix */ false) > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
needReevaluate =
|
||||
AddDiscoveredPrefix(prefix, /* aIsOnLinkPrefix */ false, aRio.GetRouteLifetime(), aRio.GetPreference());
|
||||
}
|
||||
|
||||
exit:
|
||||
return needReevaluate;
|
||||
}
|
||||
|
||||
bool RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bool aIsOnLinkPrefix)
|
||||
{
|
||||
uint8_t removedNum = 0;
|
||||
ExternalPrefix *keptPrefix = mDiscoveredPrefixes;
|
||||
TimeMilli now = TimerMilli::GetNow();
|
||||
TimeMilli earliestExpireTime = now.GetDistantFuture();
|
||||
uint8_t keptOnLinkPrefixNum = 0;
|
||||
|
||||
for (uint8_t i = 0; i < mDiscoveredPrefixNum; ++i)
|
||||
{
|
||||
ExternalPrefix &prefix = mDiscoveredPrefixes[i];
|
||||
|
||||
if ((aPrefix != nullptr && prefix.mPrefix == *aPrefix && prefix.mIsOnLinkPrefix == aIsOnLinkPrefix) ||
|
||||
(prefix.mExpireTime <= now))
|
||||
{
|
||||
RemoveExternalRoute(prefix.mPrefix);
|
||||
++removedNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
earliestExpireTime = OT_MIN(earliestExpireTime, prefix.mExpireTime);
|
||||
*keptPrefix = prefix;
|
||||
++keptPrefix;
|
||||
if (prefix.mIsOnLinkPrefix)
|
||||
{
|
||||
++keptOnLinkPrefixNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mDiscoveredPrefixNum -= removedNum;
|
||||
|
||||
if (keptOnLinkPrefixNum == 0)
|
||||
{
|
||||
mDiscoveredPrefixInvalidTimer.Stop();
|
||||
|
||||
// There are no valid on-link prefixes on infra link now, start Router Solicitation
|
||||
// To find out more on-link prefixes or timeout to advertise my local on-link prefix.
|
||||
StartRouterSolicitation();
|
||||
}
|
||||
else
|
||||
{
|
||||
mDiscoveredPrefixInvalidTimer.FireAt(earliestExpireTime);
|
||||
}
|
||||
|
||||
return (removedNum != 0); // If anything was removed we need to reevaluate.
|
||||
}
|
||||
|
||||
void RoutingManager::InvalidateAllDiscoveredPrefixes(void)
|
||||
{
|
||||
TimeMilli past = TimerMilli::GetNow();
|
||||
|
||||
for (uint8_t i = 0; i < mDiscoveredPrefixNum; ++i)
|
||||
{
|
||||
mDiscoveredPrefixes[i].mExpireTime = past;
|
||||
}
|
||||
|
||||
InvalidateDiscoveredPrefixes();
|
||||
|
||||
OT_ASSERT(mDiscoveredPrefixNum == 0);
|
||||
}
|
||||
|
||||
// Adds a discovered prefix on infra link. If the same prefix already exists,
|
||||
// only the lifetime will be updated. Returns a boolean which indicates whether
|
||||
// a new prefix is added.
|
||||
bool RoutingManager::AddDiscoveredPrefix(const Ip6::Prefix &aPrefix,
|
||||
bool aIsOnLinkPrefix,
|
||||
uint32_t aLifetime,
|
||||
otRoutePreference aRoutePreference)
|
||||
{
|
||||
OT_ASSERT(aIsOnLinkPrefix ? IsValidOmrPrefix(aPrefix) : IsValidOnLinkPrefix(aPrefix));
|
||||
OT_ASSERT(aLifetime > 0);
|
||||
|
||||
bool added = false;
|
||||
|
||||
for (uint8_t i = 0; i < mDiscoveredPrefixNum; ++i)
|
||||
{
|
||||
ExternalPrefix &prefix = mDiscoveredPrefixes[i];
|
||||
|
||||
if (aPrefix == prefix.mPrefix && aIsOnLinkPrefix == prefix.mIsOnLinkPrefix)
|
||||
{
|
||||
prefix.mExpireTime = TimerMilli::GetNow() + GetPrefixExpireDelay(aLifetime);
|
||||
mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(prefix.mExpireTime);
|
||||
|
||||
otLogInfoBr("discovered prefix %s refreshed lifetime: %u seconds", aPrefix.ToString().AsCString(),
|
||||
aLifetime);
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
if (mDiscoveredPrefixNum < kMaxDiscoveredPrefixNum)
|
||||
{
|
||||
ExternalPrefix &newPrefix = mDiscoveredPrefixes[mDiscoveredPrefixNum];
|
||||
|
||||
SuccessOrExit(AddExternalRoute(aPrefix, aRoutePreference));
|
||||
|
||||
if (aIsOnLinkPrefix)
|
||||
{
|
||||
// Stop Router Solicitation if we discovered a valid on-link prefix.
|
||||
// Otherwise, we wait till the Router Solicitation process times out.
|
||||
// So the maximum delay before the Border Router starts advertising
|
||||
// its own on-link prefix is 9 (4 + 4 + 1) seconds.
|
||||
mRouterSolicitTimer.Stop();
|
||||
}
|
||||
|
||||
newPrefix.mPrefix = aPrefix;
|
||||
newPrefix.mIsOnLinkPrefix = aIsOnLinkPrefix;
|
||||
newPrefix.mExpireTime = TimerMilli::GetNow() + GetPrefixExpireDelay(aLifetime);
|
||||
mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(newPrefix.mExpireTime);
|
||||
|
||||
++mDiscoveredPrefixNum;
|
||||
added = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
otLogWarnBr("discovered too many prefixes, ignore new prefix %s", aPrefix.ToString().AsCString());
|
||||
}
|
||||
|
||||
exit:
|
||||
return added;
|
||||
}
|
||||
|
||||
} // namespace BorderRouter
|
||||
|
||||
} // namespace ot
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
|
||||
#include <openthread/error.h>
|
||||
#include <openthread/netdata.h>
|
||||
#include <openthread/platform/infra_if.h>
|
||||
|
||||
#include "border_router/router_advertisement.hpp"
|
||||
@@ -56,9 +57,9 @@ namespace BorderRouter {
|
||||
* This class implements bi-directional routing between Thread and
|
||||
* Infrastructure networks.
|
||||
*
|
||||
* The routing manager works on both Thread interface and infrastructure
|
||||
* interface. All ICMPv6 messages are sent/recv on the infrastructure
|
||||
* interface.
|
||||
* The Border Routing manager works on both Thread interface and
|
||||
* infrastructure interface. All ICMPv6 messages are sent/recv
|
||||
* on the infrastructure interface.
|
||||
*
|
||||
*/
|
||||
class RoutingManager : public InstanceLocator
|
||||
@@ -85,6 +86,19 @@ public:
|
||||
*/
|
||||
otError Init(uint32_t aInfraIfIndex);
|
||||
|
||||
/**
|
||||
* This method enables/disables the Border Routing Manager.
|
||||
*
|
||||
* @note The Border Routing Manager is enabled by default.
|
||||
*
|
||||
* @param[in] aEnabled A boolean to enable/disable the Border Routing Manager.
|
||||
*
|
||||
* @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet.
|
||||
* @retval OT_ERROR_NONE Successfully enabled/disabled the Border Routing Manager.
|
||||
*
|
||||
*/
|
||||
otError SetEnabled(bool aEnabled);
|
||||
|
||||
/**
|
||||
* This method receives an ICMPv6 message on the infrastructure interface.
|
||||
*
|
||||
@@ -111,8 +125,9 @@ private:
|
||||
{
|
||||
kMaxOmrPrefixNum =
|
||||
OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES, // The maximum number of the OMR prefixes to advertise.
|
||||
kOmrPrefixLength = OT_IP6_PREFIX_BITSIZE, // The length of an OMR prefix. In bits.
|
||||
kOnLinkPrefixLength = OT_IP6_PREFIX_BITSIZE, // The length of an On-link prefix. In bits.
|
||||
kMaxDiscoveredPrefixNum = 8u, // The maximum number of prefixes to discover on the infra link.
|
||||
kOmrPrefixLength = OT_IP6_PREFIX_BITSIZE, // The length of an OMR prefix. In bits.
|
||||
kOnLinkPrefixLength = OT_IP6_PREFIX_BITSIZE, // The length of an On-link prefix. In bits.
|
||||
};
|
||||
|
||||
enum : uint32_t
|
||||
@@ -137,105 +152,36 @@ private:
|
||||
kMaxRtrSolicitations = 3, // The Maximum number of Router Solicitations before sending Router Advertisements.
|
||||
};
|
||||
|
||||
// This struct represents an external prefix which is
|
||||
// discovered on the infrastructure interface.
|
||||
struct ExternalPrefix : public Clearable<ExternalPrefix>
|
||||
{
|
||||
Ip6::Prefix mPrefix;
|
||||
TimeMilli mExpireTime;
|
||||
bool mIsOnLinkPrefix;
|
||||
};
|
||||
|
||||
void Start(void);
|
||||
void Stop(void);
|
||||
void HandleNotifierEvents(Events aEvents);
|
||||
bool IsInitialized(void) const { return mInfraIfIndex != 0; }
|
||||
bool IsEnabled(void) const { return mEnabled; }
|
||||
otError LoadOrGenerateRandomOmrPrefix(void);
|
||||
otError LoadOrGenerateRandomOnLinkPrefix(void);
|
||||
|
||||
/**
|
||||
* This method tells whether the first prefix is numerically smaller than the second one.
|
||||
*
|
||||
* @note The caller must guarantee that the two prefix has the same length.
|
||||
*
|
||||
*/
|
||||
static bool IsPrefixSmallerThan(const Ip6::Prefix &aFirstPrefix, const Ip6::Prefix &aSecondPrefix);
|
||||
const Ip6::Prefix *EvaluateOnLinkPrefix(void);
|
||||
|
||||
static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
|
||||
static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
|
||||
|
||||
/**
|
||||
* This method evaluate the routing policy depends on prefix and route
|
||||
* information on Thread Network and infra link. As a result, this
|
||||
* method May send RA messages on infra link and publish/unpublish
|
||||
* OMR prefix in the Thread network.
|
||||
*
|
||||
* @sa EvaluateOmrPrefix
|
||||
* @sa EvaluateOnLinkPrefix
|
||||
* @sa PublishLocalOmrPrefix
|
||||
* @sa UnpublishLocalOmrPrefix
|
||||
*
|
||||
*/
|
||||
void EvaluateRoutingPolicy(void);
|
||||
|
||||
/**
|
||||
* This method evaluates the OMR prefix for the Thread Network.
|
||||
*
|
||||
* @param[out] aNewOmrPrefixes An array of the new OMR prefixes should be advertised.
|
||||
* MUST not be nullptr.
|
||||
* @param[in] aMaxOmrPrefixNum The maximum number of OMR prefixes that @p aNewOmrPrefixes can hold.
|
||||
*
|
||||
* @returns The number of the new OMR prefixes that should be advertised after this evaluation.
|
||||
*
|
||||
*/
|
||||
void EvaluateRoutingPolicy(void);
|
||||
uint8_t EvaluateOmrPrefix(Ip6::Prefix *aNewOmrPrefixes, uint8_t aMaxOmrPrefixNum);
|
||||
|
||||
/**
|
||||
* This method evaluates the on-link prefix for the infra link.
|
||||
*
|
||||
* @returns A pointer to the new on-link prefix should be advertised.
|
||||
* nullptr if we should no longer advertise an on-link prefix.
|
||||
*
|
||||
*/
|
||||
const Ip6::Prefix *EvaluateOnLinkPrefix(void) const;
|
||||
|
||||
/**
|
||||
* This method publishes the local OMR prefix in Thread network.
|
||||
*
|
||||
*/
|
||||
otError PublishLocalOmrPrefix(void);
|
||||
|
||||
/**
|
||||
* This method unpublishes the local OMR prefix.
|
||||
*
|
||||
*/
|
||||
void UnpublishLocalOmrPrefix(void);
|
||||
|
||||
/**
|
||||
* This method starts sending Router Solicitations in random delay
|
||||
* between 0 and kMaxRtrSolicitationDelay.
|
||||
*
|
||||
*/
|
||||
void StartRouterSolicitation(void);
|
||||
|
||||
/**
|
||||
* This method sends Router Solicitation messages to discover on-link
|
||||
* prefix on infra links.
|
||||
*
|
||||
* @sa HandleRouterAdvertisement
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully sent the message.
|
||||
* @retval OT_ERROR_FAILED Failed to send the message.
|
||||
*
|
||||
*/
|
||||
void UnpublishLocalOmrPrefix(void);
|
||||
otError AddExternalRoute(const Ip6::Prefix &aPrefix, otRoutePreference aRoutePreference);
|
||||
void RemoveExternalRoute(const Ip6::Prefix &aPrefix);
|
||||
void StartRouterSolicitation(void);
|
||||
otError SendRouterSolicitation(void);
|
||||
|
||||
/**
|
||||
* This method sends Router Advertisement messages to advertise
|
||||
* on-link prefix and route for OMR prefix.
|
||||
*
|
||||
* @param[in] aNewOmrPrefixes A pointer to an array of the new OMR prefixes to be advertised.
|
||||
* @p aNewOmrPrefixNum must be zero if this argument is nullptr.
|
||||
* @param[in] aNewOmrPrefixNum The number of the new OMR prefixes to be advertised.
|
||||
* Zero means we should stop advertising OMR prefixes.
|
||||
* @param[in] aOnLinkPrefix A pointer to the new on-link prefix to be advertised.
|
||||
* nullptr means we should stop advertising on-link prefix.
|
||||
*
|
||||
*/
|
||||
void SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
|
||||
uint8_t aNewOmrPrefixNum,
|
||||
const Ip6::Prefix *aNewOnLinkPrefix);
|
||||
void SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
|
||||
uint8_t aNewOmrPrefixNum,
|
||||
const Ip6::Prefix *aNewOnLinkPrefix);
|
||||
|
||||
static void HandleRouterAdvertisementTimer(Timer &aTimer);
|
||||
void HandleRouterAdvertisementTimer(void);
|
||||
@@ -243,61 +189,63 @@ private:
|
||||
static void HandleRouterSolicitTimer(Timer &aTimer);
|
||||
void HandleRouterSolicitTimer(void);
|
||||
|
||||
static void HandleDiscoveredOnLinkPrefixInvalidTimer(Timer &aTimer);
|
||||
void HandleDiscoveredOnLinkPrefixInvalidTimer(void);
|
||||
static void HandleDiscoveredPrefixInvalidTimer(Timer &aTimer);
|
||||
void HandleDiscoveredPrefixInvalidTimer(void);
|
||||
|
||||
void HandleRouterSolicit(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength);
|
||||
void HandleRouterAdvertisement(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength);
|
||||
bool UpdateDiscoveredPrefixes(const RouterAdv::PrefixInfoOption &aPio);
|
||||
bool UpdateDiscoveredPrefixes(const RouterAdv::RouteInfoOption &aRio);
|
||||
bool InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix = nullptr, bool aIsOnLinkPrefix = true);
|
||||
void InvalidateAllDiscoveredPrefixes(void);
|
||||
bool AddDiscoveredPrefix(const Ip6::Prefix &aPrefix,
|
||||
bool aIsOnLinkPrefix,
|
||||
uint32_t aLifetime,
|
||||
otRoutePreference aRoutePreference = OT_ROUTE_PREFERENCE_MED);
|
||||
|
||||
static bool ContainsPrefix(const Ip6::Prefix &aPrefix, const Ip6::Prefix *aPrefixList, uint8_t aPrefixNum);
|
||||
|
||||
// Decides the first prefix is numerically smaller than the second one.
|
||||
static bool IsPrefixSmallerThan(const Ip6::Prefix &aFirstPrefix, const Ip6::Prefix &aSecondPrefix);
|
||||
static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
|
||||
static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
|
||||
static bool ContainsPrefix(const Ip6::Prefix &aPrefix, const Ip6::Prefix *aPrefixList, uint8_t aPrefixNum);
|
||||
static uint32_t GetPrefixExpireDelay(uint32_t aValidLifetime);
|
||||
|
||||
bool mIsRunning;
|
||||
uint32_t mInfraIfIndex;
|
||||
bool mEnabled;
|
||||
|
||||
/**
|
||||
* The OMR prefix loaded from local persistent storage or randomly generated
|
||||
* if non is found in persistent storage.
|
||||
*
|
||||
*/
|
||||
// The OMR prefix loaded from local persistent storage or randomly generated
|
||||
// if non is found in persistent storage.
|
||||
Ip6::Prefix mLocalOmrPrefix;
|
||||
|
||||
/**
|
||||
* The advertised OMR prefixes.
|
||||
*
|
||||
*/
|
||||
// The advertised OMR prefixes. For a stable Thread network without
|
||||
// manually configured OMR prefixes, there should be a single OMR prefix
|
||||
// that is being advertised because each BRs will converge to the smallest
|
||||
// OMR prefix sorted by method IsPrefixSmallerThan. If manually configured
|
||||
// OMR prefixes exist, they will also be advertised on infra link.
|
||||
Ip6::Prefix mAdvertisedOmrPrefixes[kMaxOmrPrefixNum];
|
||||
uint8_t mAdvertisedOmrPrefixNum;
|
||||
|
||||
/**
|
||||
* The on-link prefix loaded from local persistent storage or randomly generated
|
||||
* if non is found in persistent storage.
|
||||
*
|
||||
*/
|
||||
// The on-link prefix loaded from local persistent storage or randomly generated
|
||||
// if non is found in persistent storage.
|
||||
Ip6::Prefix mLocalOnLinkPrefix;
|
||||
|
||||
/**
|
||||
* The advertised on-link prefix.
|
||||
*
|
||||
* Could only be nullptr or a pointer to mLocalOnLinkPrefix.
|
||||
*
|
||||
*/
|
||||
// Could only be nullptr or a pointer to mLocalOnLinkPrefix.
|
||||
const Ip6::Prefix *mAdvertisedOnLinkPrefix;
|
||||
|
||||
/**
|
||||
* The on-link prefix we discovered on the infra link.
|
||||
*
|
||||
*/
|
||||
Ip6::Prefix mDiscoveredOnLinkPrefix;
|
||||
// The array of prefixes discovered on the infra link. Those prefixes consist of
|
||||
// on-link prefix(es) and OMR prefixes advertised by BRs in another Thread Network
|
||||
// which is connected to the same infra link.
|
||||
ExternalPrefix mDiscoveredPrefixes[kMaxDiscoveredPrefixNum];
|
||||
uint8_t mDiscoveredPrefixNum;
|
||||
|
||||
TimerMilli mDiscoveredPrefixInvalidTimer;
|
||||
|
||||
TimerMilli mRouterAdvertisementTimer;
|
||||
uint32_t mRouterAdvertisementCount;
|
||||
|
||||
TimerMilli mRouterSolicitTimer;
|
||||
uint8_t mRouterSolicitCount;
|
||||
|
||||
TimerMilli mDiscoveredOnLinkPrefixInvalidTimer;
|
||||
};
|
||||
|
||||
} // namespace BorderRouter
|
||||
|
||||
@@ -157,6 +157,14 @@ public:
|
||||
*/
|
||||
Ip6::Prefix &GetPrefix(void) { return static_cast<Ip6::Prefix &>(mPrefix); }
|
||||
|
||||
/**
|
||||
* This method sets the prefix.
|
||||
*
|
||||
* @param[in] aPrefix The prefix to set to.
|
||||
*
|
||||
*/
|
||||
void SetPrefix(const Ip6::Prefix &aPrefix) { mPrefix = aPrefix; }
|
||||
|
||||
private:
|
||||
void SetFrom(Instance & aInstance,
|
||||
const PrefixTlv & aPrefixTlv,
|
||||
|
||||
@@ -51,12 +51,19 @@
|
||||
#include <openthread/platform/infra_if.h>
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "lib/platform/exit_code.h"
|
||||
|
||||
static char sInfraIfName[IFNAMSIZ];
|
||||
static uint32_t sInfraIfIndex = 0;
|
||||
static int sInfraIfIcmp6Socket = -1;
|
||||
static otIp6Address sInfraIfLinkLocalAddr;
|
||||
|
||||
const otIp6Address *otPlatInfraIfGetLinkLocalAddress(uint32_t aInfraIfIndex)
|
||||
{
|
||||
VerifyOrDie(aInfraIfIndex == sInfraIfIndex, OT_EXIT_FAILURE);
|
||||
return &sInfraIfLinkLocalAddr;
|
||||
}
|
||||
|
||||
otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
|
||||
const otIp6Address *aDestAddress,
|
||||
const uint8_t * aBuffer,
|
||||
@@ -131,8 +138,9 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
static void InitLinkLocalAddress(void)
|
||||
static otError InitLinkLocalAddress(void)
|
||||
{
|
||||
otError error;
|
||||
struct ifaddrs *ifAddrs = nullptr;
|
||||
|
||||
if (getifaddrs(&ifAddrs) < 0)
|
||||
@@ -154,11 +162,16 @@ static void InitLinkLocalAddress(void)
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&ip6Addr->sin6_addr))
|
||||
{
|
||||
memcpy(&sInfraIfLinkLocalAddr, &ip6Addr->sin6_addr, sizeof(sInfraIfLinkLocalAddr));
|
||||
break;
|
||||
ExitNow(error = OT_ERROR_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
otLogCritPlat("cannot find IPv6 link-local address for interface %s", sInfraIfName);
|
||||
error = OT_ERROR_NOT_FOUND;
|
||||
|
||||
exit:
|
||||
freeifaddrs(ifAddrs);
|
||||
return error;
|
||||
}
|
||||
|
||||
void platformInfraIfInit(otInstance *aInstance, const char *aIfName)
|
||||
@@ -251,7 +264,7 @@ void platformInfraIfInit(otInstance *aInstance, const char *aIfName)
|
||||
}
|
||||
|
||||
sInfraIfIcmp6Socket = sock;
|
||||
InitLinkLocalAddress();
|
||||
SuccessOrDie(InitLinkLocalAddress());
|
||||
}
|
||||
|
||||
void platformInfraIfDeinit(void)
|
||||
@@ -341,8 +354,6 @@ void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet)
|
||||
// the hoplimit must be 255 and the source address must be a link-local address.
|
||||
VerifyOrExit(hopLimit == 255 && IN6_IS_ADDR_LINKLOCAL(&srcAddr.sin6_addr));
|
||||
|
||||
// Drop multicast messages sent by ourselves.
|
||||
VerifyOrExit(!otIp6IsAddressEqual(&sInfraIfLinkLocalAddr, reinterpret_cast<otIp6Address *>(&srcAddr.sin6_addr)));
|
||||
otPlatInfraIfRecvIcmp6Nd(aInstance, ifIndex, reinterpret_cast<otIp6Address *>(&srcAddr.sin6_addr), buffer,
|
||||
bufferLength);
|
||||
|
||||
|
||||
@@ -41,14 +41,15 @@ import thread_cert
|
||||
# ----------------(eth)------------------
|
||||
# | | |
|
||||
# BR1 (Leader) ----- BR2 HOST
|
||||
# |
|
||||
# ED1
|
||||
# | |
|
||||
# ROUTER1 ROUTER2
|
||||
#
|
||||
|
||||
BR1 = 1
|
||||
ROUTER1 = 2
|
||||
BR2 = 3
|
||||
HOST = 4
|
||||
ROUTER2 = 4
|
||||
HOST = 5
|
||||
|
||||
CHANNEL1 = 18
|
||||
|
||||
@@ -74,12 +75,19 @@ class MultiBorderRouters(thread_cert.TestCase):
|
||||
},
|
||||
BR2: {
|
||||
'name': 'BR_2',
|
||||
'allowlist': [BR1],
|
||||
'allowlist': [BR1, ROUTER2],
|
||||
'is_otbr': True,
|
||||
'version': '1.2',
|
||||
'channel': CHANNEL1,
|
||||
'router_selection_jitter': 2,
|
||||
},
|
||||
ROUTER2: {
|
||||
'name': 'Router_2',
|
||||
'allowlist': [BR2],
|
||||
'version': '1.2',
|
||||
'channel': CHANNEL1,
|
||||
'router_selection_jitter': 2,
|
||||
},
|
||||
HOST: {
|
||||
'name': 'Host',
|
||||
'is_host': True
|
||||
@@ -87,7 +95,7 @@ class MultiBorderRouters(thread_cert.TestCase):
|
||||
}
|
||||
|
||||
def test(self):
|
||||
self.nodes[HOST].start(start_radvd=True, prefix=config.ONLINK_PREFIX, slaac=True)
|
||||
self.nodes[HOST].start(start_radvd=False)
|
||||
self.simulator.go(5)
|
||||
|
||||
self.nodes[BR1].start()
|
||||
@@ -102,12 +110,70 @@ class MultiBorderRouters(thread_cert.TestCase):
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', self.nodes[BR2].get_state())
|
||||
|
||||
self.nodes[ROUTER2].start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', self.nodes[ROUTER2].get_state())
|
||||
|
||||
#
|
||||
# Case 1. bi-directional connectivity when there are two BRs.
|
||||
#
|
||||
|
||||
self.simulator.go(10)
|
||||
self.collect_ipaddrs()
|
||||
|
||||
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
|
||||
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
|
||||
logging.info("BR2 addrs: %r", self.nodes[BR2].get_addrs())
|
||||
logging.info("ROUTER2 addrs: %r", self.nodes[ROUTER2].get_addrs())
|
||||
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[BR2].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_prefixes()) == 1)
|
||||
|
||||
br1_omr_prefix = self.nodes[BR1].get_prefixes()[0]
|
||||
|
||||
# Each BR should independently register an external route for the on-link prefix.
|
||||
self.assertTrue(len(self.nodes[BR1].get_routes()) == 2)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 2)
|
||||
self.assertTrue(len(self.nodes[BR2].get_routes()) == 2)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_routes()) == 2)
|
||||
|
||||
external_route = self.nodes[BR1].get_routes()[0]
|
||||
br1_on_link_prefix = external_route.split(' ')[0]
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[BR2].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[HOST].get_matched_ula_addresses(br1_on_link_prefix)) == 1)
|
||||
|
||||
# Router1 and Router2 can ping each other inside the Thread network.
|
||||
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
|
||||
self.assertTrue(self.nodes[ROUTER2].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
|
||||
|
||||
# Both Router1 and Router2 can ping to/from the Host on infra link.
|
||||
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_matched_ula_addresses(br1_on_link_prefix)[0]))
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
backbone=True))
|
||||
self.assertTrue(self.nodes[ROUTER2].ping(self.nodes[HOST].get_matched_ula_addresses(br1_on_link_prefix)[0]))
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
backbone=True))
|
||||
|
||||
#
|
||||
# Case 2. Another BR continues providing Border Routing when current one is disabled.
|
||||
#
|
||||
|
||||
self.nodes[BR1].disable_br()
|
||||
|
||||
self.simulator.go(15)
|
||||
self.collect_ipaddrs()
|
||||
|
||||
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
|
||||
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
|
||||
logging.info("BR2 addrs: %r", self.nodes[BR2].get_addrs())
|
||||
logging.info("ROUTER2 addrs: %r", self.nodes[ROUTER2].get_addrs())
|
||||
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
|
||||
|
||||
self.assertGreaterEqual(len(self.nodes[HOST].get_addrs()), 2)
|
||||
@@ -115,23 +181,36 @@ class MultiBorderRouters(thread_cert.TestCase):
|
||||
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[BR2].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_prefixes()) == 1)
|
||||
|
||||
br2_omr_prefix = self.nodes[BR1].get_prefixes()[0]
|
||||
self.assertNotEqual(br1_omr_prefix, br2_omr_prefix)
|
||||
|
||||
# Only BR2 will register external route for the on-link prefix.
|
||||
self.assertTrue(len(self.nodes[BR1].get_routes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1)
|
||||
self.assertTrue(len(self.nodes[BR2].get_routes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_routes()) == 1)
|
||||
|
||||
br2_external_route = self.nodes[BR2].get_routes()[0]
|
||||
br2_on_link_prefix = br2_external_route.split(' ')[0]
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[BR2].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
|
||||
# Router1 and BR2 can ping each other inside the Thread network.
|
||||
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[BR2].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
|
||||
self.assertTrue(self.nodes[BR2].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
|
||||
self.assertTrue(len(self.nodes[HOST].get_matched_ula_addresses(br2_on_link_prefix)) == 1)
|
||||
|
||||
# Both Router1 and BR2 can ping to/from the Host on infra link.
|
||||
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
backbone=True))
|
||||
self.assertTrue(self.nodes[BR2].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[BR2].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
backbone=True))
|
||||
# Router1 and Router2 can ping each other inside the Thread network.
|
||||
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
|
||||
self.assertTrue(self.nodes[ROUTER2].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
|
||||
|
||||
# Both Router1 and Router2 can ping to/from the Host on infra link.
|
||||
for router in [ROUTER1, ROUTER2]:
|
||||
self.assertTrue(self.nodes[router].ping(self.nodes[HOST].get_matched_ula_addresses(br2_on_link_prefix)[0]))
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[router].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
backbone=True))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -40,7 +40,7 @@ import thread_cert
|
||||
# | |
|
||||
# BR1 BR2
|
||||
# | |
|
||||
# ED1 ED2
|
||||
# ROUTER1 ROUTER2
|
||||
#
|
||||
# Thread Net1 Thread Net2
|
||||
#
|
||||
@@ -119,6 +119,25 @@ class MultiThreadNetworks(thread_cert.TestCase):
|
||||
self.assertTrue(len(self.nodes[BR2].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_prefixes()) == 1)
|
||||
|
||||
br1_omr_prefix = self.nodes[BR1].get_prefixes()[0]
|
||||
br2_omr_prefix = self.nodes[BR2].get_prefixes()[0]
|
||||
|
||||
self.assertNotEqual(br1_omr_prefix, br2_omr_prefix)
|
||||
|
||||
# Each BR should independently register an external route for the on-link prefix
|
||||
# and OMR prefix in another Thread Network.
|
||||
self.assertTrue(len(self.nodes[BR1].get_routes()) == 2)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 2)
|
||||
self.assertTrue(len(self.nodes[BR2].get_routes()) == 2)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_routes()) == 2)
|
||||
|
||||
br1_external_routes = self.nodes[BR1].get_routes()
|
||||
br2_external_routes = self.nodes[BR2].get_routes()
|
||||
|
||||
br1_external_routes.sort()
|
||||
br2_external_routes.sort()
|
||||
self.assertNotEqual(br1_external_routes, br2_external_routes)
|
||||
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
|
||||
|
||||
@@ -93,6 +93,10 @@ class SingleBorderRouter(thread_cert.TestCase):
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', self.nodes[ROUTER1].get_state())
|
||||
|
||||
#
|
||||
# Case 1. There is no OMR prefix or on-link prefix.
|
||||
#
|
||||
|
||||
self.simulator.go(10)
|
||||
self.collect_ipaddrs()
|
||||
|
||||
@@ -100,22 +104,34 @@ class SingleBorderRouter(thread_cert.TestCase):
|
||||
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
|
||||
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
|
||||
|
||||
self.assertGreaterEqual(len(self.nodes[HOST].get_addrs()), 2)
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[BR1].get_routes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1)
|
||||
|
||||
omr_prefix = self.nodes[BR1].get_prefixes()[0]
|
||||
external_route = self.nodes[BR1].get_routes()[0]
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
|
||||
|
||||
br1_omr_address = self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
|
||||
router1_omr_address = self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
|
||||
host_ula_address = self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]
|
||||
|
||||
# Router1 can ping to/from the Host on infra link.
|
||||
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
backbone=True))
|
||||
|
||||
# Add two on-mesh prefix on BR1, so that
|
||||
# it will deregister its random-generated OMR prefix.
|
||||
#
|
||||
# Case 2. User adds smaller on-mesh prefix.
|
||||
# 1. Should deregister our local OMR prefix.
|
||||
# 2. Should re-register our local OMR prefix when user prefix
|
||||
# is removed.
|
||||
#
|
||||
|
||||
self.nodes[BR1].add_prefix(ON_MESH_PREFIX1)
|
||||
self.nodes[BR1].add_prefix(ON_MESH_PREFIX2)
|
||||
self.nodes[BR1].register_netdata()
|
||||
@@ -131,6 +147,8 @@ class SingleBorderRouter(thread_cert.TestCase):
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 2)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 2)
|
||||
self.assertTrue(len(self.nodes[BR1].get_routes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1)
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 2)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 2)
|
||||
@@ -143,6 +161,100 @@ class SingleBorderRouter(thread_cert.TestCase):
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[1],
|
||||
backbone=True))
|
||||
|
||||
# Remove user prefixes, should re-register local OMR prefix.
|
||||
self.nodes[BR1].remove_prefix(ON_MESH_PREFIX1)
|
||||
self.nodes[BR1].remove_prefix(ON_MESH_PREFIX2)
|
||||
self.nodes[BR1].register_netdata()
|
||||
|
||||
self.simulator.go(10)
|
||||
self.collect_ipaddrs()
|
||||
|
||||
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
|
||||
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
|
||||
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[BR1].get_routes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1)
|
||||
|
||||
# The same local OMR and on-link prefix should be re-register.
|
||||
self.assertEqual(omr_prefix, self.nodes[BR1].get_prefixes()[0])
|
||||
self.assertEqual(omr_prefix, self.nodes[ROUTER1].get_prefixes()[0])
|
||||
self.assertEqual(external_route, self.nodes[BR1].get_routes()[0])
|
||||
self.assertEqual(external_route, self.nodes[ROUTER1].get_routes()[0])
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
|
||||
|
||||
self.assertEqual(br1_omr_address, self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0])
|
||||
self.assertEqual(router1_omr_address, self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0])
|
||||
self.assertEqual(host_ula_address, self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])
|
||||
|
||||
# Router1 can ping to/from the Host on infra link.
|
||||
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
backbone=True))
|
||||
|
||||
#
|
||||
# Case 3. OMR and on-link prefixes should be removed when Border Routing is
|
||||
# explicitly disabled and added when Border Routing is enabled again.
|
||||
#
|
||||
|
||||
self.nodes[BR1].disable_br()
|
||||
|
||||
self.simulator.go(10)
|
||||
self.collect_ipaddrs()
|
||||
|
||||
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
|
||||
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
|
||||
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 0)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 0)
|
||||
self.assertTrue(len(self.nodes[BR1].get_routes()) == 0)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 0)
|
||||
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 0)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 0)
|
||||
|
||||
# Per RFC 4862, the host will not immediately remove the ULA address, but deprecate it.
|
||||
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
|
||||
|
||||
self.nodes[BR1].enable_br()
|
||||
|
||||
# It takes around 10 seconds to start sending RA messages.
|
||||
self.simulator.go(15)
|
||||
self.collect_ipaddrs()
|
||||
|
||||
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
|
||||
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
|
||||
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
|
||||
self.assertTrue(len(self.nodes[BR1].get_routes()) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1)
|
||||
|
||||
# The same local OMR and on-link prefix should be re-registered.
|
||||
self.assertEqual(omr_prefix, self.nodes[BR1].get_prefixes()[0])
|
||||
self.assertEqual(omr_prefix, self.nodes[ROUTER1].get_prefixes()[0])
|
||||
self.assertEqual(external_route, self.nodes[BR1].get_routes()[0])
|
||||
self.assertEqual(external_route, self.nodes[ROUTER1].get_routes()[0])
|
||||
|
||||
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
|
||||
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
|
||||
|
||||
self.assertEqual(br1_omr_address, self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0])
|
||||
self.assertEqual(router1_omr_address, self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0])
|
||||
self.assertEqual(host_ula_address, self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])
|
||||
|
||||
# Router1 can ping to/from the Host on infra link.
|
||||
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
|
||||
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
|
||||
backbone=True))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1281,21 +1281,39 @@ class NodeImpl:
|
||||
self.send_command(cmd)
|
||||
self._expect_done()
|
||||
|
||||
def enable_br(self):
|
||||
self.send_command('br enable')
|
||||
self._expect('Done')
|
||||
|
||||
def disable_br(self):
|
||||
self.send_command('br disable')
|
||||
self._expect('Done')
|
||||
|
||||
def get_prefixes(self):
|
||||
netdata = self.netdata_show()
|
||||
prefixes = []
|
||||
return self.get_netdata()['Prefixes']
|
||||
|
||||
for i in range(1, len(netdata)):
|
||||
if netdata[i].startswith("Routes:"):
|
||||
break
|
||||
prefixes.append(netdata[i])
|
||||
|
||||
return prefixes
|
||||
def get_routes(self):
|
||||
return self.get_netdata()['Routes']
|
||||
|
||||
def netdata_show(self):
|
||||
self.send_command('netdata show')
|
||||
return self._expect_command_output('netdata show')
|
||||
|
||||
def get_netdata(self):
|
||||
raw_netdata = self.netdata_show()
|
||||
netdata = {'Prefixes': [], 'Routes': [], 'Services': []}
|
||||
key_list = ['Prefixes', 'Routes', 'Services']
|
||||
key = None
|
||||
|
||||
for i in range(0, len(raw_netdata)):
|
||||
keys = list(filter(raw_netdata[i].startswith, key_list))
|
||||
if keys != []:
|
||||
key = keys[0]
|
||||
elif key is not None:
|
||||
netdata[key].append(raw_netdata[i])
|
||||
|
||||
return netdata
|
||||
|
||||
def add_route(self, prefix, stable=False, prf='med'):
|
||||
cmd = 'route add %s ' % prefix
|
||||
if stable:
|
||||
@@ -2157,6 +2175,17 @@ class HostNode(LinuxHost, OtbrDocker):
|
||||
def __repr__(self):
|
||||
return f'Host<{self.nodeid}>'
|
||||
|
||||
def get_matched_ula_addresses(self, prefix):
|
||||
"""Get the IPv6 addresses that matches given prefix.
|
||||
"""
|
||||
|
||||
addrs = []
|
||||
for addr in self.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA):
|
||||
if addr.startswith(prefix.split('::')[0]):
|
||||
addrs.append(addr)
|
||||
|
||||
return addrs
|
||||
|
||||
def get_ip6_address(self, address_type: config.ADDRESS_TYPE):
|
||||
"""Get specific type of IPv6 address configured on thread device.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user