diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 659d36453..9fe27ff33 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @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 90f2e646f..74b92e89d 100644 --- a/include/openthread/netdata.h +++ b/include/openthread/netdata.h @@ -230,6 +230,20 @@ struct otJoinerDiscerner; otError otNetDataSteeringDataCheckJoinerWithDiscerner(otInstance * aInstance, 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 e64664515..c28b98727 100644 --- a/src/core/api/netdata_api.cpp +++ b/src/core/api/netdata_api.cpp @@ -60,6 +60,13 @@ exit: return error; } +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix) +{ + return AsCoreType(aInstance).Get().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 84d6bc3c3..0023e9e65 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -185,6 +185,25 @@ public: */ 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 @@ private: 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 0726add89..770c2413b 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -1378,6 +1378,45 @@ exit: 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(/* 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 2fecf38ed..c1f57644d 100644 --- a/src/core/thread/network_data_leader_ftd.hpp +++ b/src/core/thread/network_data_leader_ftd.hpp @@ -173,6 +173,19 @@ public: */ 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 20752ab56..06ff0a942 100644 --- a/src/core/thread/network_data_types.hpp +++ b/src/core/thread/network_data_types.hpp @@ -169,6 +169,7 @@ class OnMeshPrefixConfig : public otBorderRouterConfig, public Equatable { 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 0ecb8882d..572d59f7b 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -137,6 +137,7 @@ extern int #endif // defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) +#include #include #include #include @@ -193,6 +194,13 @@ using namespace ot::Posix::Ip6Utils; 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 @@ void AddRtAttrUint32(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, 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 @@ static void UpdateUnicastLinux(const otIp6AddressInfo &aAddressInfo, bool aIsAdd 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 @@ static void UpdateUnicast(otInstance *aInstance, const otIp6AddressInfo &aAddres 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 @@ exit: } } -#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 @@ static otError AddExternalRoute(const otIp6Prefix &aPrefix) 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 @@ static otError AddExternalRoute(const otIp6Prefix &aPrefix) otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE); inet_pton(AF_INET6, addrBuf, data); AddRtAttr(reinterpret_cast(&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 @@ exit: return error; } -static otError DeleteExternalRoute(const otIp6Prefix &aPrefix) +static otError DeleteRoute(const otIp6Prefix &aPrefix) { constexpr unsigned int kBufSize = 512; struct @@ -631,6 +665,105 @@ exit: 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(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 @@ static void UpdateExternalRoutes(otInstance *aInstance) { 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 @@ static void UpdateExternalRoutes(otInstance *aInstance) sAddedExternalRoutes[i] = sAddedExternalRoutes[sAddedExternalRoutesNum - 1]; --sAddedExternalRoutesNum; --i; + otLogWarnPlat("[netif] Successfully deleted an external route %s in kernel", prefixString); } } @@ -698,15 +833,17 @@ static void UpdateExternalRoutes(otInstance *aInstance) } 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 @@ void platformNetifStateChange(otInstance *aInstance, otChangedFlags aFlags) { 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 bc43bdfb7..62e132468 100644 --- a/src/posix/platform/openthread-posix-config.h +++ b/src/posix/platform/openthread-posix-config.h @@ -132,11 +132,49 @@ * 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 *