mirror of
https://github.com/espressif/openthread.git
synced 2026-08-04 09:57:47 +00:00
[network-data] enhance route lookup for ALOC destination (#10509)
This commit updates `NetworkData::Leader` to enhance route lookup for ALOC destinations. It contains the following changes: - Adds `LookupRouteForServiceAloc()` to look up the route for a service ALOC. This method uses the common `CompareRouteEntries()`, which applies the same rules as in external route lookup. - Adds `LookupRouteForAgentAloc()` to look up the route for DHCPv6 or ND agent ALOC. - Adds `FindPrefixTlvForContextId()` to find a Prefix TLV matching a given Context ID (used in `GetContext()` and `LookupRouteForAgentAloc()`) - Adds `LookupRouteIn()` to perform route lookup among all entries of a given `PrefixTlv` that match a given `EntryChecker` function (used for `DefaultRouteLookup()` or `LookupRouteForAgentAloc()`) - Adds `test-031-service-aloc-route-lookup.py`.
This commit is contained in:
@@ -187,34 +187,46 @@ Error Leader::GetContext(const Ip6::Address &aAddress, Lowpan::Context &aContext
|
||||
return (aContext.mPrefix.GetLength() > 0) ? kErrorNone : kErrorNotFound;
|
||||
}
|
||||
|
||||
Error Leader::GetContext(uint8_t aContextId, Lowpan::Context &aContext) const
|
||||
const PrefixTlv *Leader::FindPrefixTlvForContextId(uint8_t aContextId, const ContextTlv *&aContextTlv) const
|
||||
{
|
||||
Error error = kErrorNotFound;
|
||||
TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd());
|
||||
const PrefixTlv *prefixTlv;
|
||||
|
||||
if (aContextId == Mle::kMeshLocalPrefixContextId)
|
||||
{
|
||||
GetContextForMeshLocalPrefix(aContext);
|
||||
ExitNow(error = kErrorNone);
|
||||
}
|
||||
|
||||
while ((prefixTlv = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
|
||||
{
|
||||
const ContextTlv *contextTlv = prefixTlv->FindSubTlv<ContextTlv>();
|
||||
|
||||
if ((contextTlv == nullptr) || (contextTlv->GetContextId() != aContextId))
|
||||
if ((contextTlv != nullptr) && (contextTlv->GetContextId() == aContextId))
|
||||
{
|
||||
continue;
|
||||
aContextTlv = contextTlv;
|
||||
break;
|
||||
}
|
||||
|
||||
prefixTlv->CopyPrefixTo(aContext.mPrefix);
|
||||
aContext.mContextId = contextTlv->GetContextId();
|
||||
aContext.mCompressFlag = contextTlv->IsCompress();
|
||||
aContext.mIsValid = true;
|
||||
ExitNow(error = kErrorNone);
|
||||
}
|
||||
|
||||
return prefixTlv;
|
||||
}
|
||||
|
||||
Error Leader::GetContext(uint8_t aContextId, Lowpan::Context &aContext) const
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd());
|
||||
const PrefixTlv *prefixTlv;
|
||||
const ContextTlv *contextTlv;
|
||||
|
||||
if (aContextId == Mle::kMeshLocalPrefixContextId)
|
||||
{
|
||||
GetContextForMeshLocalPrefix(aContext);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
prefixTlv = FindPrefixTlvForContextId(aContextId, contextTlv);
|
||||
VerifyOrExit(prefixTlv != nullptr, error = kErrorNotFound);
|
||||
|
||||
prefixTlv->CopyPrefixTo(aContext.mPrefix);
|
||||
aContext.mContextId = contextTlv->GetContextId();
|
||||
aContext.mCompressFlag = contextTlv->IsCompress();
|
||||
aContext.mIsValid = true;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
@@ -299,13 +311,22 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
template <typename EntryType> int Leader::CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const
|
||||
int Leader::CompareRouteEntries(const BorderRouterEntry &aFirst, const BorderRouterEntry &aSecond) const
|
||||
{
|
||||
// `EntryType` can be `HasRouteEntry` or `BorderRouterEntry`.
|
||||
|
||||
return CompareRouteEntries(aFirst.GetPreference(), aFirst.GetRloc(), aSecond.GetPreference(), aSecond.GetRloc());
|
||||
}
|
||||
|
||||
int Leader::CompareRouteEntries(const HasRouteEntry &aFirst, const HasRouteEntry &aSecond) const
|
||||
{
|
||||
return CompareRouteEntries(aFirst.GetPreference(), aFirst.GetRloc(), aSecond.GetPreference(), aSecond.GetRloc());
|
||||
}
|
||||
|
||||
int Leader::CompareRouteEntries(const ServerTlv &aFirst, const ServerTlv &aSecond) const
|
||||
{
|
||||
return CompareRouteEntries(/* aFirstPreference */ 0, aFirst.GetServer16(), /* aSecondPreference */ 0,
|
||||
aSecond.GetServer16());
|
||||
}
|
||||
|
||||
int Leader::CompareRouteEntries(int8_t aFirstPreference,
|
||||
uint16_t aFirstRloc,
|
||||
int8_t aSecondPreference,
|
||||
@@ -393,39 +414,51 @@ Error Leader::ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestin
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Leader::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const
|
||||
Error Leader::LookupRouteIn(const PrefixTlv &aPrefixTlv, EntryChecker aEntryChecker, uint16_t &aRloc16) const
|
||||
{
|
||||
// Iterates over all `BorderRouterEntry` associated with
|
||||
// `aPrefixTlv` which also match `aEntryChecker` and determine the
|
||||
// best route. For example, this is used from `DefaultRouteLookup()`
|
||||
// to look up best default route.
|
||||
|
||||
Error error = kErrorNoRoute;
|
||||
TlvIterator subTlvIterator(aPrefix);
|
||||
TlvIterator subTlvIterator(aPrefixTlv);
|
||||
const BorderRouterTlv *brTlv;
|
||||
const BorderRouterEntry *route = nullptr;
|
||||
const BorderRouterEntry *bestEntry = nullptr;
|
||||
|
||||
while ((brTlv = subTlvIterator.Iterate<BorderRouterTlv>()) != nullptr)
|
||||
{
|
||||
for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry();
|
||||
entry = entry->GetNext())
|
||||
{
|
||||
if (!entry->IsDefaultRoute())
|
||||
if (!aEntryChecker(*entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (route == nullptr || CompareRouteEntries(*entry, *route) > 0)
|
||||
if ((bestEntry == nullptr) || CompareRouteEntries(*entry, *bestEntry) > 0)
|
||||
{
|
||||
route = entry;
|
||||
bestEntry = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (route != nullptr)
|
||||
if (bestEntry != nullptr)
|
||||
{
|
||||
aRloc16 = route->GetRloc();
|
||||
aRloc16 = bestEntry->GetRloc();
|
||||
error = kErrorNone;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool Leader::IsEntryDefaultRoute(const BorderRouterEntry &aEntry) { return aEntry.IsDefaultRoute(); }
|
||||
|
||||
Error Leader::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const
|
||||
{
|
||||
return LookupRouteIn(aPrefix, IsEntryDefaultRoute, aRloc16);
|
||||
}
|
||||
|
||||
Error Leader::SetNetworkData(uint8_t aVersion,
|
||||
uint8_t aStableVersion,
|
||||
Type aType,
|
||||
|
||||
@@ -423,15 +423,6 @@ public:
|
||||
*/
|
||||
void HandleNetworkDataRestoredAfterReset(void);
|
||||
|
||||
/**
|
||||
* Scans network data for given Service ID and returns pointer to the respective TLV, if present.
|
||||
*
|
||||
* @param aServiceId Service ID to look for.
|
||||
* @return Pointer to the Service TLV for given Service ID, or nullptr if not present.
|
||||
*
|
||||
*/
|
||||
const ServiceTlv *FindServiceById(uint8_t aServiceId) const;
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
@@ -453,16 +444,24 @@ public:
|
||||
private:
|
||||
using FilterIndexes = MeshCoP::SteeringData::HashBitIndexes;
|
||||
|
||||
const PrefixTlv *FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const;
|
||||
typedef bool (&EntryChecker)(const BorderRouterEntry &aEntry);
|
||||
|
||||
template <typename EntryType> int CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const;
|
||||
int CompareRouteEntries(int8_t aFirstPreference,
|
||||
uint16_t aFirstRloc,
|
||||
int8_t aSecondPreference,
|
||||
uint16_t aSecondRloc) const;
|
||||
const PrefixTlv *FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const;
|
||||
const PrefixTlv *FindPrefixTlvForContextId(uint8_t aContextId, const ContextTlv *&aContextTlv) const;
|
||||
|
||||
int CompareRouteEntries(const BorderRouterEntry &aFirst, const BorderRouterEntry &aSecond) const;
|
||||
int CompareRouteEntries(const HasRouteEntry &aFirst, const HasRouteEntry &aSecond) const;
|
||||
int CompareRouteEntries(const ServerTlv &aFirst, const ServerTlv &aSecond) const;
|
||||
int CompareRouteEntries(int8_t aFirstPreference,
|
||||
uint16_t aFirstRloc,
|
||||
int8_t aSecondPreference,
|
||||
uint16_t aSecondRloc) const;
|
||||
|
||||
static bool IsEntryDefaultRoute(const BorderRouterEntry &aEntry);
|
||||
|
||||
Error ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestination, uint16_t &aRloc16) const;
|
||||
Error DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const;
|
||||
Error LookupRouteIn(const PrefixTlv &aPrefixTlv, EntryChecker aEntryChecker, uint16_t &aRloc16) const;
|
||||
Error SteeringDataCheck(const FilterIndexes &aFilterIndexes) const;
|
||||
void GetContextForMeshLocalPrefix(Lowpan::Context &aContext) const;
|
||||
Error ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType, uint16_t &aValue) const;
|
||||
@@ -480,13 +479,6 @@ private:
|
||||
static constexpr uint8_t kMinServiceId = 0x00;
|
||||
static constexpr uint8_t kMaxServiceId = 0x0f;
|
||||
|
||||
enum AnycastType : uint8_t
|
||||
{
|
||||
kAnycastDhcp6Agent,
|
||||
kAnycastNdAgent,
|
||||
kAnycastService,
|
||||
};
|
||||
|
||||
class ChangedFlags
|
||||
{
|
||||
public:
|
||||
@@ -571,8 +563,11 @@ private:
|
||||
|
||||
void HandleTimer(void);
|
||||
|
||||
Error AnycastLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aRloc16) const;
|
||||
void EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const;
|
||||
static bool IsEntryForDhcp6Agent(const BorderRouterEntry &aEntry);
|
||||
static bool IsEntryForNdAgent(const BorderRouterEntry &aEntry);
|
||||
|
||||
Error LookupRouteForServiceAloc(uint16_t aAloc16, uint16_t &aRloc16) const;
|
||||
Error LookupRouteForAgentAloc(uint8_t aContextId, EntryChecker aEntryChecker, uint16_t &aRloc16) const;
|
||||
|
||||
void RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkData);
|
||||
|
||||
@@ -582,7 +577,8 @@ private:
|
||||
Error AddService(const ServiceTlv &aService, ChangedFlags &aChangedFlags);
|
||||
Error AddServer(const ServerTlv &aServer, ServiceTlv &aDstService, ChangedFlags &aChangedFlags);
|
||||
|
||||
Error AllocateServiceId(uint8_t &aServiceId) const;
|
||||
Error AllocateServiceId(uint8_t &aServiceId) const;
|
||||
const ServiceTlv *FindServiceById(uint8_t aServiceId) const;
|
||||
|
||||
void RemoveContext(uint8_t aContextId);
|
||||
void RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId);
|
||||
|
||||
@@ -119,6 +119,8 @@ Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
aRloc16 = Mle::kInvalidRloc16;
|
||||
|
||||
if (aAloc16 == Mle::kAloc16Leader)
|
||||
{
|
||||
aRloc16 = Get<Mle::Mle>().GetLeaderRloc16();
|
||||
@@ -127,13 +129,11 @@ Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const
|
||||
{
|
||||
uint8_t contextId = static_cast<uint8_t>(aAloc16 - Mle::kAloc16DhcpAgentStart + 1);
|
||||
|
||||
error = AnycastLookup(contextId, kAnycastDhcp6Agent, aRloc16);
|
||||
error = LookupRouteForAgentAloc(contextId, IsEntryForDhcp6Agent, aRloc16);
|
||||
}
|
||||
else if (aAloc16 <= Mle::kAloc16ServiceEnd)
|
||||
{
|
||||
uint8_t serviceId = static_cast<uint8_t>(aAloc16 - Mle::kAloc16ServiceStart);
|
||||
|
||||
error = AnycastLookup(serviceId, kAnycastService, aRloc16);
|
||||
error = LookupRouteForServiceAloc(aAloc16, aRloc16);
|
||||
}
|
||||
else if (aAloc16 <= Mle::kAloc16CommissionerEnd)
|
||||
{
|
||||
@@ -150,111 +150,80 @@ Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const
|
||||
{
|
||||
uint8_t contextId = static_cast<uint8_t>(aAloc16 - Mle::kAloc16NeighborDiscoveryAgentStart + 1);
|
||||
|
||||
error = AnycastLookup(contextId, kAnycastNdAgent, aRloc16);
|
||||
error = LookupRouteForAgentAloc(contextId, IsEntryForNdAgent, aRloc16);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExitNow(error = kErrorDrop);
|
||||
error = kErrorDrop;
|
||||
}
|
||||
|
||||
SuccessOrExit(error);
|
||||
VerifyOrExit(aRloc16 != Mle::kInvalidRloc16, error = kErrorNoRoute);
|
||||
|
||||
if (Mle::IsChildRloc16(aRloc16))
|
||||
{
|
||||
// If the selected destination is a child, we use its parent
|
||||
// as the destination unless the device itself is the
|
||||
// parent of the `aRloc16`.
|
||||
|
||||
uint16_t parentRloc16 = Mle::ParentRloc16ForRloc16(aRloc16);
|
||||
|
||||
if (!Get<Mle::Mle>().HasRloc16(parentRloc16))
|
||||
{
|
||||
aRloc16 = parentRloc16;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Leader::AnycastLookup(uint8_t aServiceId, AnycastType aType, uint16_t &aRloc16) const
|
||||
Error Leader::LookupRouteForServiceAloc(uint16_t aAloc16, uint16_t &aRloc16) const
|
||||
{
|
||||
Iterator iterator = kIteratorInit;
|
||||
uint8_t bestCost = Mle::kMaxRouteCost;
|
||||
uint16_t bestDest = Mle::kInvalidRloc16;
|
||||
Error error = kErrorNoRoute;
|
||||
const ServiceTlv *serviceTlv = FindServiceById(Mle::ServiceIdFromAloc(aAloc16));
|
||||
|
||||
switch (aType)
|
||||
if (serviceTlv != nullptr)
|
||||
{
|
||||
case kAnycastDhcp6Agent:
|
||||
case kAnycastNdAgent:
|
||||
{
|
||||
OnMeshPrefixConfig config;
|
||||
Lowpan::Context context;
|
||||
TlvIterator subTlvIterator(*serviceTlv);
|
||||
const ServerTlv *bestServerTlv = nullptr;
|
||||
const ServerTlv *serverTlv;
|
||||
|
||||
SuccessOrExit(GetContext(aServiceId, context));
|
||||
|
||||
while (GetNextOnMeshPrefix(iterator, config) == kErrorNone)
|
||||
while ((serverTlv = subTlvIterator.Iterate<ServerTlv>()) != nullptr)
|
||||
{
|
||||
if (config.GetPrefix() != context.mPrefix)
|
||||
if ((bestServerTlv == nullptr) || CompareRouteEntries(*serverTlv, *bestServerTlv) > 0)
|
||||
{
|
||||
continue;
|
||||
bestServerTlv = serverTlv;
|
||||
}
|
||||
|
||||
switch (aType)
|
||||
{
|
||||
case kAnycastDhcp6Agent:
|
||||
if (!(config.mDhcp || config.mConfigure))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case kAnycastNdAgent:
|
||||
if (!config.mNdDns)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OT_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
EvaluateRoutingCost(config.mRloc16, bestCost, bestDest);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case kAnycastService:
|
||||
{
|
||||
ServiceConfig config;
|
||||
|
||||
while (GetNextService(iterator, config) == kErrorNone)
|
||||
if (bestServerTlv != nullptr)
|
||||
{
|
||||
if (config.mServiceId != aServiceId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
EvaluateRoutingCost(config.mServerConfig.mRloc16, bestCost, bestDest);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Mle::IsChildRloc16(bestDest))
|
||||
{
|
||||
// If the selected destination is a child, we use its parent
|
||||
// as the destination unless the device itself is the
|
||||
// parent of the `bestDest`.
|
||||
|
||||
uint16_t bestDestParent = Mle::ParentRloc16ForRloc16(bestDest);
|
||||
|
||||
if (!Get<Mle::Mle>().HasRloc16(bestDestParent))
|
||||
{
|
||||
bestDest = bestDestParent;
|
||||
aRloc16 = bestServerTlv->GetServer16();
|
||||
error = kErrorNone;
|
||||
}
|
||||
}
|
||||
|
||||
aRloc16 = bestDest;
|
||||
|
||||
exit:
|
||||
return (bestDest != Mle::kInvalidRloc16) ? kErrorNone : kErrorNoRoute;
|
||||
return error;
|
||||
}
|
||||
|
||||
void Leader::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const
|
||||
{
|
||||
uint8_t cost = Get<RouterTable>().GetPathCost(aDest);
|
||||
bool Leader::IsEntryForDhcp6Agent(const BorderRouterEntry &aEntry) { return aEntry.IsDhcp() || aEntry.IsConfigure(); }
|
||||
|
||||
if ((aBestDest == Mle::kInvalidRloc16) || (cost < aBestCost))
|
||||
{
|
||||
aBestDest = aDest;
|
||||
aBestCost = cost;
|
||||
}
|
||||
bool Leader::IsEntryForNdAgent(const BorderRouterEntry &aEntry) { return aEntry.IsNdDns(); }
|
||||
|
||||
Error Leader::LookupRouteForAgentAloc(uint8_t aContextId, EntryChecker aEntryChecker, uint16_t &aRloc16) const
|
||||
{
|
||||
Error error = kErrorNoRoute;
|
||||
const PrefixTlv *prefixTlv;
|
||||
const ContextTlv *contextTlv;
|
||||
|
||||
prefixTlv = FindPrefixTlvForContextId(aContextId, contextTlv);
|
||||
VerifyOrExit(prefixTlv != nullptr);
|
||||
|
||||
error = LookupRouteIn(*prefixTlv, aEntryChecker, aRloc16);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode)
|
||||
|
||||
@@ -469,6 +469,9 @@ class Node(object):
|
||||
def get_mle_counter(self):
|
||||
return self.cli('counters mle')
|
||||
|
||||
def get_ip_counters(self):
|
||||
return Node.parse_list(self.cli('counters ip'))
|
||||
|
||||
def get_br_counter_unicast_outbound_packets(self):
|
||||
outputs = self.cli('counters br')
|
||||
for line in outputs:
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
#!/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: Service ALOC destination route lookup
|
||||
#
|
||||
# Test topology:
|
||||
#
|
||||
# r1---- r2 ---- r3 ---- r4
|
||||
# |
|
||||
# |
|
||||
# fed1
|
||||
#
|
||||
# The same service is added on `r4` and `fed1`.
|
||||
|
||||
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
|
||||
print('-' * 120)
|
||||
print('Starting \'{}\''.format(test_name))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Creating `cli.Nodes` instances
|
||||
|
||||
speedup = 40
|
||||
cli.Node.set_time_speedup_factor(speedup)
|
||||
|
||||
r1 = cli.Node()
|
||||
r2 = cli.Node()
|
||||
r3 = cli.Node()
|
||||
r4 = cli.Node()
|
||||
fed1 = cli.Node()
|
||||
|
||||
nodes = [r1, r2, r3, r4, fed1]
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Form topology
|
||||
|
||||
r1.allowlist_node(r2)
|
||||
r1.allowlist_node(fed1)
|
||||
|
||||
r2.allowlist_node(r1)
|
||||
r2.allowlist_node(r3)
|
||||
|
||||
r3.allowlist_node(r2)
|
||||
r3.allowlist_node(r4)
|
||||
|
||||
r4.allowlist_node(r3)
|
||||
|
||||
fed1.allowlist_node(r1)
|
||||
|
||||
r1.form("srv-aloc")
|
||||
r2.join(r1)
|
||||
r3.join(r2)
|
||||
r4.join(r3)
|
||||
fed1.join(r1, cli.JOIN_TYPE_REED)
|
||||
|
||||
verify(r1.get_state() == 'leader')
|
||||
verify(r2.get_state() == 'router')
|
||||
verify(r3.get_state() == 'router')
|
||||
verify(r4.get_state() == 'router')
|
||||
verify(fed1.get_state() == 'child')
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test Implementation
|
||||
|
||||
# Wait till first router has either established a link or
|
||||
# has a valid "next hop" towards all other routers.
|
||||
|
||||
r1_rloc16 = int(r1.get_rloc16(), 16)
|
||||
|
||||
|
||||
def check_r1_router_table():
|
||||
table = r1.get_router_table()
|
||||
verify(len(table) == 4)
|
||||
for entry in table:
|
||||
verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63)
|
||||
|
||||
|
||||
verify_within(check_r1_router_table, 120)
|
||||
|
||||
# Add the same service on `r4` and `fed1`
|
||||
|
||||
r4.cli('service add 44970 11 00')
|
||||
r4.register_netdata()
|
||||
|
||||
fed1.cli('service add 44970 11 00')
|
||||
fed1.register_netdata()
|
||||
|
||||
|
||||
def check_netdata_services(expected_num_services):
|
||||
# Check that all nodes see the `expected_num_services` service
|
||||
# entries in network data.
|
||||
for node in nodes:
|
||||
verify(len(node.get_netdata_services()) == expected_num_services)
|
||||
|
||||
|
||||
verify_within(check_netdata_services, 100, 2)
|
||||
|
||||
# Determine the ALOC address of the added service.
|
||||
|
||||
aloc = r4.get_mesh_local_prefix().split('/')[0] + 'ff:fe00:fc10'
|
||||
|
||||
# Ping ALOC address from `r3` and verify that `r4` responds.
|
||||
# `r4` should be chosen due to its shorter path cost from `r3`.
|
||||
|
||||
old_counters = r4.get_ip_counters()
|
||||
r3.ping(aloc)
|
||||
new_counters = r4.get_ip_counters()
|
||||
|
||||
verify(int(new_counters['RxSuccess']) > int(old_counters['RxSuccess']))
|
||||
verify(int(new_counters['TxSuccess']) > int(old_counters['TxSuccess']))
|
||||
|
||||
# Ping ALOC address from `r1` and verify that `fed1` responds.
|
||||
# `fed1` should be chosen due to its shorter path cost from `r1`.
|
||||
|
||||
old_counters = fed1.get_ip_counters()
|
||||
r1.ping(aloc)
|
||||
new_counters = fed1.get_ip_counters()
|
||||
|
||||
verify(int(new_counters['RxSuccess']) > int(old_counters['RxSuccess']))
|
||||
verify(int(new_counters['TxSuccess']) > int(old_counters['TxSuccess']))
|
||||
|
||||
# Ping ALOC address from `r2` and verify that `r4` responds.
|
||||
# Both `r4` and `fed1` have the same path cost, but `r4` is
|
||||
# preferred because it is acting as a router.
|
||||
|
||||
old_counters = r4.get_ip_counters()
|
||||
r2.ping(aloc)
|
||||
new_counters = r4.get_ip_counters()
|
||||
|
||||
verify(int(new_counters['RxSuccess']) > int(old_counters['RxSuccess']))
|
||||
verify(int(new_counters['TxSuccess']) > int(old_counters['TxSuccess']))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test finished
|
||||
|
||||
cli.Node.finalize_all_nodes()
|
||||
|
||||
print('\'{}\' passed.'.format(test_name))
|
||||
@@ -195,6 +195,7 @@ if [ "$TORANJ_CLI" = 1 ]; then
|
||||
run cli/test-028-border-agent-ephemeral-key.py
|
||||
run cli/test-029-pending-dataset-key-change.py
|
||||
run cli/test-030-anycast-forwarding.py
|
||||
run cli/./test-031-service-aloc-route-lookup.py
|
||||
run cli/test-400-srp-client-server.py
|
||||
run cli/test-401-srp-server-address-cache-snoop.py
|
||||
run cli/test-500-two-brs-two-networks.py
|
||||
|
||||
Reference in New Issue
Block a user