[posix] install high priority kernel routes for OMR prefixes (#7636)
diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index 659d364..9fe27ff 100644
--- a/include/openthread/instance.h
+++ b/include/openthread/instance.h
@@ -53,7 +53,7 @@
* @note This number versions both OpenThread platform and user APIs.
*
*/
-#define OPENTHREAD_API_VERSION (205)
+#define OPENTHREAD_API_VERSION (206)
/**
* @addtogroup api-instance
diff --git a/include/openthread/netdata.h b/include/openthread/netdata.h
index 90f2e64..74b92e8 100644
--- a/include/openthread/netdata.h
+++ b/include/openthread/netdata.h
@@ -231,6 +231,20 @@
const struct otJoinerDiscerner *aDiscerner);
/**
+ * This function checks whether a given Prefix can act as a valid OMR prefix and also the Leader's Network Data contains
+ * this prefix.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[in] aPrefix A pointer to the IPv6 prefix.
+ *
+ * @returns Whether @p aPrefix is a valid OMR prefix and Leader's Network Data contains the OMR prefix @p aPrefix.
+ *
+ * @note This API is only available when `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` is used.
+ *
+ */
+bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix);
+
+/**
* @}
*
*/
diff --git a/src/core/api/netdata_api.cpp b/src/core/api/netdata_api.cpp
index e646645..c28b987 100644
--- a/src/core/api/netdata_api.cpp
+++ b/src/core/api/netdata_api.cpp
@@ -60,6 +60,13 @@
return error;
}
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix)
+{
+ return AsCoreType(aInstance).Get<NetworkData::Leader>().ContainsOmrPrefix(AsCoreType(aPrefix));
+}
+#endif
+
otError otNetDataGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig)
{
Error error = kErrorNone;
diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp
index 84d6bc3..0023e9e 100644
--- a/src/core/border_router/routing_manager.hpp
+++ b/src/core/border_router/routing_manager.hpp
@@ -185,6 +185,25 @@
*/
Error HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning);
+ /**
+ * This method checks if the on-mesh prefix configuration is a valid OMR prefix.
+ *
+ * @param[in] aOnMeshPrefixConfig The on-mesh prefix configuration to check.
+ *
+ * @returns Whether the on-mesh prefix configuration is a valid OMR prefix.
+ *
+ */
+ static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig);
+
+ /**
+ * This method checks if the OMR prefix is valid (i.e. GUA/ULA prefix with length being 64).
+ *
+ * @param[in] aOmrPrefix The OMR prefix to check.
+ * @returns Whether the OMR prefix is valid.
+ *
+ */
+ static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
+
private:
typedef NetworkData::RoutePreference RoutePreference;
@@ -347,8 +366,6 @@
void ResetDiscoveredPrefixStaleTimer(void);
static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix);
- static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig);
- static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
static bool IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio);
static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp
index 0726add..770c241 100644
--- a/src/core/thread/network_data_leader_ftd.cpp
+++ b/src/core/thread/network_data_leader_ftd.cpp
@@ -1378,6 +1378,45 @@
return error;
}
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+bool Leader::ContainsOmrPrefix(const Ip6::Prefix &aPrefix)
+{
+ PrefixTlv *prefixTlv;
+ bool contains = false;
+
+ VerifyOrExit(BorderRouter::RoutingManager::IsValidOmrPrefix(aPrefix));
+
+ prefixTlv = FindPrefix(aPrefix);
+ VerifyOrExit(prefixTlv != nullptr);
+
+ for (int i = 0; i < 2; i++)
+ {
+ const BorderRouterTlv *borderRouter = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ (i == 0));
+
+ if (borderRouter == nullptr)
+ {
+ continue;
+ }
+
+ for (const BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
+ entry = entry->GetNext())
+ {
+ OnMeshPrefixConfig config;
+
+ config.SetFrom(*prefixTlv, *borderRouter, *entry);
+
+ if (BorderRouter::RoutingManager::IsValidOmrPrefix(config))
+ {
+ ExitNow(contains = true);
+ }
+ }
+ }
+
+exit:
+ return contains;
+}
+#endif
+
} // namespace NetworkData
} // namespace ot
diff --git a/src/core/thread/network_data_leader_ftd.hpp b/src/core/thread/network_data_leader_ftd.hpp
index 2fecf38..c1f5764 100644
--- a/src/core/thread/network_data_leader_ftd.hpp
+++ b/src/core/thread/network_data_leader_ftd.hpp
@@ -173,6 +173,19 @@
*/
Error RemoveStaleChildEntries(Coap::ResponseHandler aHandler, void *aContext);
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+ /**
+ * This method indicates whether a given Prefix can act as a valid OMR prefix and exists in the network data.
+ *
+ * @param[in] aPrefix The OMR prefix to check.
+ *
+ * @retval TRUE If @p aPrefix is a valid OMR prefix and Network Data contains @p aPrefix.
+ * @retval FALSE Otherwise.
+ *
+ */
+ bool ContainsOmrPrefix(const Ip6::Prefix &aPrefix);
+#endif
+
private:
class ChangedFlags
{
diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp
index 20752ab..06ff0a9 100644
--- a/src/core/thread/network_data_types.hpp
+++ b/src/core/thread/network_data_types.hpp
@@ -169,6 +169,7 @@
public Equatable<OnMeshPrefixConfig>
{
friend class NetworkData;
+ friend class Leader;
friend class Local;
friend class Publisher;
diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp
index 0ecb888..572d59f 100644
--- a/src/posix/platform/netif.cpp
+++ b/src/posix/platform/netif.cpp
@@ -137,6 +137,7 @@
#endif // defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
+#include <openthread/border_router.h>
#include <openthread/icmp6.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
@@ -193,6 +194,13 @@
static uint32_t sNetlinkSequence = 0; ///< Netlink message sequence.
#endif
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+static constexpr uint32_t kOmrRoutesPriority = OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY;
+static constexpr uint8_t kMaxOmrRoutesNum = OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM;
+static uint8_t sAddedOmrRoutesNum = 0;
+static otIp6Prefix sAddedOmrRoutes[kMaxOmrRoutesNum];
+#endif
+
#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
static constexpr uint32_t kExternalRoutePriority = OPENTHREAD_POSIX_CONFIG_EXTERNAL_ROUTE_PRIORITY;
static constexpr uint8_t kMaxExternalRoutesNum = OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM;
@@ -345,8 +353,19 @@
AddRtAttr(aHeader, aMaxLen, aType, &aData, sizeof(aData));
}
-static void UpdateUnicastLinux(const otIp6AddressInfo &aAddressInfo, bool aIsAdded)
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
+static bool IsOmrAddress(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo)
{
+ otIp6Prefix addressPrefix{*aAddressInfo.mAddress, aAddressInfo.mPrefixLength};
+
+ return otNetDataContainsOmrPrefix(aInstance, &addressPrefix);
+}
+#endif
+
+static void UpdateUnicastLinux(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo, bool aIsAdded)
+{
+ OT_UNUSED_VARIABLE(aInstance);
+
struct
{
struct nlmsghdr nh;
@@ -380,12 +399,26 @@
AddRtAttr(&req.nh, sizeof(req), IFA_CACHEINFO, &cacheinfo, sizeof(cacheinfo));
}
-#if OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC > 0
- if (aAddressInfo.mScope > ot::Ip6::Address::kLinkLocalScope)
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
+ if (IsOmrAddress(aInstance, aAddressInfo))
{
- AddRtAttrUint32(&req.nh, sizeof(req), IFA_RT_PRIORITY, OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC);
+ // Remove prefix route for OMR address if `OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE` is enabled to
+ // avoid having two routes.
+ if (aIsAdded)
+ {
+ AddRtAttrUint32(&req.nh, sizeof(req), IFA_FLAGS, IFA_F_NOPREFIXROUTE);
+ }
}
+ else
#endif
+ {
+#if OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC > 0
+ if (aAddressInfo.mScope > ot::Ip6::Address::kLinkLocalScope)
+ {
+ AddRtAttrUint32(&req.nh, sizeof(req), IFA_RT_PRIORITY, OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC);
+ }
+#endif
+ }
if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1)
{
@@ -410,7 +443,7 @@
assert(sIpFd >= 0);
#if defined(__linux__)
- UpdateUnicastLinux(aAddressInfo, aIsAdded);
+ UpdateUnicastLinux(aInstance, aAddressInfo, aIsAdded);
#elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
{
int rval;
@@ -531,8 +564,10 @@
}
}
-#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
-static otError AddExternalRoute(const otIp6Prefix &aPrefix)
+#if __linux__ && \
+ (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE)
+
+static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority)
{
constexpr unsigned int kBufSize = 128;
struct
@@ -548,7 +583,6 @@
VerifyOrExit(netifIdx > 0, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(sNetlinkFd >= 0, error = OT_ERROR_INVALID_STATE);
- VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum, error = OT_ERROR_NO_BUFS);
req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
@@ -570,7 +604,7 @@
otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE);
inet_pton(AF_INET6, addrBuf, data);
AddRtAttr(reinterpret_cast<nlmsghdr *>(&req), sizeof(req), RTA_DST, data, sizeof(data));
- AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, kExternalRoutePriority);
+ AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, aPriority);
AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx);
if (send(sNetlinkFd, &req, sizeof(req), 0) < 0)
@@ -582,7 +616,7 @@
return error;
}
-static otError DeleteExternalRoute(const otIp6Prefix &aPrefix)
+static otError DeleteRoute(const otIp6Prefix &aPrefix)
{
constexpr unsigned int kBufSize = 512;
struct
@@ -631,6 +665,105 @@
return error;
}
+#endif // __linux__ && (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE ||
+ // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE)
+
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+
+static bool HasAddedOmrRoute(const otIp6Prefix &aOmrPrefix)
+{
+ bool found = false;
+
+ for (uint8_t i = 0; i < sAddedOmrRoutesNum; ++i)
+ {
+ if (otIp6ArePrefixesEqual(&sAddedOmrRoutes[i], &aOmrPrefix))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static otError AddOmrRoute(const otIp6Prefix &aPrefix)
+{
+ otError error;
+
+ VerifyOrExit(sAddedOmrRoutesNum < kMaxOmrRoutesNum, error = OT_ERROR_NO_BUFS);
+
+ error = AddRoute(aPrefix, kOmrRoutesPriority);
+exit:
+ return error;
+}
+
+static void UpdateOmrRoutes(otInstance *aInstance)
+{
+ otError error;
+ otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+ otBorderRouterConfig config;
+ char prefixString[OT_IP6_PREFIX_STRING_SIZE];
+
+ // Remove kernel routes if the OMR prefix is removed
+ for (int i = 0; i < static_cast<int>(sAddedOmrRoutesNum); ++i)
+ {
+ if (otNetDataContainsOmrPrefix(aInstance, &sAddedOmrRoutes[i]))
+ {
+ continue;
+ }
+
+ otIp6PrefixToString(&sAddedOmrRoutes[i], prefixString, sizeof(prefixString));
+ if ((error = DeleteRoute(sAddedOmrRoutes[i])) != OT_ERROR_NONE)
+ {
+ otLogWarnPlat("[netif] Failed to delete an OMR route %s in kernel: %s", prefixString,
+ otThreadErrorToString(error));
+ }
+ else
+ {
+ sAddedOmrRoutes[i] = sAddedOmrRoutes[sAddedOmrRoutesNum - 1];
+ --sAddedOmrRoutesNum;
+ --i;
+ otLogInfoPlat("[netif] Successfully deleted an OMR route %s in kernel", prefixString);
+ }
+ }
+
+ // Add kernel routes for OMR prefixes in Network Data
+ while (otNetDataGetNextOnMeshPrefix(aInstance, &iterator, &config) == OT_ERROR_NONE)
+ {
+ if (HasAddedOmrRoute(config.mPrefix))
+ {
+ continue;
+ }
+
+ otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
+ if ((error = AddOmrRoute(config.mPrefix)) != OT_ERROR_NONE)
+ {
+ otLogWarnPlat("[netif] Failed to add an OMR route %s in kernel: %s", prefixString,
+ otThreadErrorToString(error));
+ }
+ else
+ {
+ sAddedOmrRoutes[sAddedOmrRoutesNum++] = config.mPrefix;
+ otLogInfoPlat("[netif] Successfully added an OMR route %s in kernel: %s", prefixString);
+ }
+ }
+}
+
+#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
+
+static otError AddExternalRoute(const otIp6Prefix &aPrefix)
+{
+ otError error;
+
+ VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum, error = OT_ERROR_NO_BUFS);
+
+ error = AddRoute(aPrefix, kExternalRoutePriority);
+exit:
+ return error;
+}
+
bool HasExternalRouteInNetData(otInstance *aInstance, const otIp6Prefix &aExternalRoute)
{
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
@@ -676,9 +809,10 @@
{
continue;
}
- if ((error = DeleteExternalRoute(sAddedExternalRoutes[i])) != OT_ERROR_NONE)
+
+ otIp6PrefixToString(&sAddedExternalRoutes[i], prefixString, sizeof(prefixString));
+ if ((error = DeleteRoute(sAddedExternalRoutes[i])) != OT_ERROR_NONE)
{
- otIp6PrefixToString(&sAddedExternalRoutes[i], prefixString, sizeof(prefixString));
otLogWarnPlat("[netif] Failed to delete an external route %s in kernel: %s", prefixString,
otThreadErrorToString(error));
}
@@ -687,6 +821,7 @@
sAddedExternalRoutes[i] = sAddedExternalRoutes[sAddedExternalRoutesNum - 1];
--sAddedExternalRoutesNum;
--i;
+ otLogWarnPlat("[netif] Successfully deleted an external route %s in kernel", prefixString);
}
}
@@ -698,15 +833,17 @@
}
VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum,
otLogWarnPlat("[netif] No buffer to add more external routes in kernel"));
+
+ otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
if ((error = AddExternalRoute(config.mPrefix)) != OT_ERROR_NONE)
{
- otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
otLogWarnPlat("[netif] Failed to add an external route %s in kernel: %s", prefixString,
otThreadErrorToString(error));
}
else
{
sAddedExternalRoutes[sAddedExternalRoutesNum++] = config.mPrefix;
+ otLogWarnPlat("[netif] Successfully added an external route %s in kernel: %s", prefixString);
}
}
exit:
@@ -732,18 +869,18 @@
{
UpdateLink(aInstance);
}
-#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
if (OT_CHANGED_THREAD_NETDATA & aFlags)
{
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+ UpdateOmrRoutes(aInstance);
+#endif
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
UpdateExternalRoutes(aInstance);
- }
#endif
#if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE
- if (OT_CHANGED_THREAD_NETDATA & aFlags)
- {
ot::Posix::UpdateIpSets(aInstance);
- }
#endif
+ }
}
static void processReceive(otMessage *aMessage, void *aContext)
diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h
index bc43bdf..62e1324 100644
--- a/src/posix/platform/openthread-posix-config.h
+++ b/src/posix/platform/openthread-posix-config.h
@@ -132,12 +132,50 @@
* This setting configures the prefix route metric on the Thread network interface.
* Define as 0 to use use the default prefix route metric.
*
+ * Note: The feature works on Linux kernel v4.18+.
+ *
*/
#ifndef OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC
#define OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC 0
#endif
/**
+ * @def OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
+ *
+ * Define as 1 to add OMR routes to POSIX kernel when OMR prefixes are changed in netdata.
+ *
+ * Note: This feature can be used to add OMR routes with non-default priority. Unlike
+ * `OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC`, it works on Linux kernels before v4.18.
+ * However, `OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC` should be preferred on Linux kernel v4.18+.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
+#define OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE 0
+#endif
+
+/**
+ * @def OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY
+ *
+ * This macro defines the priority of OMR routes added to kernel. The larger the number, the lower the priority. We
+ * need to assign a high priority to such routes so that kernel prefers the Thread link rather than infrastructure.
+ * Otherwise we may unnecessarily transmit packets via infrastructure, which potentially causes looping issue.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY
+#define OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY 1
+#endif
+
+/**
+ * @def OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM
+ *
+ * This macro defines the max number of OMR routes that can be added to kernel.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM
+#define OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES
+#endif
+
+/**
* @def OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
*
* Define as 1 to add external routes to POSIX kernel when external routes are changed in netdata.