[dua] implement ND Proxy callbacks (#5595)

This commit:
- Add callback for Domain Prefix events
- Add callback for ND Proxy events
- Add corresponding test to verify ND Proxy works
- Some misc enhancements to the Backbone tests
  - Run sudo modprobe ip6table_filter in Docker host so that ip6tables
    works in ubuntu docker instances.
  - Use -DOTBR_REST=OFF -DOTBR_WEB=OFF for building OTBR Docker image
    since these features are not used in tests.
  - Setup radvd on Host to send Router Advertisements for the Domain
    Prefix using configuration: AdvOnLink on; AdvAutonomous off;
    AdvRouterAddr off;
  - Set sysctl net.ipv6.conf.eth0.accept_ra=2 for BBR and Host
This commit is contained in:
Simon Lin
2020-11-02 08:06:45 -08:00
committed by GitHub
parent c0cae3477a
commit 61cf7c9a48
15 changed files with 537 additions and 29 deletions
-1
View File
@@ -251,7 +251,6 @@ jobs:
MULTIPLY: 1
PYTHONUNBUFFERED: 1
VERBOSE: 1
OTBR_COMMIT: "3cf6ff1126dca96803252514691f40d0554046bf" # Date: Mon Oct 26 15:27:04 2020 +0800
steps:
- uses: actions/checkout@v2
- name: Build OTBR Docker
+97
View File
@@ -313,6 +313,103 @@ otError otBackboneRouterMulticastListenerGetNext(otInstance *
otBackboneRouterMulticastListenerIterator *aIterator,
otBackboneRouterMulticastListenerInfo * aListenerInfo);
/**
* Represents the ND Proxy events.
*
*/
typedef enum
{
OT_BACKBONE_ROUTER_NDPROXY_ADDED = 0, ///< ND Proxy was added.
OT_BACKBONE_ROUTER_NDPROXY_REMOVED = 1, ///< ND Proxy was removed.
OT_BACKBONE_ROUTER_NDPROXY_RENEWED = 2, ///< ND Proxy was renewed.
OT_BACKBONE_ROUTER_NDPROXY_CLEARED = 3, ///< All ND Proxies were cleared.
} otBackboneRouterNdProxyEvent;
/**
* This function pointer is called whenever the Nd Proxy changed.
*
* @param[in] aContext The user context pointer.
* @param[in] aEvent The ND Proxy event.
* @param[in] aDua The Domain Unicast Address of the ND Proxy, or `nullptr` if @p aEvent is
* `OT_BACKBONE_ROUTER_NDPROXY_CLEARED`.
*
*/
typedef void (*otBackboneRouterNdProxyCallback)(void * aContext,
otBackboneRouterNdProxyEvent aEvent,
const otIp6Address * aDua);
/**
* This method sets the Backbone Router ND Proxy callback.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aCallback A pointer to the ND Proxy callback.
* @param[in] aContext A user context pointer.
*
*/
void otBackboneRouterSetNdProxyCallback(otInstance * aInstance,
otBackboneRouterNdProxyCallback aCallback,
void * aContext);
/**
* Represents the Backbone Router ND Proxy info.
*
*/
struct otBackboneRouterNdProxyInfo
{
otIp6InterfaceIdentifier *mMeshLocalIid; ///< Mesh-local IID
uint32_t mTimeSinceLastTransaction; ///< Time since last transaction (Seconds)
uint16_t mRloc16; ///< RLOC16
};
/**
* This method gets the Backbone Router ND Proxy info.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aDua The Domain Unicast Address.
* @param[out] aNdProxyInfo A pointer to the ND Proxy info.
*
* @retval OT_ERROR_NONE Successfully got the ND Proxy info.
* @retval OT_ERROR_NOT_FOUND Failed to find the Domain Unicast Address in the ND Proxy table.
*
*/
otError otBackboneRouterGetNdProxyInfo(otInstance * aInstance,
const otIp6Address * aDua,
otBackboneRouterNdProxyInfo *aNdProxyInfo);
/**
* Represents the Domain Prefix events.
*
*/
typedef enum
{
OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED = 0, ///< Domain Prefix was added.
OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED = 1, ///< Domain Prefix was removed.
OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED = 2, ///< Domain Prefix was changed.
} otBackboneRouterDomainPrefixEvent;
/**
* This function pointer is called whenever the Domain Prefix changed.
*
* @param[in] aContext The user context pointer.
* @param[in] aEvent The Domain Prefix event.
* @param[in] aDomainPrefix The new Domain Prefix if added or changed, nullptr otherwise.
*
*/
typedef void (*otBackboneRouterDomainPrefixCallback)(void * aContext,
otBackboneRouterDomainPrefixEvent aEvent,
const otIp6Prefix * aDomainPrefix);
/**
* This method sets the Backbone Router Domain Prefix callback.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aCallback A pointer to the Domain Prefix callback.
* @param[in] aContext A user context pointer.
*
*/
void otBackboneRouterSetDomainPrefixCallback(otInstance * aInstance,
otBackboneRouterDomainPrefixCallback aCallback,
void * aContext);
/**
* @}
*
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (44)
#define OPENTHREAD_API_VERSION (45)
/**
* @addtogroup api-instance
+3 -5
View File
@@ -279,8 +279,8 @@ do_build_otbr_docker()
echo "Building OTBR Docker ..."
local otdir
local otbrdir
local otbr_options="-DOT_DUA=ON -DOT_MLR=ON -DOT_COVERAGE=ON"
local otbr_commit=${OTBR_COMMIT:-master}
local otbr_options="-DOT_DUA=ON -DOT_MLR=ON -DOT_COVERAGE=ON -DOTBR_REST=OFF -DOTBR_WEB=OFF"
local otbr_commit=${OTBR_COMMIT:-8d0b6ccd857441208f56ed29050a9367d8d74a62} # [backbone-router] implement Backbone ND proxy #581
local otbr_docker_image=${OTBR_DOCKER_IMAGE:-otbr-ot12-backbone-ci}
otbrdir=$(mktemp -d -t otbr_XXXXXX)
@@ -289,10 +289,8 @@ do_build_otbr_docker()
(
cd "${otbrdir}"
git init
git remote add origin https://github.com/openthread/ot-br-posix.git
git fetch origin "${otbr_commit}" --depth 1
git fetch https://github.com/openthread/ot-br-posix.git "${otbr_commit}" --depth 1
git reset --hard FETCH_HEAD
git submodule update --init --recursive --depth 1
rm -rf third_party/openthread/repo
cp -r "${otdir}" third_party/openthread/repo
rm -rf .git
+28
View File
@@ -182,4 +182,32 @@ otError otBackboneRouterMulticastListenerGetNext(otInstance *
}
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
void otBackboneRouterSetNdProxyCallback(otInstance * aInstance,
otBackboneRouterNdProxyCallback aCallback,
void * aContext)
{
Instance &instance = *static_cast<Instance *>(aInstance);
instance.Get<BackboneRouter::NdProxyTable>().SetCallback(aCallback, aContext);
}
otError otBackboneRouterGetNdProxyInfo(otInstance * aInstance,
const otIp6Address * aDua,
otBackboneRouterNdProxyInfo *aNdProxyInfo)
{
Instance &instance = *static_cast<Instance *>(aInstance);
return instance.Get<BackboneRouter::NdProxyTable>().GetInfo(reinterpret_cast<const Ip6::Address &>(*aDua),
*aNdProxyInfo);
}
void otBackboneRouterSetDomainPrefixCallback(otInstance * aInstance,
otBackboneRouterDomainPrefixCallback aCallback,
void * aContext)
{
Instance &instance = *static_cast<Instance *>(aInstance);
return instance.Get<BackboneRouter::Local>().SetDomainPrefixCallback(aCallback, aContext);
}
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
+1 -1
View File
@@ -301,7 +301,7 @@ void Leader::UpdateDomainPrefixConfig(void)
LogDomainPrefix(state, mDomainPrefix);
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
Get<Local>().UpdateAllDomainBackboneRouters(state);
Get<Local>().HandleDomainPrefixUpdate(state);
Get<NdProxyTable>().HandleDomainPrefixUpdate(state);
#endif
+30 -1
View File
@@ -55,6 +55,8 @@ Local::Local(Instance &aInstance)
, mSequenceNumber(Random::NonCrypto::GetUint8())
, mRegistrationJitter(Mle::kBackboneRouterRegistrationJitter)
, mIsServiceAdded(false)
, mDomainPrefixCallback(nullptr)
, mDomainPrefixCallbackContext(nullptr)
{
mDomainPrefixConfig.GetPrefix().SetLength(0);
@@ -359,7 +361,7 @@ exit:
return;
}
void Local::UpdateAllDomainBackboneRouters(Leader::DomainPrefixState aState)
void Local::HandleDomainPrefixUpdate(Leader::DomainPrefixState aState)
{
if (!IsEnabled())
{
@@ -377,6 +379,27 @@ void Local::UpdateAllDomainBackboneRouters(Leader::DomainPrefixState aState)
Get<BackboneTmfAgent>().SubscribeMulticast(mAllDomainBackboneRouters);
}
if (mDomainPrefixCallback != nullptr)
{
switch (aState)
{
case Leader::kDomainPrefixAdded:
mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED,
Get<Leader>().GetDomainPrefix());
break;
case Leader::kDomainPrefixRemoved:
mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED,
Get<Leader>().GetDomainPrefix());
break;
case Leader::kDomainPrefixRefreshed:
mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED,
Get<Leader>().GetDomainPrefix());
break;
default:
break;
}
}
exit:
return;
}
@@ -419,6 +442,12 @@ void Local::LogBackboneRouterService(const char *aAction, otError aError)
}
#endif
void Local::SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext)
{
mDomainPrefixCallback = aCallback;
mDomainPrefixCallbackContext = aContext;
}
} // namespace BackboneRouter
} // namespace ot
+15 -4
View File
@@ -227,7 +227,16 @@ public:
* @param[in] aState The Domain Prefix state or state change.
*
*/
void UpdateAllDomainBackboneRouters(Leader::DomainPrefixState aState);
void HandleDomainPrefixUpdate(Leader::DomainPrefixState aState);
/**
* This method sets the Domain Prefix callback.
*
* @param[in] aCallback The callback function.
* @param[in] aContext A user context pointer.
*
*/
void SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext);
private:
void SetState(BackboneRouterState aState);
@@ -255,9 +264,11 @@ private:
NetworkData::OnMeshPrefixConfig mDomainPrefixConfig;
Ip6::NetifUnicastAddress mBackboneRouterPrimaryAloc;
Ip6::Address mAllNetworkBackboneRouters;
Ip6::Address mAllDomainBackboneRouters;
Ip6::NetifUnicastAddress mBackboneRouterPrimaryAloc;
Ip6::Address mAllNetworkBackboneRouters;
Ip6::Address mAllDomainBackboneRouters;
otBackboneRouterDomainPrefixCallback mDomainPrefixCallback;
void * mDomainPrefixCallbackContext;
};
} // namespace BackboneRouter
+59 -3
View File
@@ -141,6 +141,11 @@ void NdProxyTable::Clear(void)
proxy.Clear();
}
if (mCallback != nullptr)
{
mCallback(mCallbackContext, OT_BACKBONE_ROUTER_NDPROXY_CLEARED, nullptr);
}
otLogNoteBbr("NdProxyTable::Clear!");
}
@@ -158,13 +163,14 @@ otError NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid,
VerifyOrExit(proxy->mMeshLocalIid == aMeshLocalIid, error = OT_ERROR_DUPLICATED);
proxy->Update(aRloc16, timeSinceLastTransaction);
NotifyDuaRegistrationOnBackboneLink(*proxy);
NotifyDuaRegistrationOnBackboneLink(*proxy, /* aIsRenew */ true);
ExitNow();
}
proxy = FindByMeshLocalIid(aMeshLocalIid);
if (proxy != nullptr)
{
TriggerCallback(OT_BACKBONE_ROUTER_NDPROXY_REMOVED, proxy->mAddressIid);
Erase(*proxy);
}
else
@@ -245,7 +251,7 @@ void NdProxyTable::HandleTimer(void)
if (proxy.IsDadAttamptsComplete())
{
proxy.mDadFlag = false;
NotifyDuaRegistrationOnBackboneLink(proxy);
NotifyDuaRegistrationOnBackboneLink(proxy, /* aIsRenew */ false);
}
else
{
@@ -262,6 +268,31 @@ exit:
return;
}
void NdProxyTable::SetCallback(otBackboneRouterNdProxyCallback aCallback, void *aContext)
{
mCallback = aCallback;
mCallbackContext = aContext;
}
void NdProxyTable::TriggerCallback(otBackboneRouterNdProxyEvent aEvent,
const Ip6::InterfaceIdentifier &aAddressIid) const
{
Ip6::Address dua;
const Ip6::Prefix *prefix = Get<BackboneRouter::Leader>().GetDomainPrefix();
VerifyOrExit(mCallback != nullptr);
OT_ASSERT(prefix != nullptr);
dua.SetPrefix(*prefix);
dua.SetIid(aAddressIid);
mCallback(mCallbackContext, aEvent, &dua);
exit:
return;
}
void NdProxyTable::NotifyDadComplete(NdProxyTable::NdProxy &aNdProxy, bool aDuplicated)
{
if (aDuplicated)
@@ -292,15 +323,40 @@ NdProxyTable::NdProxy *NdProxyTable::ResolveDua(const Ip6::Address &aDua)
return Get<Leader>().IsDomainUnicast(aDua) ? FindByAddressIid(aDua.GetIid()) : nullptr;
}
void NdProxyTable::NotifyDuaRegistrationOnBackboneLink(NdProxyTable::NdProxy &aNdProxy)
void NdProxyTable::NotifyDuaRegistrationOnBackboneLink(NdProxyTable::NdProxy &aNdProxy, bool aIsRenew)
{
if (!aNdProxy.mDadFlag)
{
TriggerCallback(aIsRenew ? OT_BACKBONE_ROUTER_NDPROXY_RENEWED : OT_BACKBONE_ROUTER_NDPROXY_ADDED,
aNdProxy.mAddressIid);
IgnoreError(Get<BackboneRouter::Manager>().SendProactiveBackboneNotification(
GetDua(aNdProxy), aNdProxy.GetMeshLocalIid(), aNdProxy.GetTimeSinceLastTransaction()));
}
}
otError NdProxyTable::GetInfo(const Ip6::Address &aDua, otBackboneRouterNdProxyInfo &aNdProxyInfo)
{
otError error = OT_ERROR_NOT_FOUND;
VerifyOrExit(Get<Leader>().IsDomainUnicast(aDua), error = OT_ERROR_INVALID_ARGS);
for (NdProxy &proxy : Iterate(kFilterValid))
{
if (proxy.mAddressIid == aDua.GetIid())
{
aNdProxyInfo.mMeshLocalIid = &proxy.mMeshLocalIid;
aNdProxyInfo.mTimeSinceLastTransaction = proxy.GetTimeSinceLastTransaction();
aNdProxyInfo.mRloc16 = proxy.mRloc16;
ExitNow(error = OT_ERROR_NONE);
}
}
exit:
return error;
}
} // namespace BackboneRouter
} // namespace ot
+29 -3
View File
@@ -132,6 +132,8 @@ public:
*/
explicit NdProxyTable(Instance &aInstance)
: InstanceLocator(aInstance)
, mCallback(nullptr)
, mCallbackContext(nullptr)
, mIsAnyDadInProcess(false)
{
}
@@ -206,6 +208,27 @@ public:
*/
static void Erase(NdProxy &aNdProxy);
/*
* This method sets the ND Proxy callback.
*
* @param[in] aCallback The callback function.
* @param[in] aContext A user context pointer.
*
*/
void SetCallback(otBackboneRouterNdProxyCallback aCallback, void *aContext);
/**
* This method retrieves the ND Proxy info of the Domain Unicast Address.
*
* @param[in] aDua The Domain Unicast Address to get info.
* @param[in] aNdProxyInfo A pointer to the ND Proxy info.
*
* @retval OT_ERROR_NONE Successfully retrieve the ND Proxy info.
* @retval OT_ERROR_NOT_FOUND Failed to find the Domain Unicast Address in the ND Proxy table.
*
*/
otError GetInfo(const Ip6::Address &aDua, otBackboneRouterNdProxyInfo &aNdProxyInfo);
private:
enum
{
@@ -275,10 +298,13 @@ private:
NdProxy * FindByMeshLocalIid(const Ip6::InterfaceIdentifier &aMeshLocalIid);
NdProxy * FindInvalid(void);
Ip6::Address GetDua(NdProxy &aNdProxy);
void NotifyDuaRegistrationOnBackboneLink(NdProxy &aNdProxy);
void NotifyDuaRegistrationOnBackboneLink(NdProxy &aNdProxy, bool aIsRenew);
void TriggerCallback(otBackboneRouterNdProxyEvent aEvent, const Ip6::InterfaceIdentifier &aAddressIid) const;
NdProxy mProxies[kMaxNdProxyNum];
bool mIsAnyDadInProcess : 1;
NdProxy mProxies[kMaxNdProxyNum];
otBackboneRouterNdProxyCallback mCallback;
void * mCallbackContext;
bool mIsAnyDadInProcess : 1;
};
} // namespace BackboneRouter
@@ -122,8 +122,8 @@ class BBR_5_11_01(thread_cert.TestCase):
backbone=True))
# Step 23: Host sends ping packet to destination Dg
self.assertFalse(self.nodes[HOST].ping(Dg,
backbone=True)) # Must fail since ND Proxying is not implemented yet
# TODO: (DUA) implement DUA routing on OTBR
self.assertFalse(self.nodes[HOST].ping(Dg, backbone=True))
def verify(self, pv: PacketVerifier):
pkts = pv.pkts
@@ -161,12 +161,10 @@ class BBR_5_11_01(thread_cert.TestCase):
pkts.filter_eth_src(Host_ETH).filter_ipv6_src_dst(Host_BGUA, BR_1_BGUA).filter_ping_reply().must_next()
# Step 16: Host: Queries DUA, Dg, with ND-NS
# TODO: setup radvd on Host
pkts.filter_eth_src(Host_ETH).filter_icmpv6_nd_ns(Dg).must_not_next()
pkts.filter_eth_src(Host_ETH).filter_icmpv6_nd_ns(Dg).must_next()
# Step 17: BR_1: Responds with a neighbor advertisement.
# TODO: (DUA) implement ND proxy on PBBR
pkts.filter_eth_src(BR_1_ETH).filter_icmpv6_nd_na(Dg).must_not_next()
pkts.filter_eth_src(BR_1_ETH).filter_icmpv6_nd_na(Dg).must_next()
if __name__ == '__main__':
@@ -0,0 +1,225 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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.
#
# This test verifies PBBR's ND Proxy feature:
# 1. Sends ICMPv6 Neighbor Advertisement (NA) for ND Proxy
# 2. Receive and handle ICMPv6 Neighbor Solicitation (NS) and sends NA accordingly.
#
import ipaddress
import unittest
import config
import thread_cert
from pktverify.packet_verifier import PacketVerifier
from pktverify.consts import DUA_RECENT_TIME
PBBR = 1
SBBR = 2
ROUTER1 = 3
HOST = 4
REREG_DELAY = 5 # Seconds
MLR_TIMEOUT = 300 # Seconds
WAIT_REDUNDANCE = 3
class TestNdProxy(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
# Topology:
# ---- -(eth)------------
# | | |
# PBBR----SBBR HOST
# \ /
# Router1
#
TOPOLOGY = {
PBBR: {
'name': 'PBBR',
'allowlist': [SBBR, ROUTER1],
'is_otbr': True,
'version': '1.2',
'router_selection_jitter': 1,
},
SBBR: {
'name': 'SBBR',
'allowlist': [PBBR, ROUTER1],
'is_otbr': True,
'version': '1.2',
'router_selection_jitter': 1,
},
ROUTER1: {
'name': 'ROUTER1',
'allowlist': [PBBR, SBBR],
'version': '1.2',
'router_selection_jitter': 1,
},
HOST: {
'name': 'HOST',
'is_host': True
},
}
def test(self):
# Bring up HOST
self.nodes[HOST].start()
# Bring up PBBR
self.nodes[PBBR].start()
self.simulator.go(5)
self.assertEqual('leader', self.nodes[PBBR].get_state())
self.wait_node_state(PBBR, 'leader', 5)
self.nodes[PBBR].set_backbone_router(reg_delay=REREG_DELAY, mlr_timeout=MLR_TIMEOUT)
self.nodes[PBBR].enable_backbone_router()
self.nodes[PBBR].set_domain_prefix(config.DOMAIN_PREFIX, 'prosD')
self.simulator.go(5)
self.assertTrue(self.nodes[PBBR].is_primary_backbone_router)
self.assertIsNotNone(self.nodes[PBBR].get_ip6_address(config.ADDRESS_TYPE.DUA))
# Bring up SBBR
self.nodes[SBBR].start()
self.simulator.go(5)
self.assertEqual('router', self.nodes[SBBR].get_state())
self.nodes[SBBR].set_backbone_router(reg_delay=REREG_DELAY, mlr_timeout=MLR_TIMEOUT)
self.nodes[SBBR].enable_backbone_router()
self.simulator.go(5)
self.assertFalse(self.nodes[SBBR].is_primary_backbone_router)
self.assertIsNotNone(self.nodes[SBBR].get_ip6_address(config.ADDRESS_TYPE.DUA))
# Bring up ROUTER1
self.nodes[ROUTER1].start()
self.simulator.go(5)
self.assertEqual('router', self.nodes[ROUTER1].get_state())
self.assertIsNotNone(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.DUA))
self.collect_ipaddrs()
self.collect_rloc16s()
# Wait for DUA_RECENT_TIME
self.simulator.go(DUA_RECENT_TIME + WAIT_REDUNDANCE)
self._test_neighbor_solicitation(PBBR)
self._test_neighbor_solicitation(SBBR)
self._test_neighbor_solicitation(ROUTER1)
def _test_neighbor_solicitation(self, nodeid):
dua = self.nodes[nodeid].get_ip6_address(config.ADDRESS_TYPE.DUA)
print('Node DUA: ', dua)
self.nodes[HOST].ip_neighbors_flush()
self.nodes[HOST].ping(dua, backbone=True)
self.simulator.go(WAIT_REDUNDANCE)
def verify(self, pv: PacketVerifier):
pkts = pv.pkts
pv.add_common_vars()
pv.summary.show()
PBBR_DUA = pv.vars['PBBR_DUA']
SBBR_DUA = pv.vars['SBBR_DUA']
ROUTER1_DUA = pv.vars['ROUTER1_DUA']
PBBR_ETH = pv.vars['PBBR_ETH']
SBBR_ETH = pv.vars['SBBR_ETH']
HOST_ETH = pv.vars['HOST_ETH']
HOST_BGUA = pv.vars['HOST_BGUA']
# SBBR must not send NA for any DUA
pkts.filter_eth_src(SBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(PBBR_DUA).must_not_next()
pkts.filter_eth_src(SBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(SBBR_DUA).must_not_next()
pkts.filter_eth_src(SBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(ROUTER1_DUA).must_not_next()
# PBBR must send unsolicited NA for PBBR's DUA
pkts.filter_eth_src(PBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(PBBR_DUA).filter(
'icmpv6.nd.na.flag.s == 0').must_next().must_verify("""
icmpv6.opt.linkaddr == {PBBR_ETH}
and icmpv6.nd.na.flag.r == 1
and icmpv6.nd.na.flag.o == 1
and icmpv6.nd.na.flag.rsv == 0
""",
PBBR_ETH=PBBR_ETH)
# PBBR must send unsolicited NA for SBBR's DUA
pkts.filter_eth_src(PBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(SBBR_DUA).filter(
'icmpv6.nd.na.flag.s == 0').must_next().must_verify("""
icmpv6.opt.linkaddr == {PBBR_ETH}
and icmpv6.nd.na.flag.r == 1
and icmpv6.nd.na.flag.o == 1
and icmpv6.nd.na.flag.rsv == 0
""",
PBBR_ETH=PBBR_ETH)
# PBBR must send unsolicited NA for ROUTER1's DUA
pkts.filter_eth_src(PBBR_ETH).filter_LLANMA().filter_icmpv6_nd_na(ROUTER1_DUA).filter(
'icmpv6.nd.na.flag.s == 0').must_next().must_verify("""
icmpv6.opt.linkaddr == {PBBR_ETH}
and icmpv6.nd.na.flag.r == 1
and icmpv6.nd.na.flag.o == 1
and icmpv6.nd.na.flag.rsv == 0
""",
PBBR_ETH=PBBR_ETH)
# HOST should send NS for PBBR's DUA
pkts.filter_eth_src(HOST_ETH).filter_icmpv6_nd_ns(PBBR_DUA).must_next()
# PBBR must send solicited NA for PBBR's DUA
pkts.filter_eth_src(PBBR_ETH).filter_ipv6_dst(HOST_BGUA).filter_icmpv6_nd_na(PBBR_DUA).filter(
'icmpv6.nd.na.flag.s == 1').must_next().must_verify("""
icmpv6.opt.linkaddr == {PBBR_ETH}
and icmpv6.nd.na.flag.r == 1
and icmpv6.nd.na.flag.o == 0
and icmpv6.nd.na.flag.rsv == 0
""",
PBBR_ETH=PBBR_ETH)
# Host should send NS for SBBR's DUA
pkts.filter_eth_src(HOST_ETH).filter_icmpv6_nd_ns(SBBR_DUA).must_next()
# PBBR must send solicited NA for SBBR's DUA
pkts.filter_eth_src(PBBR_ETH).filter_ipv6_dst(HOST_BGUA).filter_icmpv6_nd_na(SBBR_DUA).filter(
'icmpv6.nd.na.flag.s == 1').must_next().must_verify("""
icmpv6.opt.linkaddr == {PBBR_ETH}
and icmpv6.nd.na.flag.r == 1
and icmpv6.nd.na.flag.o == 0
and icmpv6.nd.na.flag.rsv == 0
""",
PBBR_ETH=PBBR_ETH)
# Host should send NS for ROUTER1's DUA
pkts.filter_eth_src(HOST_ETH).filter_icmpv6_nd_ns(ROUTER1_DUA).must_next()
# PBBR must send solicited NA for ROUTER1's DUA
pkts.filter_eth_src(PBBR_ETH).filter_ipv6_dst(HOST_BGUA).filter_icmpv6_nd_na(ROUTER1_DUA).filter(
'icmpv6.nd.na.flag.s == 1').must_next().must_verify("""
icmpv6.opt.linkaddr == {PBBR_ETH}
and icmpv6.nd.na.flag.r == 1
and icmpv6.nd.na.flag.o == 0
and icmpv6.nd.na.flag.rsv == 0
""",
PBBR_ETH=PBBR_ETH)
if __name__ == '__main__':
unittest.main()
+43 -3
View File
@@ -217,6 +217,9 @@ class OtbrDocker:
else:
return lines
def _setup_sysctl(self):
self.bash(f'sysctl net.ipv6.conf.{self.ETH_DEV}.accept_ra=2')
class OtCli:
@@ -2018,6 +2021,14 @@ class LinuxHost():
else:
return super().ping(*args, **kwargs)
def ip_neighbors_flush(self):
# clear neigh cache on linux
self.bash(f'ip -6 neigh list dev {self.ETH_DEV}')
self.bash(f'ip -6 neigh flush nud all nud failed nud noarp dev {self.ETH_DEV}')
self.bash('ip -6 neigh list nud all dev %s | cut -d " " -f1 | sudo xargs -I{} ip -6 neigh delete {} dev %s' %
(self.ETH_DEV, self.ETH_DEV))
self.bash(f'ip -6 neigh list dev {self.ETH_DEV}')
class OtbrNode(LinuxHost, NodeImpl, OtbrDocker):
is_otbr = True
@@ -2030,6 +2041,10 @@ class OtbrNode(LinuxHost, NodeImpl, OtbrDocker):
def get_addrs(self) -> List[str]:
return super().get_addrs() + self.get_ether_addrs()
def start(self):
self._setup_sysctl()
super().start()
class HostNode(LinuxHost, OtbrDocker):
is_host = True
@@ -2040,11 +2055,11 @@ class HostNode(LinuxHost, OtbrDocker):
super().__init__(nodeid, **kwargs)
def start(self):
# TODO: Use radvd to advertise the Domain Prefix on the Backbone link.
pass
self._setup_sysctl()
self._service_radvd_start()
def stop(self):
pass
self._service_radvd_stop()
def get_addrs(self) -> List[str]:
return self.get_ether_addrs()
@@ -2068,6 +2083,31 @@ class HostNode(LinuxHost, OtbrDocker):
else:
return None
def _service_radvd_start(self):
self.bash("""cat >/etc/radvd.conf <<EOF
interface eth0
{
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 30;
AdvDefaultPreference low;
prefix %s
{
AdvOnLink on;
AdvAutonomous off;
AdvRouterAddr off;
};
};
EOF
""" % config.DOMAIN_PREFIX)
self.bash('service radvd start')
self.bash('service radvd status') # Make sure radvd service is running
def _service_radvd_stop(self):
self.bash('service radvd stop')
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -81,7 +81,7 @@ class Ipv6Addr(Bytes):
super().__init__(addr)
if len(self) != 16:
raise ValueError((addr, self))
raise ValueError(addr)
self._addr = ipaddress.IPv6Address(self)
def __repr__(self):
@@ -85,6 +85,7 @@ def cleanup_env():
def setup_env():
bash('sudo modprobe ip6table_filter')
bash(f'docker image inspect {config.OTBR_DOCKER_IMAGE} >/dev/null')