From b605bb993e6b08d14334ae32918c051d6a5e0e22 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Mon, 23 Sep 2024 23:38:20 +0800 Subject: [PATCH] [ncp] add border routing InfraIf setup (#10707) --- .github/workflows/unit.yml | 2 +- src/lib/spinel/spinel.h | 15 ++++ src/ncp/ncp_base.cpp | 4 ++ src/ncp/ncp_base.hpp | 19 +++++ src/ncp/ncp_base_dispatcher.cpp | 3 + src/ncp/ncp_base_ftd.cpp | 58 +++++++++++++++ src/ncp/platform/infra_if.cpp | 5 +- tests/unit/CMakeLists.txt | 35 +++++++++ tests/unit/test_ncp_infra_if.cpp | 120 +++++++++++++++++++++++++++++++ 9 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 tests/unit/test_ncp_infra_if.cpp diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index c66c8e463..30025d248 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -79,7 +79,7 @@ jobs: sudo rm /etc/apt/sources.list.d/* && sudo apt-get update sudo apt-get --no-install-recommends install -y ninja-build lcov - name: Build Simulation - run: ./script/cmake-build simulation + run: ./script/cmake-build simulation -DOT_BORDER_ROUTING=ON -DOT_NCP_INFRA_IF=ON - name: Test Simulation run: cd build/simulation && ninja test - name: Build Multipan Simulation diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 169b12ab8..5451f3060 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -4946,6 +4946,21 @@ enum SPINEL_PROP_MULTIPAN__END = 0x910, + SPINEL_PROP_INFRA_IF__BEGIN = 0x910, + + /// Infrastructure interface setup. + /** Format: `LbA(6)` + * Type: Write + * + * `L`: The infrastructure interface index. + * `b`: If the infrastrue interface is running. + * `A(6)`: The IPv6 addresses of the infrastructure interface. + * + */ + SPINEL_PROP_INFRA_IF_SETUP = SPINEL_PROP_INFRA_IF__BEGIN + 1, + + SPINEL_PROP_INFRA_IF__END = 0x920, + SPINEL_PROP_NEST__BEGIN = 0x3BC0, SPINEL_PROP_NEST_STREAM_MFG = SPINEL_PROP_NEST__BEGIN + 0, diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 6896878fe..d85591b6c 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -317,6 +317,10 @@ NcpBase::NcpBase(Instance *aInstance) , mDidInitialUpdates(false) , mDatasetSendMgmtPendingSetResult(SPINEL_STATUS_OK) , mLogTimestampBase(0) +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE + , mInfraIfAddrCount(0) + , mInfraIfIndex(0) +#endif #if OPENTHREAD_CONFIG_DIAG_ENABLE , mDiagOutput(nullptr) , mDiagOutputLen(0) diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index c326b8e36..5e179b452 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -225,6 +225,15 @@ public: */ bool ShouldDeferHostSend(void); + /** + * Check if the infrastructure interface has an IPv6 address. + * + * @param[in] aInfraIfIndex The index of the instructure interface to query. + * @param[in] aAddress The IPv6 address to query. + * + */ + bool InfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress); + protected: static constexpr uint8_t kBitsPerByte = 8; ///< Number of bits in a byte. @@ -746,6 +755,16 @@ protected: uint64_t mLogTimestampBase; // Timestamp base used for logging +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE + otError InfraIfAddAddress(const otIp6Address &aAddress); + bool InfraIfContainsAddress(const otIp6Address &aAddress); + + static constexpr uint8_t kMaxInfraIfAddrs = 10; + otIp6Address mInfraIfAddrs[kMaxInfraIfAddrs]; + uint8_t mInfraIfAddrCount; + uint32_t mInfraIfIndex; +#endif + #if OPENTHREAD_CONFIG_DIAG_ENABLE char *mDiagOutput; uint16_t mDiagOutputLen; diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index c7b7c1fe8..fe11d7fc5 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -512,6 +512,9 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_RADIO && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE), #endif +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_INFRA_IF_SETUP), +#endif #if OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_UNSOL_UPDATE_FILTER), #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE diff --git a/src/ncp/ncp_base_ftd.cpp b/src/ncp/ncp_base_ftd.cpp index b0d723312..866ae0098 100644 --- a/src/ncp/ncp_base_ftd.cpp +++ b/src/ncp/ncp_base_ftd.cpp @@ -1403,6 +1403,64 @@ exit: } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE +#if OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + bool isInfraRunning; + + SuccessOrExit(error = mDecoder.ReadUint32(mInfraIfIndex)); + SuccessOrExit(error = mDecoder.ReadBool(isInfraRunning)); + + mInfraIfAddrCount = 0; + while (!mDecoder.IsAllReadInStruct()) + { + const otIp6Address *addr; + + SuccessOrExit(error = mDecoder.ReadIp6Address(addr)); + SuccessOrExit(error = InfraIfAddAddress(*addr)); + } + + IgnoreError(otBorderRoutingSetEnabled(mInstance, /* aEnabled */ false)); + SuccessOrExit(error = otBorderRoutingInit(mInstance, mInfraIfIndex, isInfraRunning)); + SuccessOrExit(error = otBorderRoutingSetEnabled(mInstance, /* aEnabled */ true)); + +exit: + return error; +} + +otError NcpBase::InfraIfAddAddress(const otIp6Address &aAddress) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(mInfraIfAddrCount < kMaxInfraIfAddrs, error = OT_ERROR_NO_BUFS); + memcpy(&mInfraIfAddrs[mInfraIfAddrCount++], &aAddress, sizeof(aAddress)); +exit: + return error; +} + +bool NcpBase::InfraIfContainsAddress(const otIp6Address &aAddress) +{ + bool result = false; + + for (uint8_t i = 0; i < mInfraIfAddrCount; i++) + { + if (memcmp(&mInfraIfAddrs[i], &aAddress, sizeof(aAddress)) == 0) + { + result = true; + break; + } + } + return result; +} + +bool NcpBase::InfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress) +{ + return aInfraIfIndex == mInfraIfIndex && InfraIfContainsAddress(*aAddress); +} + +#endif // OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE + } // namespace Ncp } // namespace ot diff --git a/src/ncp/platform/infra_if.cpp b/src/ncp/platform/infra_if.cpp index 8dd176c7d..bc9048646 100644 --- a/src/ncp/platform/infra_if.cpp +++ b/src/ncp/platform/infra_if.cpp @@ -33,10 +33,7 @@ #if OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress) { - OT_UNUSED_VARIABLE(aInfraIfIndex); - OT_UNUSED_VARIABLE(aAddress); - - return true; + return ot::Ncp::NcpBase::GetNcpInstance()->InfraIfHasAddress(aInfraIfIndex, aAddress); } otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index e936417c1..dd4cdbdb0 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -161,6 +161,39 @@ macro(ot_unit_test name) add_test(NAME ot-test-${name} COMMAND ot-test-${name}) endmacro() +#---------------------------------------------------------------------------------------------------------------------- + +macro(ot_unit_ncp_test name) + + # Macro to add an OpenThread unit test for NCP functions. + # + # Unit test name will be `ot-test-ncp-{name}`. Test source file of + # `test_ncp_{name}.cpp` is used. Optional extra arguments can be + # passed to provide additional source files. + + add_executable(ot-test-ncp-${name} + test_ncp_${name}.cpp ${ARGN} + ) + + target_include_directories(ot-test-ncp-${name} + PRIVATE + ${COMMON_INCLUDES} + ) + + target_link_libraries(ot-test-ncp-${name} + PRIVATE + openthread-ncp-ftd + ${COMMON_LIBS} + ) + + target_compile_options(ot-test-ncp-${name} + PRIVATE + -DOPENTHREAD_FTD=1 + ) + + add_test(NAME ot-test-ncp-${name} COMMAND ot-test-ncp-${name}) +endmacro() + #---------------------------------------------------------------------------------------------------------------------- # Unit tests @@ -228,6 +261,8 @@ ot_unit_test(toolchain test_toolchain_c.c) ot_unit_test(trickle_timer) ot_unit_test(url) +ot_unit_ncp_test(infra_if) + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(OT_MULTIPAN_RCP) diff --git a/tests/unit/test_ncp_infra_if.cpp b/tests/unit/test_ncp_infra_if.cpp new file mode 100644 index 000000000..692ef3f50 --- /dev/null +++ b/tests/unit/test_ncp_infra_if.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024, 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. + */ + +#include + +#include +#include + +#include "test_platform.h" +#include "test_util.h" +#include "common/code_utils.hpp" +#include "lib/spinel/spinel_buffer.hpp" +#include "lib/spinel/spinel_encoder.hpp" +#include "ncp/ncp_base.hpp" + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE + +namespace ot { + +constexpr uint16_t kMaxSpinelBufferSize = 2048; + +static otError GenerateSpinelInfraIfSetUpFrame(uint32_t akInfraIfIndex, + bool aIsRunning, + const otIp6Address *aAddrs, + uint8_t aAddrCount, + uint8_t *aBuf, + uint16_t &aLen) +{ + otError error = OT_ERROR_NONE; + uint8_t buf[kMaxSpinelBufferSize]; + Spinel::Buffer ncpBuffer(buf, kMaxSpinelBufferSize); + Spinel::Encoder encoder(ncpBuffer); + + uint8_t header = SPINEL_HEADER_FLAG | 0 /* Iid */ | 1 /* Tid */; + SuccessOrExit(error = encoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_INFRA_IF_SETUP)); + SuccessOrExit(error = encoder.WriteUint32(akInfraIfIndex)); + SuccessOrExit(error = encoder.WriteBool(true)); + for (uint8_t i = 0; i < aAddrCount; i++) + { + SuccessOrExit(error = encoder.WriteIp6Address(aAddrs[i])); + } + SuccessOrExit(error = encoder.EndFrame()); + + SuccessOrExit(ncpBuffer.OutFrameBegin()); + aLen = ncpBuffer.OutFrameGetLength(); + VerifyOrExit(ncpBuffer.OutFrameRead(aLen, aBuf) == aLen, error = OT_ERROR_FAILED); + +exit: + return error; +} + +void TestNcpInfraIfSetUp(void) +{ + Instance *instance = static_cast(testInitInstance()); + Ncp::NcpBase ncpBase(instance); + + uint8_t recvBuf[kMaxSpinelBufferSize]; + uint16_t recvLen; + constexpr uint32_t kInfraIfIndex = 1; + + const otIp6Address infraIfAddresses[] = { + {0xfd, 0x35, 0x7a, 0x7d, 0x0f, 0x16, 0xe7, 0xe3, 0xc9, 0x79, 0x59, 0x29, 0xc8, 0xc2, 0xa3, 0x7b}, + }; + + VerifyOrQuit(otBorderRoutingGetState(instance) == OT_BORDER_ROUTING_STATE_UNINITIALIZED); + + SuccessOrQuit(GenerateSpinelInfraIfSetUpFrame(kInfraIfIndex, true /* IsRunning */, infraIfAddresses, + sizeof(infraIfAddresses) / sizeof(infraIfAddresses[0]), recvBuf, + recvLen)); + ncpBase.HandleReceive(recvBuf, recvLen); + VerifyOrQuit(otBorderRoutingGetState(instance) == OT_BORDER_ROUTING_STATE_STOPPED); + VerifyOrQuit(otPlatInfraIfHasAddress(kInfraIfIndex, &infraIfAddresses[0])); + VerifyOrQuit(!otPlatInfraIfHasAddress(kInfraIfIndex + 100, &infraIfAddresses[0])); + + SuccessOrQuit( + GenerateSpinelInfraIfSetUpFrame(kInfraIfIndex, true /* IsRunning */, infraIfAddresses, 0, recvBuf, recvLen)); + ncpBase.HandleReceive(recvBuf, recvLen); + VerifyOrQuit(otBorderRoutingGetState(instance) == OT_BORDER_ROUTING_STATE_STOPPED); + VerifyOrQuit(!otPlatInfraIfHasAddress(kInfraIfIndex, &infraIfAddresses[0])); + + printf("Test Ncp Infra If SetUp passed.\n"); +} + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE + +int main(void) +{ +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NCP_INFRA_IF_ENABLE + ot::TestNcpInfraIfSetUp(); +#endif + printf("All tests passed\n"); + return 0; +}