mirror of
https://github.com/espressif/openthread.git
synced 2026-08-08 03:37:46 +00:00
[posix] add external routes to kernel on POSIX (#6782)
This is helpful when POSIX kernel is handling a packet at the thread TUN device.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -505,6 +517,212 @@ exit:
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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__
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2021, 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.
|
||||
#
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
import ipaddress
|
||||
import config
|
||||
import thread_cert
|
||||
|
||||
# Test description:
|
||||
# This test verifies that when external routes are changed in Network Data, a
|
||||
# border router will make according updates to routes at the linux kernel.
|
||||
#
|
||||
# Topology:
|
||||
# ----------------(eth)----------------------
|
||||
# | | |
|
||||
# BR1 (Leader) ----- BR2 HOST
|
||||
# | |
|
||||
# ROUTER1 ROUTER2
|
||||
|
||||
BR1 = 1
|
||||
BR2 = 2
|
||||
ROUTER1 = 3
|
||||
ROUTER2 = 4
|
||||
HOST = 5
|
||||
|
||||
ROUTE1 = '2402:1234:1234:1234::/64'
|
||||
|
||||
|
||||
class MultiBorderRouters(thread_cert.TestCase):
|
||||
USE_MESSAGE_FACTORY = False
|
||||
|
||||
TOPOLOGY = {
|
||||
BR1: {
|
||||
'name': 'BR_1',
|
||||
'allowlist': [BR2, ROUTER1],
|
||||
'is_otbr': True,
|
||||
'version': '1.2',
|
||||
},
|
||||
BR2: {
|
||||
'name': 'BR_2',
|
||||
'allowlist': [BR1, ROUTER2],
|
||||
'is_otbr': True,
|
||||
'version': '1.2',
|
||||
},
|
||||
ROUTER1: {
|
||||
'name': 'Router_1',
|
||||
'allowlist': [BR1],
|
||||
'version': '1.2',
|
||||
},
|
||||
ROUTER2: {
|
||||
'name': 'Router_2',
|
||||
'allowlist': [BR2],
|
||||
'version': '1.2',
|
||||
},
|
||||
HOST: {
|
||||
'name': 'Host',
|
||||
'is_host': True,
|
||||
}
|
||||
}
|
||||
|
||||
def test(self):
|
||||
br1 = self.nodes[BR1]
|
||||
br2 = self.nodes[BR2]
|
||||
router1 = self.nodes[ROUTER1]
|
||||
router2 = self.nodes[ROUTER2]
|
||||
host = self.nodes[HOST]
|
||||
|
||||
br1.start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('leader', br1.get_state())
|
||||
|
||||
br2.start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', br2.get_state())
|
||||
|
||||
router1.start()
|
||||
router2.start()
|
||||
host.start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', router1.get_state())
|
||||
self.assertEqual('router', router2.get_state())
|
||||
|
||||
# Manually add ROUTE1 at BR1
|
||||
br1.add_route(ROUTE1)
|
||||
br1.register_netdata()
|
||||
|
||||
# BRs has installed all external routes in their kernels respectively
|
||||
netdata = br1.get_netdata()
|
||||
br1_kernel_routes = self.get_routes_from_kernel(br1)
|
||||
for route in self.get_routes_from_netdata(netdata, br1.get_addr16()):
|
||||
self.assertIn(route, br1_kernel_routes)
|
||||
br2_kernel_routes = self.get_routes_from_kernel(br2)
|
||||
for route in self.get_routes_from_netdata(netdata, br2.get_addr16()):
|
||||
self.assertIn(route, br2_kernel_routes)
|
||||
|
||||
# ROUTE1 has been installed in BR2 but not BR1
|
||||
self.assertNotIn(ipaddress.IPv6Network(ROUTE1), br1_kernel_routes)
|
||||
self.assertIn(ipaddress.IPv6Network(ROUTE1), br2_kernel_routes)
|
||||
|
||||
# Remove ROUTE1
|
||||
br1.remove_route(ROUTE1)
|
||||
br1.register_netdata()
|
||||
|
||||
# Verify that external routes are still in kernel routes
|
||||
netdata = br1.get_netdata()
|
||||
br1_kernel_routes = self.get_routes_from_kernel(br1)
|
||||
for route in self.get_routes_from_netdata(netdata, br1.get_addr16()):
|
||||
self.assertIn(route, br1_kernel_routes)
|
||||
br2_kernel_routes = self.get_routes_from_kernel(br2)
|
||||
for route in self.get_routes_from_netdata(netdata, br2.get_addr16()):
|
||||
self.assertIn(route, br2_kernel_routes)
|
||||
|
||||
# ROUTE1 has been removed from kernel routes
|
||||
self.assertNotIn(ipaddress.IPv6Network(ROUTE1), br1_kernel_routes)
|
||||
self.assertNotIn(ipaddress.IPv6Network(ROUTE1), br2_kernel_routes)
|
||||
|
||||
br2.bash('ip link set eth0 down')
|
||||
self.simulator.go(10)
|
||||
|
||||
# HOST pings BR2's OMR, the ping should succeed
|
||||
self.assertTrue(host.ping_ether(br2.get_ip6_address(address_type=config.ADDRESS_TYPE.OMR)[0]))
|
||||
|
||||
def get_routes_from_netdata(self, netdata, exclude_rloc16):
|
||||
routes = []
|
||||
for entry in netdata['Routes']:
|
||||
items = entry.split()
|
||||
prefix = items[0]
|
||||
rloc16 = int(items[-1], 16)
|
||||
if rloc16 != exclude_rloc16:
|
||||
routes.append(ipaddress.IPv6Network(prefix))
|
||||
return routes
|
||||
|
||||
def get_routes_from_kernel(self, br):
|
||||
routes = []
|
||||
for entry in br.bash('ip -6 -d route list dev wpan0'):
|
||||
routes.append(ipaddress.IPv6Network(entry.split()[1]))
|
||||
return routes
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user