Skip to content

Commit

Permalink
[telemetry] add external route related telemetry (#2284)
Browse files Browse the repository at this point in the history
This commit added new entry `ExternalRoutes` inside message
`wpan_border_router` to indicate the information about the external
route added in leader network data

The `ExternalRoutes` has three bool fields:
 - `has_default_route_added`: if leader network data has `route ::/0`
 - `has_ula_route_added`: if leader network data has route `fc00::/7`
 - `has_others_route_added`: if leader network has others route

These information are collectly only when both TELEMETRY_DATA_API and
BORDER_ROUTING are enabled.

This commit also enhanced the class `otbr::Ip6Address` and class
`otbr::Ip6Prefix` by adding some more constructors, so that the ipv6
address/prefix can be easily constructed by a string. The `operator==`
of `Ip6Prefix` is also added to compare the prefix length and first
N-bit of the prefix, where N is length of the prefix.
  • Loading branch information
zesonzhang committed May 31, 2024
1 parent a05cdc4 commit 78fa14b
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/common/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,30 @@ Ip6Address Ip6Address::FromString(const char *aStr)
return addr;
}

bool Ip6Prefix::operator==(const Ip6Prefix &aOther) const
{
bool isEqual = false;
uint8_t lengthFullBytes; // the number of complete bytes in the prefix length
uint8_t lengthRemainingBits; // the number of remaining bits in the prefix length that do not form a complete byte

VerifyOrExit(mLength == aOther.mLength);

lengthFullBytes = mLength / 8;
lengthRemainingBits = mLength % 8;
VerifyOrExit(memcmp(mPrefix.m8, aOther.mPrefix.m8, lengthFullBytes) == 0);

if (lengthRemainingBits > 0)
{
uint8_t mask = 0xff << (8 - lengthRemainingBits);
VerifyOrExit((mPrefix.m8[lengthFullBytes] & mask) == (aOther.mPrefix.m8[lengthFullBytes] & mask));
}

isEqual = true;

exit:
return isEqual;
}

void Ip6Prefix::Set(const otIp6Prefix &aPrefix)
{
memcpy(reinterpret_cast<void *>(this), &aPrefix, sizeof(*this));
Expand Down
51 changes: 51 additions & 0 deletions src/common/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ class Ip6Address
*/
Ip6Address(const uint8_t (&aAddress)[16]);

/**
* Constructor with a string.
*
* @param[in] aString The string representing the IPv6 address.
*
*/
Ip6Address(const char *aString) { FromString(aString, *this); }

/**
* This method overloads `<` operator and compares if the Ip6 address is smaller than the other address.
*
Expand Down Expand Up @@ -314,6 +322,33 @@ class Ip6Prefix
*/
Ip6Prefix(void) { Clear(); }

/**
* Constructor with an Ip6 address string and prefix length.
*
* @param[in] aIp6AddrStr The IPv6 address string.
* @param[in] aLength The prefix length.
*
*/
Ip6Prefix(const char *aIp6AddrStr, uint8_t aLength)
: mPrefix(aIp6AddrStr)
, mLength(aLength)
{
}

/**
* This method overloads `==` operator for comparing two Ip6Prefix objects by comparing their prefix and length.
*
* Two IpPrefix objects are considered equal if:
* - their lengths are equal, and
* - their first n-bits of the addresses are the same, where n is the length of the prefix.
*
* @param[in] aOther The Ip6Prefix object to compare with.
*
* @returns True if the two objects are equal, false otherwise.
*
*/
bool operator==(const Ip6Prefix &aOther) const;

/**
* This method sets the Ip6 prefix to an `otIp6Prefix` value.
*
Expand Down Expand Up @@ -344,6 +379,22 @@ class Ip6Prefix
*/
bool IsValid(void) const { return mLength > 0 && mLength <= 128; }

/**
* This method checks if the object is the default route prefix ("::/0")
*
* @returns true if the object is the default route prefix, false otherwise.
*
*/
bool IsDefaultRoutePrefix(void) const { return (*this == Ip6Prefix("::", 0)); }

/**
* This method checks if the object is the ULA prefix ("fc00::/7")
*
* @returns true if the object is the ULA prefix, false otherwise.
*
*/
bool IsUlaPrefix(void) const { return (*this == Ip6Prefix("fc00::", 7)); }

Ip6Address mPrefix; ///< The IPv6 prefix.
uint8_t mLength; ///< The IPv6 prefix length (in bits).
};
Expand Down
16 changes: 16 additions & 0 deletions src/proto/thread_telemetry.proto
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,19 @@ message TelemetryData {
optional uint32 global_unicast_address_count = 7;
}

// Message to indicate the information of external routes in network data.
message ExternalRoutes {
// Indicates whether the a zero-length prefix (::/0) added from this BR
optional bool has_default_route_added = 1;

// Indicates whether the a ULA prefix (fc00::/7) added from this BR
optional bool has_ula_route_added = 2;

// Indicates whether the other prefixes (other than "::/0" or "fc00::/7") added
// from this BR. (BR is a managed infrastructure router).
optional bool has_others_route_added = 3;
}

message WpanBorderRouter {
// Border routing counters
optional BorderRoutingCounters border_routing_counters = 1;
Expand Down Expand Up @@ -532,6 +545,9 @@ message TelemetryData {

// Information about the infra link
optional InfraLinkInfo infra_link_info = 12;

// Information about the external routes in network data.
optional ExternalRoutes external_route_info = 13;
}

message RcpStabilityStatistics {
Expand Down
44 changes: 44 additions & 0 deletions src/utils/thread_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,46 @@ void ThreadHelper::DetachGracefullyCallback(void)
}

#if OTBR_ENABLE_TELEMETRY_DATA_API
#if OTBR_ENABLE_BORDER_ROUTING
void ThreadHelper::RetrieveExternalRouteInfo(threadnetwork::TelemetryData_ExternalRoutes *aExternalRouteInfo)
{
bool isDefaultRouteAdded = false;
bool isUlaRouteAdded = false;
bool isOthersRouteAdded = false;
Ip6Prefix prefix;
uint16_t rloc16 = otThreadGetRloc16(mInstance);

otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
otExternalRouteConfig config;

while (otNetDataGetNextRoute(mInstance, &iterator, &config) == OT_ERROR_NONE)
{
if (!config.mStable || config.mRloc16 != rloc16)
{
continue;
}

prefix.Set(config.mPrefix);
if (prefix.IsDefaultRoutePrefix())
{
isDefaultRouteAdded = true;
}
else if (prefix.IsUlaPrefix())
{
isUlaRouteAdded = true;
}
else
{
isOthersRouteAdded = true;
}
}

aExternalRouteInfo->set_has_default_route_added(isDefaultRouteAdded);
aExternalRouteInfo->set_has_ula_route_added(isUlaRouteAdded);
aExternalRouteInfo->set_has_others_route_added(isOthersRouteAdded);
}
#endif // OTBR_ENABLE_BORDER_ROUTING

otError ThreadHelper::RetrieveTelemetryData(Mdns::Publisher *aPublisher, threadnetwork::TelemetryData &telemetryData)
{
otError error = OT_ERROR_NONE;
Expand Down Expand Up @@ -1281,6 +1321,10 @@ otError ThreadHelper::RetrieveTelemetryData(Mdns::Publisher *aPublisher, threadn
infraLinkInfo->set_global_unicast_address_count(addressCounters.mGlobalUnicastAddresses);
}
// End of InfraLinkInfo section.

// ExternalRoutes section
RetrieveExternalRouteInfo(wpanBorderRouter->mutable_external_route_info());

#endif

#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
Expand Down
4 changes: 4 additions & 0 deletions src/utils/thread_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ class ThreadHelper

void ActiveDatasetChangedCallback(void);

#if OTBR_ENABLE_TELEMETRY_DATA_API && OTBR_ENABLE_BORDER_ROUTING
void RetrieveExternalRouteInfo(threadnetwork::TelemetryData_ExternalRoutes *aExternalRouteInfo);
#endif

otInstance *mInstance;

otbr::Ncp::RcpHost *mHost;
Expand Down
3 changes: 3 additions & 0 deletions tests/dbus/test_dbus_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ void CheckTelemetryData(ThreadApiDBus *aApi)
TEST_ASSERT(telemetryData.wpan_border_router().infra_link_info().link_local_address_count() == 0);
TEST_ASSERT(telemetryData.wpan_border_router().infra_link_info().unique_local_address_count() == 0);
TEST_ASSERT(telemetryData.wpan_border_router().infra_link_info().global_unicast_address_count() == 0);
TEST_ASSERT(telemetryData.wpan_border_router().external_route_info().has_default_route_added() == false);
TEST_ASSERT(telemetryData.wpan_border_router().external_route_info().has_ula_route_added() == false);
TEST_ASSERT(telemetryData.wpan_border_router().external_route_info().has_others_route_added() == false);
#endif
TEST_ASSERT(telemetryData.wpan_border_router().mdns().service_registration_responses().success_count() > 0);
#if OTBR_ENABLE_NAT64
Expand Down
1 change: 1 addition & 0 deletions tests/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ add_executable(otbr-test-unit
$<$<BOOL:${OTBR_DBUS}>:test_dbus_message.cpp>
$<$<STREQUAL:${OTBR_MDNS},"mDNSResponder">:test_mdns_mdnssd.cpp>
main.cpp
test_common_types.cpp
test_dns_utils.cpp
test_logging.cpp
test_once_callback.cpp
Expand Down
116 changes: 116 additions & 0 deletions tests/unit/test_common_types.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* 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.
*/

#include <CppUTest/TestHarness.h>

#include "common/types.hpp"

//-------------------------------------------------------------
// Test for Ip6Address
// TODO: Add Ip6Address tests
TEST_GROUP(Ip6Address){};

TEST(Ip6Address, NULL)
{
TEST_EXIT;
}

//-------------------------------------------------------------
// Test for Ip6Prefix
TEST_GROUP(Ip6Prefix){};

TEST(Ip6Prefix, ConstructorWithAddressAndLength)
{
using otbr::Ip6Prefix;

Ip6Prefix prefix1("::", 0);
STRCMP_EQUAL("::/0", prefix1.ToString().c_str());
CHECK_EQUAL(0, prefix1.mLength);

Ip6Prefix prefix2("fc00::", 7);
STRCMP_EQUAL("fc00::/7", prefix2.ToString().c_str());
CHECK_EQUAL(7, prefix2.mLength);

Ip6Prefix prefix3("2001:db8::", 64);
STRCMP_EQUAL("2001:db8::/64", prefix3.ToString().c_str());
CHECK_EQUAL(64, prefix3.mLength);

Ip6Prefix prefix4("2001:db8::1", 128);
STRCMP_EQUAL("2001:db8::1/128", prefix4.ToString().c_str());
CHECK_EQUAL(128, prefix4.mLength);
}

TEST(Ip6Prefix, EqualityOperator)
{
using otbr::Ip6Prefix;

// same prefix and length
CHECK(Ip6Prefix("::", 0) == Ip6Prefix("::", 0));
CHECK(Ip6Prefix("fc00::", 0) == Ip6Prefix("fc00::", 0));
CHECK(Ip6Prefix("2001:db8::", 64) == Ip6Prefix("2001:db8::", 64));

// same prefix, different length
CHECK_FALSE(Ip6Prefix("::", 0) == Ip6Prefix("::", 7));
CHECK_FALSE(Ip6Prefix("fc00::", 0) == Ip6Prefix("fc00::", 7));
CHECK_FALSE(Ip6Prefix("fc00::", 7) == Ip6Prefix("fc00::", 8));
CHECK_FALSE(Ip6Prefix("2001:db8::", 64) == Ip6Prefix("2001:db8::", 32));

// different prefix object, same length
CHECK(Ip6Prefix("::", 0) == Ip6Prefix("::1", 0));
CHECK(Ip6Prefix("::", 0) == Ip6Prefix("2001::", 0));
CHECK(Ip6Prefix("::", 0) == Ip6Prefix("2001:db8::1", 0));
CHECK(Ip6Prefix("fc00::", 7) == Ip6Prefix("fd00::", 7));
CHECK(Ip6Prefix("fc00::", 8) == Ip6Prefix("fc00:1234::", 8));
CHECK(Ip6Prefix("2001:db8::", 32) == Ip6Prefix("2001:db8:abcd::", 32));
CHECK(Ip6Prefix("2001:db8:0:1::", 63) == Ip6Prefix("2001:db8::", 63));
CHECK(Ip6Prefix("2001:db8::", 64) == Ip6Prefix("2001:db8::1", 64));
CHECK(Ip6Prefix("2001:db8::3", 127) == Ip6Prefix("2001:db8::2", 127));

CHECK_FALSE(Ip6Prefix("fc00::", 7) == Ip6Prefix("fe00::", 7));
CHECK_FALSE(Ip6Prefix("fc00::", 16) == Ip6Prefix("fc01::", 16));
CHECK_FALSE(Ip6Prefix("fc00::", 32) == Ip6Prefix("fc00:1::", 32));
CHECK_FALSE(Ip6Prefix("2001:db8:0:1::", 64) == Ip6Prefix("2001:db8::", 64));
CHECK_FALSE(Ip6Prefix("2001:db8::1", 128) == Ip6Prefix("2001:db8::", 128));

// different prefix object, different length
CHECK_FALSE(Ip6Prefix("::", 0) == Ip6Prefix("2001::", 7));
CHECK_FALSE(Ip6Prefix("fc00::", 7) == Ip6Prefix("fd00::", 8));
CHECK_FALSE(Ip6Prefix("2001:db8:0:1::", 63) == Ip6Prefix("2001:db8::", 64));
}

// TODO: add more test cases for otbr::Ip6Prefix

//-------------------------------------------------------------
// Test for MacAddress
// TODO: Add MacAddress tests
TEST_GROUP(MacAddress){};

TEST(MacAddress, NULL)
{
TEST_EXIT;
}

0 comments on commit 78fa14b

Please sign in to comment.