Skip to content

Commit

Permalink
[posix] add external routes to kernel on POSIX (openthread#6782)
Browse files Browse the repository at this point in the history
This is helpful when POSIX kernel is handling a packet at the thread
TUN device.
  • Loading branch information
superwhd authored Jul 8, 2021
1 parent 4484042 commit 0f680af
Show file tree
Hide file tree
Showing 6 changed files with 442 additions and 1 deletion.
2 changes: 1 addition & 1 deletion include/openthread/instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (134)
#define OPENTHREAD_API_VERSION (135)

/**
* @addtogroup api-instance
Expand Down
12 changes: 12 additions & 0 deletions include/openthread/ip6.h
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,18 @@ const uint16_t *otIp6GetUnsecurePorts(otInstance *aInstance, uint8_t *aNumEntrie
*/
bool otIp6IsAddressEqual(const otIp6Address *aFirst, const otIp6Address *aSecond);

/**
* Test if two IPv6 prefixes are the same.
*
* @param[in] aFirst A pointer to the first IPv6 prefix to compare.
* @param[in] aSecond A pointer to the second IPv6 prefix to compare.
*
* @retval TRUE The two IPv6 prefixes are the same.
* @retval FALSE The two IPv6 prefixes are not the same.
*
*/
bool otIp6ArePrefixesEqual(const otIp6Prefix *aFirst, const otIp6Prefix *aSecond);

/**
* This function converts a human-readable IPv6 address string into a binary representation.
*
Expand Down
5 changes: 5 additions & 0 deletions src/core/api/ip6_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ bool otIp6IsAddressEqual(const otIp6Address *aFirst, const otIp6Address *aSecond
return *static_cast<const Ip6::Address *>(aFirst) == *static_cast<const Ip6::Address *>(aSecond);
}

bool otIp6ArePrefixesEqual(const otIp6Prefix *aFirst, const otIp6Prefix *aSecond)
{
return *static_cast<const Ip6::Prefix *>(aFirst) == *static_cast<const Ip6::Prefix *>(aSecond);
}

otError otIp6AddressFromString(const char *aString, otIp6Address *aAddress)
{
return static_cast<Ip6::Address *>(aAddress)->FromString(aString);
Expand Down
224 changes: 224 additions & 0 deletions src/posix/platform/netif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ extern int
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include <openthread/message.h>
#include <openthread/netdata.h>
#include <openthread/platform/misc.h>

#include "common/code_utils.hpp"
Expand Down Expand Up @@ -189,6 +190,17 @@ using namespace ot::Posix::Ip6Utils;
static uint32_t sNetlinkSequence = 0; ///< Netlink message sequence.
#endif

#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
#if defined(__linux__)
static constexpr uint32_t kExternalRoutePriority = OPENTHREAD_POSIX_CONFIG_EXTERNAL_ROUTE_PRIORITY;
static constexpr uint8_t kMaxExternalRoutesNum = OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM;
static uint8_t sAddedExternalRoutesNum = 0;
static otIp6Prefix sAddedExternalRoutes[kMaxExternalRoutesNum];
#else
#error "OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE only works on Linux platform"
#endif // defined(__linux__)
#endif

#if defined(RTM_NEWMADDR) || defined(__NetBSD__)
// on some BSDs (mac OS, FreeBSD), we get RTM_NEWMADDR/RTM_DELMADDR messages, so we don't need to monitor using MLD
// on NetBSD, MLD monitoring simply doesn't work
Expand Down Expand Up @@ -505,6 +517,212 @@ static void UpdateLink(otInstance *aInstance)
}
}

#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
void AddRtAttr(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, const void *aData, uint8_t aLen)
{
uint8_t len = RTA_LENGTH(aLen);
struct rtattr *rta;

assert(NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len) <= aMaxLen);
OT_UNUSED_VARIABLE(aMaxLen);

rta = (struct rtattr *)((char *)(aHeader) + NLMSG_ALIGN((aHeader)->nlmsg_len));
rta->rta_type = aType;
rta->rta_len = len;
if (aLen)
{
memcpy(RTA_DATA(rta), aData, aLen);
}
aHeader->nlmsg_len = NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len);
}

void AddRtAttrUint32(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, uint32_t aData)
{
AddRtAttr(aHeader, aMaxLen, aType, &aData, sizeof(aData));
}

static otError AddExternalRoute(const otIp6Prefix &aPrefix)
{
constexpr unsigned int kBufSize = 128;
struct
{
struct nlmsghdr header;
struct rtmsg msg;
char buf[kBufSize];
} req{};
unsigned char data[sizeof(in6_addr)];
char addrBuf[OT_IP6_ADDRESS_STRING_SIZE];
unsigned int netifIdx = otSysGetThreadNetifIndex();
otError error = OT_ERROR_NONE;

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;

req.header.nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg));
req.header.nlmsg_type = RTM_NEWROUTE;
req.header.nlmsg_pid = 0;
req.header.nlmsg_seq = ++sNetlinkSequence;

req.msg.rtm_family = AF_INET6;
req.msg.rtm_src_len = 0;
req.msg.rtm_dst_len = aPrefix.mLength;
req.msg.rtm_tos = 0;
req.msg.rtm_scope = RT_SCOPE_UNIVERSE;
req.msg.rtm_type = RTN_UNICAST;
req.msg.rtm_table = RT_TABLE_MAIN;
req.msg.rtm_protocol = RTPROT_BOOT;
req.msg.rtm_flags = 0;

otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE);
inet_pton(AF_INET6, addrBuf, data);
AddRtAttr(&req.header, sizeof(req), RTA_DST, data, sizeof(data));
AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, kExternalRoutePriority);
AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx);

if (send(sNetlinkFd, &req, sizeof(req), 0) < 0)
{
VerifyOrExit(errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK, error = OT_ERROR_BUSY);
DieNow(OT_EXIT_ERROR_ERRNO);
}
exit:
return error;
}

static otError DeleteExternalRoute(const otIp6Prefix &aPrefix)
{
constexpr unsigned int kBufSize = 512;
struct
{
struct nlmsghdr header;
struct rtmsg msg;
char buf[kBufSize];
} req{};
unsigned char data[sizeof(in6_addr)];
char addrBuf[OT_IP6_ADDRESS_STRING_SIZE];
unsigned int netifIdx = otSysGetThreadNetifIndex();
otError error = OT_ERROR_NONE;

VerifyOrExit(netifIdx > 0, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(sNetlinkFd >= 0, error = OT_ERROR_INVALID_STATE);

req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_NONREC;

req.header.nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg));
req.header.nlmsg_type = RTM_DELROUTE;
req.header.nlmsg_pid = 0;
req.header.nlmsg_seq = ++sNetlinkSequence;

req.msg.rtm_family = AF_INET6;
req.msg.rtm_src_len = 0;
req.msg.rtm_dst_len = aPrefix.mLength;
req.msg.rtm_tos = 0;
req.msg.rtm_scope = RT_SCOPE_UNIVERSE;
req.msg.rtm_type = RTN_UNICAST;
req.msg.rtm_table = RT_TABLE_MAIN;
req.msg.rtm_protocol = RTPROT_BOOT;
req.msg.rtm_flags = 0;

otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE);
inet_pton(AF_INET6, addrBuf, data);
AddRtAttr(&req.header, sizeof(req), RTA_DST, data, sizeof(data));
AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx);

if (send(sNetlinkFd, &req, sizeof(req), 0) < 0)
{
VerifyOrExit(errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK, error = OT_ERROR_BUSY);
DieNow(OT_EXIT_ERROR_ERRNO);
}

exit:
return error;
}

bool HasExternalRouteInNetData(otInstance *aInstance, const otIp6Prefix &aExternalRoute)
{
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
otExternalRouteConfig config;
bool found = false;

while (otNetDataGetNextRoute(aInstance, &iterator, &config) == OT_ERROR_NONE)
{
if (otIp6ArePrefixesEqual(&config.mPrefix, &aExternalRoute))
{
found = true;
break;
}
}
return found;
}

bool HasAddedExternalRoute(const otIp6Prefix &aExternalRoute)
{
bool found = false;

for (uint8_t i = 0; i < sAddedExternalRoutesNum; ++i)
{
if (otIp6ArePrefixesEqual(&sAddedExternalRoutes[i], &aExternalRoute))
{
found = true;
break;
}
}
return found;
}

static void UpdateExternalRoutes(otInstance *aInstance)
{
otError error;
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
otExternalRouteConfig config;
char prefixString[OT_IP6_PREFIX_STRING_SIZE];

for (int i = 0; i < static_cast<int>(sAddedExternalRoutesNum); ++i)
{
if (HasExternalRouteInNetData(aInstance, sAddedExternalRoutes[i]))
{
continue;
}
if ((error = DeleteExternalRoute(sAddedExternalRoutes[i])) != OT_ERROR_NONE)
{
otIp6PrefixToString(&sAddedExternalRoutes[i], prefixString, sizeof(prefixString));
otLogWarnPlat("failed to delete an external route %s in kernel: %s", prefixString,
otThreadErrorToString(error));
}
else
{
sAddedExternalRoutes[i] = sAddedExternalRoutes[sAddedExternalRoutesNum - 1];
--sAddedExternalRoutesNum;
--i;
}
}

while (otNetDataGetNextRoute(aInstance, &iterator, &config) == OT_ERROR_NONE)
{
if (config.mRloc16 == otThreadGetRloc16(aInstance) || HasAddedExternalRoute(config.mPrefix))
{
continue;
}
VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum,
otLogWarnPlat("no buffer to add more external routes in kernel"));
if ((error = AddExternalRoute(config.mPrefix)) != OT_ERROR_NONE)
{
otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
otLogWarnPlat("failed to add an external route %s in kernel: %s", prefixString,
otThreadErrorToString(error));
}
else
{
sAddedExternalRoutes[sAddedExternalRoutesNum++] = config.mPrefix;
}
}
exit:
return;
}
#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE

static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsAdded, void *aContext)
{
if (aAddressInfo->mAddress->mFields.m8[0] == 0xff)
Expand All @@ -523,6 +741,12 @@ void platformNetifStateChange(otInstance *aInstance, otChangedFlags aFlags)
{
UpdateLink(aInstance);
}
#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
if (OT_CHANGED_THREAD_NETDATA & aFlags)
{
UpdateExternalRoutes(aInstance);
}
#endif
}

static void processReceive(otMessage *aMessage, void *aContext)
Expand Down
32 changes: 32 additions & 0 deletions src/posix/platform/openthread-posix-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,38 @@
#define OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE 0
#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.
*
*/
#ifdef __linux__
#ifndef OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
#define OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE 1
#endif
#endif

/**
* @def OPENTHREAD_POSIX_CONFIG_EXTERNAL_ROUTE_PRIORITY
*
* This macro defines the priority of external routes added to kernel.
*
*/
#ifndef OPENTHREAD_POSIX_CONFIG_EXTERNAL_ROUTE_PRIORITY
#define OPENTHREAD_POSIX_CONFIG_EXTERNAL_ROUTE_PRIORITY 512
#endif

/**
* @def OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM
*
* This macro defines the max number of external routes that can be added to kernel.
*
*/
#ifndef OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM
#define OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM 8
#endif

#ifdef __APPLE__

/**
Expand Down
Loading

0 comments on commit 0f680af

Please sign in to comment.