mirror of
https://github.com/espressif/openthread.git
synced 2026-09-09 02:30:11 +00:00
[routing-manager] check reachability & send ICMPv6 unreach err (#10548)
This commit introduces a mechanism to check for reachability of messages forwarded by the BR and send an ICMPv6 Destination Unreachable error to the sender if needed. Specifically, if the Border Router (BR) decides to forward an IPv6 message outside the AIL and the message's source address matches a BR-generated ULA OMR prefix (with low preference), and the destination is unreachable using this source address, then an ICMPv6 Destination Unreachable message is sent back to the sender. For example, this situation can occur when a local, non-infrastructure-derived ULA OMR prefix is published alongside a `::/0` route (due to discovered PIO/RIO prefixes by the BR). A Thread mesh device may try to reach addresses beyond the local AIL (e.g., the global internet) using the ULA OMR prefix, which would be unreachable. This feature is controlled by an OT config flag, enabled by default. Alternatively, this functionality may be implemented within the platform layer, in which case the configuration should be disabled. This commit also adds a test case `test-504-br-icmp-unreach-err.py` validating the newly added behavior.
This commit is contained in:
@@ -598,6 +598,18 @@ void otBorderRoutingDhcp6PdSetRequestCallback(otInstance
|
||||
otBorderRoutingRequestDhcp6PdCallback aCallback,
|
||||
void *aContext);
|
||||
|
||||
/**
|
||||
* Sets the local on-link prefix.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE`.
|
||||
*
|
||||
* This is intended for testing only and using it will make the BR non-compliant with the Thread Specification.
|
||||
*
|
||||
* @param[in] aPrefix The on-link prefix to use.
|
||||
*
|
||||
*/
|
||||
void otBorderRoutingSetOnLinkPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -76,8 +76,9 @@ typedef enum otIcmp6Type
|
||||
*/
|
||||
typedef enum otIcmp6Code
|
||||
{
|
||||
OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE = 0, ///< Destination Unreachable No Route
|
||||
OT_ICMP6_CODE_FRAGM_REAS_TIME_EX = 1, ///< Fragment Reassembly Time Exceeded
|
||||
OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE = 0, ///< Destination Unreachable (Type 1) - No Route
|
||||
OT_ICMP6_CODE_DST_UNREACH_PROHIBITED = 1, ///< Destination Unreachable (Type 1) - Administratively Prohibited
|
||||
OT_ICMP6_CODE_FRAGM_REAS_TIME_EX = 1, ///< Time Exceeded (Type 3) - Fragment Reassembly
|
||||
} otIcmp6Code;
|
||||
|
||||
#define OT_ICMP6_HEADER_DATA_SIZE 4 ///< Size of ICMPv6 Header.
|
||||
|
||||
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (433)
|
||||
#define OPENTHREAD_API_VERSION (434)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
+14
-1
@@ -258,7 +258,20 @@ template <> otError Br::Process<Cmd("onlinkprefix")>(Arg aArgs[])
|
||||
otError error = OT_ERROR_NONE;
|
||||
PrefixType outputPrefixTypes;
|
||||
|
||||
SuccessOrExit(error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes));
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE
|
||||
if (aArgs[0] == "test")
|
||||
{
|
||||
otIp6Prefix prefix;
|
||||
|
||||
SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
|
||||
otBorderRoutingSetOnLinkPrefix(GetInstancePtr(), &prefix);
|
||||
ExitNow();
|
||||
}
|
||||
#endif
|
||||
|
||||
error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes);
|
||||
|
||||
SuccessOrExit(error);
|
||||
|
||||
/**
|
||||
* @cli br onlinkprefix local
|
||||
|
||||
@@ -217,6 +217,7 @@ uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge)
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
|
||||
|
||||
void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled)
|
||||
{
|
||||
AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().SetDhcp6PdEnabled(aEnabled);
|
||||
@@ -237,4 +238,13 @@ void otBorderRoutingDhcp6PdSetRequestCallback(otInstance
|
||||
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE
|
||||
|
||||
void otBorderRoutingSetOnLinkPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix)
|
||||
{
|
||||
AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().SetOnLinkPrefix(AsCoreType(aPrefix));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
|
||||
@@ -817,6 +817,62 @@ bool RoutingManager::NetworkDataContainsUlaRoute(void) const
|
||||
return contains;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
|
||||
|
||||
void RoutingManager::CheckReachabilityToSendIcmpError(const Message &aMessage, const Ip6::Header &aIp6Header)
|
||||
{
|
||||
bool matchesUlaOmrLowPrf = false;
|
||||
NetworkData::Iterator iterator = NetworkData::kIteratorInit;
|
||||
NetworkData::OnMeshPrefixConfig prefixConfig;
|
||||
Ip6::MessageInfo messageInfo;
|
||||
|
||||
VerifyOrExit(IsRunning() && IsInitialPolicyEvaluationDone());
|
||||
|
||||
VerifyOrExit(!aIp6Header.GetDestination().IsMulticast());
|
||||
|
||||
// Validate that source matches a ULA OMR prefix with low preference
|
||||
// (indicating it is not infrastructure-derived).
|
||||
|
||||
while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
|
||||
{
|
||||
if (IsValidOmrPrefix(prefixConfig) && prefixConfig.GetPrefix().IsUniqueLocal() &&
|
||||
aIp6Header.GetSource().MatchesPrefix(prefixConfig.GetPrefix()))
|
||||
{
|
||||
if (prefixConfig.GetPreference() >= NetworkData::kRoutePreferenceMedium)
|
||||
{
|
||||
matchesUlaOmrLowPrf = false;
|
||||
break;
|
||||
}
|
||||
|
||||
matchesUlaOmrLowPrf = true;
|
||||
|
||||
// Keep checking other prefixes, as the same prefix might
|
||||
// be added with a higher preference by another BR.
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrExit(matchesUlaOmrLowPrf);
|
||||
|
||||
VerifyOrExit(!mRxRaTracker.IsAddressOnLink(aIp6Header.GetDestination()));
|
||||
VerifyOrExit(!mRxRaTracker.IsAddressReachableThroughExplicitRoute(aIp6Header.GetDestination()));
|
||||
VerifyOrExit(!Get<NetworkData::Leader>().IsNat64(aIp6Header.GetDestination()));
|
||||
|
||||
LogInfo("Send ICMP unreachable for fwd msg with local ULA src and non-local dst");
|
||||
LogInfo(" src: %s", aIp6Header.GetSource().ToString().AsCString());
|
||||
LogInfo(" dst: %s", aIp6Header.GetDestination().ToString().AsCString());
|
||||
|
||||
messageInfo.Clear();
|
||||
messageInfo.SetPeerAddr(aIp6Header.GetSource());
|
||||
|
||||
IgnoreError(Get<Ip6::Icmp>().SendError(Ip6::Icmp::Header::kTypeDstUnreach,
|
||||
Ip6::Icmp::Header::kCodeDstUnreachProhibited, messageInfo, aMessage));
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
|
||||
|
||||
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
|
||||
|
||||
void RoutingManager::LogPrefixInfoOption(const Ip6::Prefix &aPrefix,
|
||||
@@ -1804,6 +1860,48 @@ void RoutingManager::RxRaTracker::SetHeaderFlagsOn(RouterAdvert::Header &aHeader
|
||||
}
|
||||
}
|
||||
|
||||
bool RoutingManager::RxRaTracker::IsAddressOnLink(const Ip6::Address &aAddress) const
|
||||
{
|
||||
bool isOnLink = false;
|
||||
|
||||
for (const Router &router : mRouters)
|
||||
{
|
||||
for (const OnLinkPrefix &onLinkPrefix : router.mOnLinkPrefixes)
|
||||
{
|
||||
isOnLink = aAddress.MatchesPrefix(onLinkPrefix.GetPrefix());
|
||||
VerifyOrExit(!isOnLink);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return isOnLink;
|
||||
}
|
||||
|
||||
bool RoutingManager::RxRaTracker::IsAddressReachableThroughExplicitRoute(const Ip6::Address &aAddress) const
|
||||
{
|
||||
// Checks whether the `aAddress` matches any discovered route
|
||||
// prefix excluding `::/0`.
|
||||
|
||||
bool isReachable = false;
|
||||
|
||||
for (const Router &router : mRouters)
|
||||
{
|
||||
for (const RoutePrefix &routePrefix : router.mRoutePrefixes)
|
||||
{
|
||||
if (routePrefix.GetPrefix().GetLength() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
isReachable = aAddress.MatchesPrefix(routePrefix.GetPrefix());
|
||||
VerifyOrExit(!isReachable);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return isReachable;
|
||||
}
|
||||
|
||||
void RoutingManager::RxRaTracker::InitIterator(PrefixTableIterator &aIterator) const
|
||||
{
|
||||
static_cast<Iterator &>(aIterator).Init(mRouters.GetHead(), Uptime::MsecToSec(Get<Uptime>().GetUptime()));
|
||||
|
||||
@@ -602,6 +602,34 @@ public:
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
|
||||
/**
|
||||
* Determines whether to send an ICMPv6 Destination Unreachable error to the sender based on reachability and
|
||||
* source address.
|
||||
*
|
||||
* Specifically, if the Border Router (BR) decides to forward a unicast IPv6 message outside the AIL and the
|
||||
* message's source address matches a BR-generated ULA OMR prefix (with low preference), and the destination is
|
||||
* unreachable using this source address, then an ICMPv6 Destination Unreachable message is sent back to the sender.
|
||||
*
|
||||
* @param[in] aMessage The message.
|
||||
* @param[in] aIp6Header The IPv6 header of @p aMessage.
|
||||
*
|
||||
*/
|
||||
void CheckReachabilityToSendIcmpError(const Message &aMessage, const Ip6::Header &aIp6Header);
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE
|
||||
/**
|
||||
* Sets the local on-link prefix.
|
||||
*
|
||||
* This is intended for testing only and using it will make a device non-compliant with the Thread Specification.
|
||||
*
|
||||
* @param[in] aPrefix The on-link prefix to use.
|
||||
*
|
||||
*/
|
||||
void SetOnLinkPrefix(const Ip6::Prefix &aPrefix) { mOnLinkPrefixManager.SetLocalPrefix(aPrefix); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Constants
|
||||
@@ -859,6 +887,9 @@ private:
|
||||
|
||||
const RouterAdvert::Header &GetLocalRaHeaderToMirror(void) const { return mLocalRaHeader; }
|
||||
|
||||
bool IsAddressOnLink(const Ip6::Address &aAddress) const;
|
||||
bool IsAddressReachableThroughExplicitRoute(const Ip6::Address &aAddress) const;
|
||||
|
||||
// Iterating over discovered items
|
||||
void InitIterator(PrefixTableIterator &aIterator) const;
|
||||
Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const;
|
||||
@@ -1178,6 +1209,9 @@ private:
|
||||
void HandleNetDataChange(void);
|
||||
void HandleExtPanIdChange(void);
|
||||
void HandleTimer(void);
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE
|
||||
void SetLocalPrefix(const Ip6::Prefix &aPrefix) { mLocalPrefix = aPrefix; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum State : uint8_t // State of `mLocalPrefix`
|
||||
|
||||
@@ -88,6 +88,31 @@
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
|
||||
*
|
||||
* Define to 1 to allow Routing Manager to check for reachability of messages being forwarded by the BR and determine
|
||||
* whether to send an ICMPv6 Destination Unreachable error back to the sender.
|
||||
*
|
||||
* Specifically, if the Border Router (BR) decides to forward a unicast IPv6 message outside the AIL and the message's
|
||||
* source address matches a BR-generated ULA OMR prefix (with low preference), and the destination is unreachable
|
||||
* using this source address, then an ICMPv6 Destination Unreachable message is sent back to the sender.
|
||||
*
|
||||
* For example, this situation can occur when a local, non-infrastructure-derived ULA OMR prefix is published alongside
|
||||
* a `::/0` route (due to discovered PIO/RIO prefixes by the BR). A Thread mesh device may try to reach addresses
|
||||
* beyond the local AIL (e.g., the global internet) using its ULA OMR address as source, which would be unreachable.
|
||||
*
|
||||
* Alternatively, this functionality may be implemented within the platform layer, in which case this configuration
|
||||
* should be disabled. Note that the platform layer is always responsible for implementing generation of "ICMPv6
|
||||
* Destination Unreachable - No Route" messages. This reachability function will only generate "ICMPv6 Destination
|
||||
* Unreachable - Communication Administratively Prohibited" messages for specific cases where there may be a
|
||||
* default route to the destination but the source address type prohibits usable communication with this destination.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS
|
||||
*
|
||||
@@ -177,6 +202,18 @@
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE
|
||||
*
|
||||
* Define to 1 to enable testing related APIs to be provided by the `RoutingManager`.
|
||||
*
|
||||
* This is intended for testing only. Production devices SHOULD set this to zero.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_MOCK_PLAT_APIS_ENABLE
|
||||
*
|
||||
|
||||
@@ -100,8 +100,9 @@ public:
|
||||
*/
|
||||
enum Code : uint8_t
|
||||
{
|
||||
kCodeDstUnreachNoRoute = OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE, ///< Destination Unreachable No Route
|
||||
kCodeFragmReasTimeEx = OT_ICMP6_CODE_FRAGM_REAS_TIME_EX, ///< Fragment Reassembly Time Exceeded
|
||||
kCodeDstUnreachNoRoute = OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE, ///< Dest Unreachable - No Route
|
||||
kCodeDstUnreachProhibited = OT_ICMP6_CODE_DST_UNREACH_PROHIBITED, ///< Dest Unreachable - Admin Prohibited
|
||||
kCodeFragmReasTimeEx = OT_ICMP6_CODE_FRAGM_REAS_TIME_EX, ///< Time Exceeded - Frag Reassembly
|
||||
};
|
||||
|
||||
static constexpr uint8_t kTypeFieldOffset = 0; ///< The byte offset of Type field in ICMP6 header.
|
||||
|
||||
@@ -958,6 +958,13 @@ Error Ip6::PassToHost(OwnedPtr<Message> &aMessagePtr,
|
||||
// Do not pass IPv6 packets that exceed kMinimalMtu.
|
||||
VerifyOrExit(aMessagePtr->GetLength() <= kMinimalMtu, error = kErrorDrop);
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
|
||||
if (!aReceive)
|
||||
{
|
||||
Get<BorderRouter::RoutingManager>().CheckReachabilityToSendIcmpError(*aMessagePtr, aHeader);
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the sender used mesh-local address as source, do not pass to
|
||||
// host unless this message is intended for this device itself.
|
||||
if (Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource()))
|
||||
|
||||
@@ -132,6 +132,24 @@ Error Leader::GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const
|
||||
return error;
|
||||
}
|
||||
|
||||
bool Leader::IsNat64(const Ip6::Address &aAddress) const
|
||||
{
|
||||
bool isNat64 = false;
|
||||
Iterator iterator = kIteratorInit;
|
||||
ExternalRouteConfig config;
|
||||
|
||||
while (GetNextExternalRoute(iterator, config) == kErrorNone)
|
||||
{
|
||||
if (config.mNat64 && config.GetPrefix().IsValidNat64() && aAddress.MatchesPrefix(config.GetPrefix()))
|
||||
{
|
||||
isNat64 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isNat64;
|
||||
}
|
||||
|
||||
const PrefixTlv *Leader::FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const
|
||||
{
|
||||
// This method iterates over Prefix TLVs which match a given IPv6
|
||||
|
||||
@@ -327,6 +327,17 @@ public:
|
||||
*/
|
||||
Error GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the given IPv6 address matches any NAT64 prefixes.
|
||||
*
|
||||
* @param[in] aAddress An IPv6 address to check.
|
||||
*
|
||||
* @retval TRUE If @p aAddress matches a NAT64 prefix.
|
||||
* @retval FALSE If @p aAddress does not match a NAT64 prefix.
|
||||
*
|
||||
*/
|
||||
bool IsNat64(const Ip6::Address &aAddress) const;
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
/**
|
||||
* Defines the match mode constants to compare two RLOC16 values.
|
||||
|
||||
@@ -802,6 +802,9 @@ class Node(object):
|
||||
def br_get_local_onlinkprefix(self):
|
||||
return self._cli_single_output('br onlinkprefix local')
|
||||
|
||||
def br_set_test_local_onlinkprefix(self, prefix):
|
||||
self._cli_no_output('br onlinkprefix test', prefix)
|
||||
|
||||
def br_get_routeprf(self):
|
||||
return self._cli_single_output('br routeprf')
|
||||
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2024, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from cli import verify
|
||||
from cli import verify_within
|
||||
import cli
|
||||
import time
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test description:
|
||||
#
|
||||
# BR sending ICMPv6 Unreachable error.
|
||||
#
|
||||
# - - - AIL- - -
|
||||
# | |
|
||||
# BR1 BR2
|
||||
# |
|
||||
# router2
|
||||
#
|
||||
|
||||
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
|
||||
print('-' * 120)
|
||||
print('Starting \'{}\''.format(test_name))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Creating `cli.Nodes` instances
|
||||
|
||||
speedup = 20
|
||||
cli.Node.set_time_speedup_factor(speedup)
|
||||
|
||||
br1 = cli.Node()
|
||||
br2 = cli.Node()
|
||||
router2 = cli.Node()
|
||||
|
||||
IF_INDEX = 1
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test implementation
|
||||
|
||||
# Start `br1` on its own Thread network with a custom OMR
|
||||
# prefix and a custom on-link prefix. Both prefixes are
|
||||
# non-ULA.
|
||||
|
||||
br1.form('net1')
|
||||
verify(br1.get_state() == 'leader')
|
||||
|
||||
br1.add_prefix('1000::/64', 'paors')
|
||||
br1.register_netdata()
|
||||
|
||||
br1.br_init(IF_INDEX, 1)
|
||||
br1.br_enable()
|
||||
|
||||
br1.br_set_test_local_onlinkprefix('2000::/64')
|
||||
|
||||
time.sleep(2)
|
||||
verify(br1.br_get_state() == 'running')
|
||||
|
||||
br1_local_onlink = br1.br_get_local_onlinkprefix()
|
||||
br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0]
|
||||
verify(br1_local_onlink == br1_favored_onlink)
|
||||
verify(br1_local_onlink == '2000:0:0:0::/64')
|
||||
|
||||
br1_local_omr = br1.br_get_local_omrprefix()
|
||||
br1_favored_omr = br1.br_get_favored_omrprefix().split()[0]
|
||||
verify(br1_favored_omr == '1000:0:0:0::/64')
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Start `br2` on its own Thread network.
|
||||
|
||||
br2.form('net2')
|
||||
router2.join(br2)
|
||||
verify(br2.get_state() == 'leader')
|
||||
verify(router2.get_state() == 'router')
|
||||
|
||||
br2.br_init(IF_INDEX, 1)
|
||||
br2.br_enable()
|
||||
|
||||
time.sleep(2)
|
||||
verify(br2.br_get_state() == 'running')
|
||||
|
||||
br2_local_omr = br2.br_get_local_omrprefix()
|
||||
br2_favored_omr = br2.br_get_favored_omrprefix().split()[0]
|
||||
verify(br2_local_omr == br2_favored_omr)
|
||||
|
||||
br2_local_onlink = br2.br_get_local_onlinkprefix()
|
||||
br2_favored_onlink = br2.br_get_favored_onlinkprefix().split()[0]
|
||||
verify(br2_local_onlink != br2_favored_onlink)
|
||||
verify(br2_favored_onlink == '2000:0:0:0::/64')
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Validate that the `br2` published '::/0' route due to
|
||||
# discovery of a non-ULA on-link prefix.
|
||||
|
||||
for node in [br2, router2]:
|
||||
routes = node.get_netdata_routes()
|
||||
verify(len(routes) == 1)
|
||||
verify(routes[0].startswith('::/0 s'))
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Send a ping from `router2` to an AIL address (derived from the
|
||||
# advertised on-link prefix). Verify that, BR2 receives and
|
||||
# forwards the message, and BR2 does not send an ICMPv6 error, as
|
||||
# the destination is within the AIL.
|
||||
|
||||
router2.ping('2000::1', verify_success=False)
|
||||
|
||||
verify(br2.get_br_counter_unicast_outbound_packets() == 1)
|
||||
|
||||
rx_history = router2.cli('history rx list')
|
||||
verify(len(rx_history) == 0)
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Send a ping from `router2` to an address on BR1 mesh network
|
||||
# (derived from its OMR prefix). Verify that again it is
|
||||
# received and forwarded by BR2 and again no ICMPv6 error is
|
||||
# sent.
|
||||
|
||||
router2.ping('1000::1', verify_success=False)
|
||||
|
||||
verify(br2.get_br_counter_unicast_outbound_packets() == 2)
|
||||
|
||||
rx_history = router2.cli('history rx list')
|
||||
verify(len(rx_history) == 0)
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Send a ping to an outside AIL address, make sure BR2
|
||||
# does receive and forward it and now also sends an ICMPv6
|
||||
# error
|
||||
|
||||
router2.ping('3000::', verify_success=False)
|
||||
|
||||
verify(br2.get_br_counter_unicast_outbound_packets() == 3)
|
||||
|
||||
rx_history = router2.cli('history rx list')
|
||||
verify(rx_history[1].strip().startswith('type:ICMP6(Unreach)'))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test finished
|
||||
|
||||
cli.Node.finalize_all_nodes()
|
||||
|
||||
print('\'{}\' passed.'.format(test_name))
|
||||
@@ -57,6 +57,8 @@
|
||||
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE 1
|
||||
|
||||
@@ -202,6 +202,8 @@ if [ "$TORANJ_CLI" = 1 ]; then
|
||||
run cli/test-500-two-brs-two-networks.py
|
||||
run cli/test-501-multi-br-failure-recovery.py
|
||||
run cli/test-502-multi-br-leader-failure-recovery.py
|
||||
run cli/test-503-peer-tbr-discovery.py
|
||||
run cli/test-504-br-icmp-unreach-err.py
|
||||
run cli/test-601-channel-manager-channel-change.py
|
||||
# Skip the "channel-select" test on a TREL only radio link, since it
|
||||
# requires energy scan which is not supported in this case.
|
||||
|
||||
Reference in New Issue
Block a user