diff --git a/src/ncp/ncp_host.cpp b/src/ncp/ncp_host.cpp index af988feb45f..0917d9ac363 100644 --- a/src/ncp/ncp_host.cpp +++ b/src/ncp/ncp_host.cpp @@ -90,6 +90,8 @@ void NcpHost::Init(void) mNcpSpinel.Ip6SetAddressMulticastCallback( [this](const std::vector &aAddrs) { mNetif.UpdateIp6MulticastAddresses(aAddrs); }); mNcpSpinel.NetifSetStateChangedCallback([this](bool aState) { mNetif.SetNetifState(aState); }); + mNcpSpinel.Ip6SetReceiveCallback( + [this](const uint8_t *aData, uint16_t aLength) { mNetif.Ip6Receive(aData, aLength); }); } void NcpHost::Deinit(void) @@ -147,6 +149,8 @@ void NcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatase void NcpHost::Process(const MainloopContext &aMainloop) { mSpinelDriver.Process(&aMainloop); + + mNetif.Process(&aMainloop); } void NcpHost::Update(MainloopContext &aMainloop) @@ -158,6 +162,8 @@ void NcpHost::Update(MainloopContext &aMainloop) aMainloop.mTimeout.tv_sec = 0; aMainloop.mTimeout.tv_usec = 0; } + + mNetif.UpdateFdSet(&aMainloop); } } // namespace Ncp diff --git a/src/ncp/ncp_spinel.cpp b/src/ncp/ncp_spinel.cpp index c9a0458dba2..57295ec9abf 100644 --- a/src/ncp/ncp_spinel.cpp +++ b/src/ncp/ncp_spinel.cpp @@ -153,11 +153,13 @@ void NcpSpinel::Ip6SetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask) otbrError NcpSpinel::Ip6Send(const uint8_t *aData, uint16_t aLength) { - // TODO: Impelement this function. - OTBR_UNUSED_VARIABLE(aData); - OTBR_UNUSED_VARIABLE(aLength); + otbrError error = OTBR_ERROR_NONE; + EncodingFunc encodingFunc = [this, aData, aLength] { return mEncoder.WriteDataWithLen(aData, aLength); }; + + SuccessOrExit(SetProperty(SPINEL_PROP_STREAM_NET, encodingFunc), error = OTBR_ERROR_OPENTHREAD); - return OTBR_ERROR_NONE; +exit: + return error; } void NcpSpinel::ThreadSetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask) @@ -393,6 +395,16 @@ void NcpSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, ui break; } + case SPINEL_PROP_STREAM_NET: + { + const uint8_t *data; + uint16_t dataLen; + + SuccessOrExit(ParseIp6StreamNet(aBuffer, aLength, data, dataLen), error = OTBR_ERROR_PARSE); + SafeInvoke(mIp6ReceiveCallback, data, dataLen); + break; + } + default: otbrLogWarning("Received uncognized key: %u", aKey); break; @@ -449,6 +461,9 @@ otbrError NcpSpinel::HandleResponseForPropSet(spinel_tid_t aTid, } break; + case SPINEL_PROP_STREAM_NET: + break; + default: VerifyOrExit(aKey == mWaitingKeyTable[aTid], error = OTBR_ERROR_INVALID_STATE); break; @@ -587,6 +602,20 @@ otError NcpSpinel::ParseIp6MulticastAddresses(const uint8_t *aBuf, uint8_t aLen, return error; } +otError NcpSpinel::ParseIp6StreamNet(const uint8_t *aBuf, uint8_t aLen, const uint8_t *&aData, uint16_t &aDataLen) +{ + otError error = OT_ERROR_NONE; + ot::Spinel::Decoder decoder; + + VerifyOrExit(aBuf != nullptr, error = OT_ERROR_INVALID_ARGS); + + decoder.Init(aBuf, aLen); + error = decoder.ReadDataWithLen(aData, aDataLen); + +exit: + return error; +} + otDeviceRole NcpSpinel::SpinelRoleToDeviceRole(spinel_net_role_t aRole) { otDeviceRole role = OT_DEVICE_ROLE_DISABLED; diff --git a/src/ncp/ncp_spinel.hpp b/src/ncp/ncp_spinel.hpp index f60895db47f..bfcea057617 100644 --- a/src/ncp/ncp_spinel.hpp +++ b/src/ncp/ncp_spinel.hpp @@ -86,6 +86,7 @@ class NcpSpinel using Ip6AddressTableCallback = std::function &)>; using Ip6MulticastAddressTableCallback = std::function &)>; using NetifStateChangedCallback = std::function; + using Ip6ReceiveCallback = std::function; /** * Constructor. @@ -178,6 +179,14 @@ class NcpSpinel mIp6MulticastAddressTableCallback = aCallback; } + /** + * This method sets the callback to receive IP6 datagrams. + * + * @param[in] aCallback The callback to receive IP6 datagrams. + * + */ + void Ip6SetReceiveCallback(const Ip6ReceiveCallback &aCallback) { mIp6ReceiveCallback = aCallback; } + /** * This methods sends an IP6 datagram through the NCP. * @@ -286,6 +295,7 @@ class NcpSpinel otError ParseIp6AddressTable(const uint8_t *aBuf, uint16_t aLength, std::vector &aAddressTable); otError ParseIp6MulticastAddresses(const uint8_t *aBuf, uint8_t aLen, std::vector &aAddressList); + otError ParseIp6StreamNet(const uint8_t *aBuf, uint8_t aLen, const uint8_t *&aData, uint16_t &aDataLen); ot::Spinel::SpinelDriver *mSpinelDriver; uint16_t mCmdTidsInUse; ///< Used transaction ids. @@ -314,6 +324,7 @@ class NcpSpinel Ip6AddressTableCallback mIp6AddressTableCallback; Ip6MulticastAddressTableCallback mIp6MulticastAddressTableCallback; + Ip6ReceiveCallback mIp6ReceiveCallback; NetifStateChangedCallback mNetifStateChangedCallback; }; diff --git a/tests/scripts/expect/_common.exp b/tests/scripts/expect/_common.exp index bb02ff81b39..277e9709e95 100644 --- a/tests/scripts/expect/_common.exp +++ b/tests/scripts/expect/_common.exp @@ -101,3 +101,12 @@ proc switch_node {id} { send_user "\n# ${id}\n" set spawn_id $spawn_ids($id) } + +proc get_ipaddr {type} { + send "ipaddr $type\n" + expect "ipaddr $type" + set rval [expect_line {([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}}] + expect_line "Done" + + return $rval +} diff --git a/tests/scripts/expect/ncp_netif_tx_rx.exp b/tests/scripts/expect/ncp_netif_tx_rx.exp new file mode 100755 index 00000000000..8e7d1f3c5e4 --- /dev/null +++ b/tests/scripts/expect/ncp_netif_tx_rx.exp @@ -0,0 +1,67 @@ +#!/usr/bin/expect -f +# +# 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. +# +source "tests/scripts/expect/_common.exp" + +set dataset "0e080000000000010000000300001435060004001fffe002087d61eb42cdc48d6a0708fd0d07fca1b9f0500510ba088fc2bd6c3b3897f7a10f58263ff3030f4f70656e5468726561642d353234660102524f04109dc023ccd447b12b50997ef68020f19e0c0402a0f7f8" +set dataset_dbus "0x0e,0x08,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x14,0x35,0x06,0x00,0x04,0x00,0x1f,0xff,0xe0,0x02,0x08,0x7d,0x61,0xeb,0x42,0xcd,0xc4,0x8d,0x6a,0x07,0x08,0xfd,0x0d,0x07,0xfc,0xa1,0xb9,0xf0,0x50,0x05,0x10,0xba,0x08,0x8f,0xc2,0xbd,0x6c,0x3b,0x38,0x97,0xf7,0xa1,0x0f,0x58,0x26,0x3f,0xf3,0x03,0x0f,0x4f,0x70,0x65,0x6e,0x54,0x68,0x72,0x65,0x61,0x64,0x2d,0x35,0x32,0x34,0x66,0x01,0x02,0x52,0x4f,0x04,0x10,0x9d,0xc0,0x23,0xcc,0xd4,0x47,0xb1,0x2b,0x50,0x99,0x7e,0xf6,0x80,0x20,0xf1,0x9e,0x0c,0x04,0x02,0xa0,0xf7,0xf8" + +# Step 1. Start a Thread node and create a Thread network +spawn_node 1 cli $::env(EXP_OT_CLI_PATH) + +send "dataset set active ${dataset}\n" +expect_line "Done" +send "ifconfig up\n" +expect_line "Done" +send "thread start\n" +expect_line "Done" +wait_for "state" "leader" +expect_line "Done" +set dest_addr [get_ipaddr mleid] + +# Step 2. Start otbr-agent with a NCP and join the network by dbus join method +spawn_node 2 otbr $::env(EXP_OT_NCP_PATH) +sleep 1 + +spawn dbus-send --system --dest=io.openthread.BorderRouter.wpan0 --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 io.openthread.BorderRouter.Join "array:byte:${dataset_dbus}" +expect eof + +# Step 3. Wait 10 seconds, check if the otbr-agent has attached successfully +sleep 10 +spawn dbus-send --system --dest=io.openthread.BorderRouter.wpan0 --print-reply --reply-timeout=1000 /io/openthread/BorderRouter/wpan0 org.freedesktop.DBus.Properties.Get string:io.openthread.BorderRouter string:DeviceRole +expect -re {router|child} { +} timeout { + puts "timeout!" + exit 1 +} +expect eof + +# Step 4. Verify pinging from otbr-agent NCP to the cli node +spawn ping6 -c 10 ${dest_addr} +expect "10 packets transmitted, 10 received, 0% packet loss" +expect eof