[dbus] enhance external route support (#399)
* Add ExternalRoute structure
* Add ExternalRouteTable property
* Enhance tests for the external route.
diff --git a/src/dbus/client/thread_api_dbus.cpp b/src/dbus/client/thread_api_dbus.cpp
index 208d729..c4e5cb3 100644
--- a/src/dbus/client/thread_api_dbus.cpp
+++ b/src/dbus/client/thread_api_dbus.cpp
@@ -348,9 +348,9 @@
return CallDBusMethodSync(OTBR_DBUS_REMOVE_ON_MESH_PREFIX_METHOD, std::tie(aPrefix));
}
-ClientError ThreadApiDBus::AddExternalRoute(const Ip6Prefix &aPrefix, int8_t aPreference, bool aStable)
+ClientError ThreadApiDBus::AddExternalRoute(const ExternalRoute &aExternalRoute)
{
- return CallDBusMethodSync(OTBR_DBUS_ADD_EXTERNAL_ROUTE_METHOD, std::tie(aPrefix, aPreference, aStable));
+ return CallDBusMethodSync(OTBR_DBUS_ADD_EXTERNAL_ROUTE_METHOD, std::tie(aExternalRoute));
}
ClientError ThreadApiDBus::RemoveExternalRoute(const Ip6Prefix &aPrefix)
@@ -504,6 +504,11 @@
return GetProperty(OTBR_DBUS_PROPERTY_RADIO_TX_POWER, aTxPower);
}
+ClientError ThreadApiDBus::GetExternalRoutes(std::vector<ExternalRoute> &aExternalRoutes)
+{
+ return GetProperty(OTBR_DBUS_PROPERTY_EXTERNAL_ROUTES, aExternalRoutes);
+}
+
std::string ThreadApiDBus::GetInterfaceName(void)
{
return mInterfaceName;
diff --git a/src/dbus/client/thread_api_dbus.hpp b/src/dbus/client/thread_api_dbus.hpp
index 4c92fbd..b38bf52 100644
--- a/src/dbus/client/thread_api_dbus.hpp
+++ b/src/dbus/client/thread_api_dbus.hpp
@@ -210,16 +210,14 @@
/**
* This method adds an external route.
*
- * @param[in] aPrefix The route prefix.
- * @param[in] aPreference The route preference.
- * @param[in] aStable Whether or not the route is stable.
+ * @param[in] aExternalroute The external route config
*
* @retval ERROR_NONE successfully performed the dbus function call
* @retval ERROR_DBUS dbus encode/decode error
* @retval ... OpenThread defined error value otherwise
*
*/
- ClientError AddExternalRoute(const Ip6Prefix &aPrefix, int8_t aPerference, bool aStable);
+ ClientError AddExternalRoute(const ExternalRoute &aExternalRoute);
/**
* This method removes an external route.
@@ -571,6 +569,18 @@
ClientError GetRadioTxPower(int8_t &aTxPower);
/**
+ * This method gets the external route table
+ *
+ * @param[out] aExternalRoutes The external route table
+ *
+ * @retval ERROR_NONE successfully performed the dbus function call
+ * @retval ERROR_DBUS dbus encode/decode error
+ * @retval ... OpenThread defined error value otherwise
+ *
+ */
+ ClientError GetExternalRoutes(std::vector<ExternalRoute> &aExternalRoutes);
+
+ /**
* This method returns the network interface name the client is bound to.
*
* @returns The network interface name.
diff --git a/src/dbus/common/constants.hpp b/src/dbus/common/constants.hpp
index 1258c75..13e6c91 100644
--- a/src/dbus/common/constants.hpp
+++ b/src/dbus/common/constants.hpp
@@ -77,6 +77,7 @@
#define OTBR_DBUS_PROPERTY_PARTITION_ID_PROEPRTY "PartitionID"
#define OTBR_DBUS_PROPERTY_INSTANT_RSSI "InstantRssi"
#define OTBR_DBUS_PROPERTY_RADIO_TX_POWER "RadioTxPower"
+#define OTBR_DBUS_PROPERTY_EXTERNAL_ROUTES "ExternalRoutes"
#define OTBR_ROLE_NAME_DISABLED "disabled"
#define OTBR_ROLE_NAME_DETACHED "detached"
diff --git a/src/dbus/common/dbus_message_helper.hpp b/src/dbus/common/dbus_message_helper.hpp
index 7ffde1e..4f2a42f 100644
--- a/src/dbus/common/dbus_message_helper.hpp
+++ b/src/dbus/common/dbus_message_helper.hpp
@@ -52,6 +52,8 @@
otbrError DBusMessageExtract(DBusMessageIter *aIter, LinkModeConfig &aConfig);
otbrError DBusMessageEncode(DBusMessageIter *aIter, const Ip6Prefix &aPrefix);
otbrError DBusMessageExtract(DBusMessageIter *aIter, Ip6Prefix &aPrefix);
+otbrError DBusMessageEncode(DBusMessageIter *aIter, const ExternalRoute &aRoute);
+otbrError DBusMessageExtract(DBusMessageIter *aIter, ExternalRoute &aRoute);
otbrError DBusMessageEncode(DBusMessageIter *aIter, const OnMeshPrefix &aPrefix);
otbrError DBusMessageExtract(DBusMessageIter *aIter, OnMeshPrefix &aPrefix);
otbrError DBusMessageEncode(DBusMessageIter *aIter, const MacCounters &aCounters);
@@ -101,10 +103,22 @@
template <> struct DBusTypeTrait<Ip6Prefix>
{
- // struct of { arrray of bytes, byte}
+ // struct of {array of bytes, byte}
static constexpr const char *TYPE_AS_STRING = "(ayy)";
};
+template <> struct DBusTypeTrait<ExternalRoute>
+{
+ // struct of {{array of bytes, byte}, uint16, byte, bool, bool}
+ static constexpr const char *TYPE_AS_STRING = "((ayy)qybb)";
+};
+
+template <> struct DBusTypeTrait<std::vector<ExternalRoute>>
+{
+ // array of {{array of bytes, byte}, uint16, byte, bool, bool}
+ static constexpr const char *TYPE_AS_STRING = "a((ayy)qybb)";
+};
+
template <> struct DBusTypeTrait<LeaderData>
{
// struct of { uint32, byte, byte, byte, byte }
diff --git a/src/dbus/common/dbus_message_helper_openthread.cpp b/src/dbus/common/dbus_message_helper_openthread.cpp
index 8cae00f..c02830b 100644
--- a/src/dbus/common/dbus_message_helper_openthread.cpp
+++ b/src/dbus/common/dbus_message_helper_openthread.cpp
@@ -146,12 +146,11 @@
VerifyOrExit(dbus_message_iter_open_container(aIter, DBUS_TYPE_STRUCT, nullptr, &sub), error = OTBR_ERROR_DBUS);
VerifyOrExit(aPrefix.mPrefix.size() <= OTBR_IP6_PREFIX_SIZE, error = OTBR_ERROR_DBUS);
- SuccessOrExit(DBusMessageEncode(&sub, aPrefix.mPrefix));
- SuccessOrExit(DBusMessageEncode(&sub, aPrefix.mLength));
+ SuccessOrExit(error = DBusMessageEncode(&sub, aPrefix.mPrefix));
+ SuccessOrExit(error = DBusMessageEncode(&sub, aPrefix.mLength));
- VerifyOrExit(dbus_message_iter_close_container(aIter, &sub));
+ VerifyOrExit(dbus_message_iter_close_container(aIter, &sub), error = OTBR_ERROR_DBUS);
- error = OTBR_ERROR_NONE;
exit:
return error;
}
@@ -167,6 +166,44 @@
SuccessOrExit(error = DBusMessageExtract(&sub, aPrefix.mLength));
dbus_message_iter_next(aIter);
+
+exit:
+ return error;
+}
+
+otbrError DBusMessageEncode(DBusMessageIter *aIter, const ExternalRoute &aRoute)
+{
+ DBusMessageIter sub;
+ otbrError error = OTBR_ERROR_NONE;
+
+ VerifyOrExit(dbus_message_iter_open_container(aIter, DBUS_TYPE_STRUCT, nullptr, &sub), error = OTBR_ERROR_DBUS);
+
+ SuccessOrExit(error = DBusMessageEncode(&sub, aRoute.mPrefix));
+ SuccessOrExit(error = DBusMessageEncode(&sub, aRoute.mRloc16));
+ SuccessOrExit(error = DBusMessageEncode(&sub, aRoute.mPreference));
+ SuccessOrExit(error = DBusMessageEncode(&sub, aRoute.mStable));
+ SuccessOrExit(error = DBusMessageEncode(&sub, aRoute.mNextHopIsThisDevice));
+
+ VerifyOrExit(dbus_message_iter_close_container(aIter, &sub), error = OTBR_ERROR_DBUS);
+
+exit:
+ return error;
+}
+
+otbrError DBusMessageExtract(DBusMessageIter *aIter, ExternalRoute &aRoute)
+{
+ DBusMessageIter sub;
+ otbrError error = OTBR_ERROR_NONE;
+
+ dbus_message_iter_recurse(aIter, &sub);
+ SuccessOrExit(error = DBusMessageExtract(&sub, aRoute.mPrefix));
+ SuccessOrExit(error = DBusMessageExtract(&sub, aRoute.mRloc16));
+ SuccessOrExit(error = DBusMessageExtract(&sub, aRoute.mPreference));
+ SuccessOrExit(error = DBusMessageExtract(&sub, aRoute.mStable));
+ SuccessOrExit(error = DBusMessageExtract(&sub, aRoute.mNextHopIsThisDevice));
+
+ dbus_message_iter_next(aIter);
+
exit:
return error;
}
diff --git a/src/dbus/common/types.hpp b/src/dbus/common/types.hpp
index 5ca31a5..5cebae2 100644
--- a/src/dbus/common/types.hpp
+++ b/src/dbus/common/types.hpp
@@ -127,6 +127,39 @@
bool mStable;
};
+struct ExternalRoute
+{
+ /**
+ * The IPv6 prefix.
+ */
+ Ip6Prefix mPrefix;
+
+ /**
+ * The Rloc associated with the external route entry.
+ *
+ * This value is ignored when adding an external route. For any added route, the device's Rloc is used.
+ */
+ uint16_t mRloc16;
+
+ /**
+ * A 2-bit signed integer indicating router preference as defined in RFC 4191.
+ */
+ int8_t mPreference;
+
+ /**
+ * TRUE, if this configuration is considered Stable Network Data. FALSE, otherwise.
+ */
+ bool mStable;
+
+ /**
+ * TRUE if the external route entry's next hop is this device itself (i.e., the route was added earlier by this
+ * device). FALSE otherwise.
+ *
+ * This value is ignored when adding an external route. For any added route the next hop is this device.
+ */
+ bool mNextHopIsThisDevice;
+};
+
/**
* This structure represents the MAC layer counters.
*
diff --git a/src/dbus/server/dbus_thread_object.cpp b/src/dbus/server/dbus_thread_object.cpp
index 07609b3..a066a15 100644
--- a/src/dbus/server/dbus_thread_object.cpp
+++ b/src/dbus/server/dbus_thread_object.cpp
@@ -183,6 +183,8 @@
std::bind(&DBusThreadObject::GetInstantRssiHandler, this, _1));
RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_RADIO_TX_POWER,
std::bind(&DBusThreadObject::GetRadioTxPowerHandler, this, _1));
+ RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_EXTERNAL_ROUTES,
+ std::bind(&DBusThreadObject::GetExternalRoutesHandler, this, _1));
return error;
}
@@ -373,24 +375,25 @@
void DBusThreadObject::AddExternalRouteHandler(DBusRequest &aRequest)
{
auto threadHelper = mNcp->GetThreadHelper();
- Ip6Prefix routePrefix;
- int8_t preference;
- bool stable;
- auto args = std::tie(routePrefix, preference, stable);
+ ExternalRoute route;
+ auto args = std::tie(route);
otError error = OT_ERROR_NONE;
- otExternalRouteConfig route;
- otIp6Prefix & prefix = route.mPrefix;
+ otExternalRouteConfig otRoute;
+ otIp6Prefix & prefix = otRoute.mPrefix;
VerifyOrExit(DBusMessageToTuple(*aRequest.GetMessage(), args) == OTBR_ERROR_NONE, error = OT_ERROR_INVALID_ARGS);
// size is guaranteed by parsing
- std::copy(routePrefix.mPrefix.begin(), routePrefix.mPrefix.end(), &prefix.mPrefix.mFields.m8[0]);
- prefix.mLength = routePrefix.mLength;
- route.mPreference = preference;
- route.mStable = stable;
+ std::copy(route.mPrefix.mPrefix.begin(), route.mPrefix.mPrefix.end(), &prefix.mPrefix.mFields.m8[0]);
+ prefix.mLength = route.mPrefix.mLength;
+ otRoute.mPreference = route.mPreference;
+ otRoute.mStable = route.mStable;
- SuccessOrExit(error = otBorderRouterAddRoute(threadHelper->GetInstance(), &route));
- SuccessOrExit(error = otBorderRouterRegister(threadHelper->GetInstance()));
+ SuccessOrExit(error = otBorderRouterAddRoute(threadHelper->GetInstance(), &otRoute));
+ if (route.mStable)
+ {
+ SuccessOrExit(error = otBorderRouterRegister(threadHelper->GetInstance()));
+ }
exit:
aRequest.ReplyOtResult(error);
@@ -906,5 +909,37 @@
return error;
}
+otError DBusThreadObject::GetExternalRoutesHandler(DBusMessageIter &aIter)
+{
+ auto threadHelper = mNcp->GetThreadHelper();
+ otError error = OT_ERROR_NONE;
+ otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT;
+ otExternalRouteConfig config;
+ std::vector<ExternalRoute> externalRouteTable;
+
+ while (otNetDataGetNextRoute(threadHelper->GetInstance(), &iter, &config) == OT_ERROR_NONE)
+ {
+ ExternalRoute route;
+
+ route.mPrefix.mPrefix = std::vector<uint8_t>(&config.mPrefix.mPrefix.mFields.m8[0],
+ &config.mPrefix.mPrefix.mFields.m8[OTBR_IP6_PREFIX_SIZE]);
+ route.mPrefix.mLength = config.mPrefix.mLength;
+ route.mRloc16 = config.mRloc16;
+ route.mPreference = config.mPreference;
+ route.mStable = config.mStable;
+ route.mNextHopIsThisDevice = config.mNextHopIsThisDevice;
+ externalRouteTable.push_back(route);
+ }
+
+ printf("Encode size %zu\n", externalRouteTable.size());
+
+ VerifyOrExit(DBusMessageEncodeToVariant(&aIter, externalRouteTable) == OTBR_ERROR_NONE,
+ error = OT_ERROR_INVALID_ARGS);
+
+exit:
+ printf("error %d\n", error);
+ return error;
+}
+
} // namespace DBus
} // namespace otbr
diff --git a/src/dbus/server/dbus_thread_object.hpp b/src/dbus/server/dbus_thread_object.hpp
index d0dd873..1427248 100644
--- a/src/dbus/server/dbus_thread_object.hpp
+++ b/src/dbus/server/dbus_thread_object.hpp
@@ -109,6 +109,7 @@
otError GetPartitionIDHandler(DBusMessageIter &aIter);
otError GetInstantRssiHandler(DBusMessageIter &aIter);
otError GetRadioTxPowerHandler(DBusMessageIter &aIter);
+ otError GetExternalRoutesHandler(DBusMessageIter &aIter);
void ReplyScanResult(DBusRequest &aRequest, otError aError, const std::vector<otActiveScanResult> &aResult);
diff --git a/src/dbus/server/instropect.xml b/src/dbus/server/instropect.xml
index a721e30..d44a089 100644
--- a/src/dbus/server/instropect.xml
+++ b/src/dbus/server/instropect.xml
@@ -57,13 +57,17 @@
<method name="AddExternalRoute">
<!--
struct {
- uint8[] prefix_bytes
- uint8 prefix_length
+ struct {
+ uint8[] prefix_bytes
+ uint8 prefix_length
+ }
+ uint16 rloc
+ uint8 preference
+ bool stable
+ bool next_hop_is_self
}
-->
- <arg name="prefix" type="(ayy)"/>
- <arg name="preference" type="y"/>
- <arg name="stable" type="b"/>
+ <arg name="prefix" type="((ayy)qybb)"/>
</method>
<method name="RemoveExternalRoute">
@@ -317,6 +321,22 @@
<property name="RadioTxPower" type="y" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
+
+ <!--
+ struct {
+ struct {
+ uint8[] prefix_bytes
+ uint8 prefix_length
+ }
+ uint16 rloc
+ uint8 preference
+ bool stable
+ bool next_hop_is_self
+ }
+ -->
+ <property name="ExternalRoutes" type="((ayy)qybb)" access="read">
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
+ </property>
</interface>
<interface name="org.freedesktop.DBus.Properties">
diff --git a/tests/dbus/test_dbus_client.cpp b/tests/dbus/test_dbus_client.cpp
index 0567ffd..9c25467 100644
--- a/tests/dbus/test_dbus_client.cpp
+++ b/tests/dbus/test_dbus_client.cpp
@@ -28,6 +28,7 @@
#include <assert.h>
#include <stdio.h>
+#include <string.h>
#include <memory>
@@ -39,7 +40,10 @@
using otbr::DBus::ActiveScanResult;
using otbr::DBus::ClientError;
using otbr::DBus::DeviceRole;
+using otbr::DBus::ExternalRoute;
+using otbr::DBus::Ip6Prefix;
using otbr::DBus::LinkModeConfig;
+using otbr::DBus::OnMeshPrefix;
using otbr::DBus::ThreadApiDBus;
struct DBusConnectionDeleter
@@ -49,6 +53,33 @@
using UniqueDBusConnection = std::unique_ptr<DBusConnection, DBusConnectionDeleter>;
+static bool operator==(const otbr::DBus::Ip6Prefix &aLhs, const otbr::DBus::Ip6Prefix &aRhs)
+{
+ bool prefixDataEquality = (aLhs.mPrefix.size() == aRhs.mPrefix.size()) &&
+ (memcmp(&aLhs.mPrefix[0], &aRhs.mPrefix[0], aLhs.mPrefix.size()) == 0);
+
+ return prefixDataEquality && aLhs.mLength == aRhs.mLength;
+}
+
+static void CheckExternalRoute(ThreadApiDBus *aApi, const Ip6Prefix &aPrefix)
+{
+ ExternalRoute route;
+ std::vector<ExternalRoute> externalRouteTable;
+
+ route.mPrefix = aPrefix;
+ route.mStable = true;
+ route.mPreference = 0;
+
+ assert(aApi->AddExternalRoute(route) == OTBR_ERROR_NONE);
+ assert(aApi->GetExternalRoutes(externalRouteTable) == OTBR_ERROR_NONE);
+ assert(externalRouteTable.size() == 1);
+ assert(externalRouteTable[0].mPrefix == aPrefix);
+ assert(externalRouteTable[0].mPreference == 0);
+ assert(externalRouteTable[0].mStable);
+ assert(externalRouteTable[0].mNextHopIsThisDevice);
+ assert(aApi->RemoveExternalRoute(aPrefix) == OTBR_ERROR_NONE);
+}
+
int main()
{
DBusError error;
@@ -101,8 +132,8 @@
std::vector<otbr::DBus::ChildInfo> childTable;
std::vector<otbr::DBus::NeighborInfo> neighborTable;
uint32_t partitionId;
- otbr::DBus::Ip6Prefix prefix;
- otbr::DBus::OnMeshPrefix onMeshPrefix;
+ Ip6Prefix prefix;
+ OnMeshPrefix onMeshPrefix;
prefix.mPrefix = {0xfd, 0xcd, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
prefix.mLength = 64;
@@ -125,8 +156,7 @@
assert(api->GetPartitionId(partitionId) == OTBR_ERROR_NONE);
assert(api->GetInstantRssi(rssi) == OTBR_ERROR_NONE);
assert(api->GetRadioTxPower(txPower) == OTBR_ERROR_NONE);
- assert(api->AddExternalRoute(prefix, 0, true) == OTBR_ERROR_NONE);
- assert(api->RemoveExternalRoute(prefix) == OTBR_ERROR_NONE);
+ CheckExternalRoute(api.get(), prefix);
assert(api->AddOnMeshPrefix(onMeshPrefix) == OTBR_ERROR_NONE);
assert(api->RemoveOnMeshPrefix(onMeshPrefix.mPrefix) == OTBR_ERROR_NONE);
api->FactoryReset(nullptr);
diff --git a/tests/unit/test_dbus_message.cpp b/tests/unit/test_dbus_message.cpp
index f3db941..9b9b469 100644
--- a/tests/unit/test_dbus_message.cpp
+++ b/tests/unit/test_dbus_message.cpp
@@ -105,6 +105,20 @@
aLhs.mIsNative == aRhs.mIsNative && aLhs.mIsJoinable == aRhs.mIsJoinable;
}
+bool operator==(const otbr::DBus::Ip6Prefix &aLhs, const otbr::DBus::Ip6Prefix &aRhs)
+{
+ bool prefixDataEquality = (aLhs.mPrefix.size() == aRhs.mPrefix.size()) &&
+ (memcmp(&aLhs.mPrefix[0], &aRhs.mPrefix[0], aLhs.mPrefix.size()) == 0);
+
+ return prefixDataEquality && aLhs.mLength == aRhs.mLength;
+}
+
+bool operator==(const otbr::DBus::ExternalRoute &aLhs, const otbr::DBus::ExternalRoute &aRhs)
+{
+ return aLhs.mPrefix == aRhs.mPrefix && aLhs.mRloc16 == aRhs.mRloc16 && aLhs.mPreference == aRhs.mPreference &&
+ aLhs.mStable == aRhs.mStable && aLhs.mNextHopIsThisDevice == aRhs.mNextHopIsThisDevice;
+}
+
inline otbrError DBusMessageEncode(DBusMessageIter *aIter, const TestStruct &aValue)
{
otbrError error = OTBR_ERROR_DBUS;
@@ -295,3 +309,21 @@
dbus_message_unref(msg);
}
+
+TEST(DBusMessage, TestOtbrExternalRoute)
+{
+ DBusMessage * msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
+ tuple<std::vector<otbr::DBus::ExternalRoute>> setVals(
+ {{otbr::DBus::Ip6Prefix({{0xfa, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, 64}), uint16_t(0xfc00), 1, true,
+ true}});
+ tuple<std::vector<otbr::DBus::ExternalRoute>> getVals;
+
+ CHECK(msg != NULL);
+
+ CHECK(TupleToDBusMessage(*msg, setVals) == OTBR_ERROR_NONE);
+ CHECK(DBusMessageToTuple(*msg, getVals) == OTBR_ERROR_NONE);
+
+ CHECK(std::get<0>(setVals)[0] == std::get<0>(getVals)[0]);
+
+ dbus_message_unref(msg);
+}