From 4cb0de4233d1dab612d01821ae27332a775cd04d Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Wed, 3 Sep 2025 01:12:13 +0800 Subject: [PATCH] [p2p] add initial peer-to-peer support (#11494) This commit implements a minimum P2P which only supports sending/receiving wake-up frames and establishing P2P links between rx-on-when-idle devices. --- doc/ot_api_doc.h | 12 + doc/ot_config_doc.h | 1 + etc/cmake/options.cmake | 1 + include/openthread/BUILD.gn | 2 + include/openthread/instance.h | 2 +- include/openthread/provisional/link.h | 89 +++++ include/openthread/provisional/p2p.h | 131 +++++++ script/test | 8 + src/cli/README.md | 12 + src/cli/cli.cpp | 55 +++ src/cli/cli.hpp | 5 + src/core/BUILD.gn | 7 + src/core/CMakeLists.txt | 4 + src/core/api/p2p_api.cpp | 56 +++ src/core/config/p2p.h | 68 ++++ src/core/config/wakeup.h | 18 + src/core/instance/instance.hpp | 4 + src/core/mac/mac.cpp | 29 +- src/core/mac/mac_types.cpp | 19 + src/core/mac/mac_types.hpp | 95 +++++ src/core/mac/wakeup_tx_scheduler.cpp | 18 +- src/core/mac/wakeup_tx_scheduler.hpp | 27 +- src/core/openthread-core-config.h | 1 + src/core/thread/mle.cpp | 48 ++- src/core/thread/mle.hpp | 128 ++++++- src/core/thread/mle_p2p.cpp | 406 +++++++++++++++++++++ src/core/thread/mle_types.hpp | 69 ++-- src/core/thread/neighbor.cpp | 4 + src/core/thread/neighbor.hpp | 1 + src/core/thread/neighbor_table.cpp | 14 + src/core/thread/neighbor_table.hpp | 4 + src/core/thread/peer.cpp | 64 ++++ src/core/thread/peer.hpp | 97 +++++ src/core/thread/peer_table.cpp | 135 +++++++ src/core/thread/peer_table.hpp | 190 ++++++++++ tests/scripts/expect/v1_5-cli-p2p-link.exp | 217 +++++++++++ tests/unit/test_child_table.cpp | 4 + 37 files changed, 1986 insertions(+), 59 deletions(-) create mode 100644 include/openthread/provisional/link.h create mode 100644 include/openthread/provisional/p2p.h create mode 100644 src/core/api/p2p_api.cpp create mode 100644 src/core/config/p2p.h create mode 100644 src/core/thread/mle_p2p.cpp create mode 100644 src/core/thread/peer.cpp create mode 100644 src/core/thread/peer.hpp create mode 100644 src/core/thread/peer_table.cpp create mode 100644 src/core/thread/peer_table.hpp create mode 100755 tests/scripts/expect/v1_5-cli-p2p-link.exp diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index 1e89578c2..54252193b 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -159,6 +159,18 @@ * * @} * + * @defgroup api-provisional Provisional + * @brief + * This module includes the OpenThread provisional APIs. These APIs are not stable and users should use them with + * caution. + * + * @{ + * + * @defgroup api-provisional-link Link + * @defgroup api-provisional-p2p Peer-to-Peer + * + * @} + * * @} */ diff --git a/doc/ot_config_doc.h b/doc/ot_config_doc.h index e72eb2329..6d05d88de 100644 --- a/doc/ot_config_doc.h +++ b/doc/ot_config_doc.h @@ -73,6 +73,7 @@ * @defgroup config-nat64 NAT64 * @defgroup config-netdata-publisher Network Data Publisher * @defgroup config-network-diagnostic Network Diagnostics + * @defgroup config-p2p Peer-to-Peer * @defgroup config-parent-search Parent Search * @defgroup config-ping-sender Ping Sender * @defgroup config-platform Platform Specific Services diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index a13e943d8..48dbcce19 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -239,6 +239,7 @@ ot_option(OT_NETDIAG_CLIENT OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE "Network ot_option(OT_NETDIAG_VENDOR_INFO OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE "Allow setting vendor info at runtime") ot_option(OT_OPERATIONAL_DATASET_AUTO_INIT OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT "operational dataset auto init") ot_option(OT_OTNS OPENTHREAD_CONFIG_OTNS_ENABLE "OTNS") +ot_option(OT_P2P OPENTHREAD_CONFIG_P2P_ENABLE "peer to peer") ot_option(OT_PING_SENDER OPENTHREAD_CONFIG_PING_SENDER_ENABLE "ping sender" ${OT_APP_CLI}) ot_option(OT_PLATFORM_BOOTLOADER_MODE OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE "platform bootloader mode") ot_option(OT_PLATFORM_DNSSD OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE "platform dnssd") diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index f27b1d19c..973606fde 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -111,6 +111,8 @@ source_set("openthread") { "platform/toolchain.h", "platform/trel.h", "platform/udp.h", + "provisional/link.h", + "provisional/p2p.h", "radio_stats.h", "random_crypto.h", "random_noncrypto.h", diff --git a/include/openthread/instance.h b/include/openthread/instance.h index edf47d572..dd2ea0edd 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (531) +#define OPENTHREAD_API_VERSION (532) /** * @addtogroup api-instance diff --git a/include/openthread/provisional/link.h b/include/openthread/provisional/link.h new file mode 100644 index 000000000..a55dd726b --- /dev/null +++ b/include/openthread/provisional/link.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * @brief + * This file defines the OpenThread provisional IEEE 802.15.4 Link Layer API. + */ +#ifndef OPENTHREAD_PROVISIONAL_LINK_H_ +#define OPENTHREAD_PROVISIONAL_LINK_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @addtogroup api-provisional-link + * + * @brief + * This module includes provisional functions that control link-layer configuration. + * + * @{ + */ + +/** + * Represents the wake-up identifier. + */ +typedef uint64_t otWakeupId; + +/** + * Represents the wake-up request type. + */ +typedef enum otWakeupType +{ + OT_WAKEUP_TYPE_EXT_ADDRESS = 0, ///< Wake up the peer by the extended address. + OT_WAKEUP_TYPE_IDENTIFIER = 1, ///< Wake up the peer by the wake-up identifier. + OT_WAKEUP_TYPE_GROUP_IDENTIFIER = 2, ///< Wake up peers by the group wake-up identifier. +} otWakeupType; + +/** + * Represents the request to wake up the peer. + */ +typedef struct otWakeupRequest +{ + union + { + otWakeupId mWakeupId; ///< Wake-up identifier of the Wake-up Listener. + otExtAddress mExtAddress; ///< IEEE 802.15.4 Extended Address of the Wake-up Listener. + } mShared; + + otWakeupType mType; ///< Indicates the wake-up request type (`OT_WAKEUP_TYPE_*` enumeration). +} otWakeupRequest; + +/** + * @} + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_PROVISIONAL_LINK_H_ diff --git a/include/openthread/provisional/p2p.h b/include/openthread/provisional/p2p.h new file mode 100644 index 000000000..8c9afca7e --- /dev/null +++ b/include/openthread/provisional/p2p.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * @brief + * This file defines the OpenThread provisional P2P (peer-to-peer) API. + */ + +#ifndef OPENTHREAD_PROVISIONAL_P2P_H_ +#define OPENTHREAD_PROVISIONAL_P2P_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @addtogroup api-provisional-p2p + * + * @brief + * This module includes provisional functions for the Thread P2P link. + * + * @note + * The functions in this module require `OPENTHREAD_CONFIG_P2P_ENABLE=1`. + * + * @{ + */ + +/** + * Represents a request for establishing P2P links. + */ +typedef struct otP2pRequest +{ + otWakeupRequest mWakeupRequest; ///< Wake-up request. +} otP2pRequest; + +/** + * Notifies the caller that the P2P link establishment process has ended. + * + * @param[in] aContext A pointer to the application-specific context. + */ +typedef void (*otP2pLinkDoneCallback)(void *aContext); + +/** + * Attempts to wake up peers and establish P2P links with peers. + * + * If the @p aP2pRequest indicates a group wake-up, this method establishes multiple P2P links with peers. + * Otherwise, it establishes at most one P2P link. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aP2pRequest A pointer to P2P request. + * @param[in] aCallback A pointer to the function that is called when the P2P link establishment process ends . + * @param[in] aContext A pointer to the callback application-specific context. + * + * @retval OT_ERROR_NONE Successfully started to establish P2P links. + * @retval OT_ERROR_BUSY Establishing a P2P link was in progress. + * @retval OT_ERROR_INVALID_STATE Device was disabled or not fully configured. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space to establish a P2P link. + */ +otError otP2pWakeupAndLink(otInstance *aInstance, + const otP2pRequest *aP2pRequest, + otP2pLinkDoneCallback aCallback, + void *aContext); + +/** + * Defines events of the P2P link. + */ +typedef enum otP2pEvent +{ + OT_P2P_EVENT_LINKED = 0, ///< The P2P link has been established. + OT_P2P_EVENT_UNLINKED = 1, ///< The P2P link has been torn down. +} otP2pEvent; + +/** + * Callback function pointer to signal events of the P2P link. + * + * @param[in] aEvent The P2P link event. + * @param[in] aExtAddress A pointer to the peer's Extended Address of the P2P link. + * @param[in] aContext A pointer to the application-specific context. + */ +typedef void (*otP2pEventCallback)(otP2pEvent aEvent, const otExtAddress *aExtAddress, void *aContext); + +/** + * Sets the callback function to notify event changes of P2P links. + * + * A subsequent call to this function will replace any previously set callback. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aCallback The callback function pointer. + * @param[in] aContext A pointer to the callback application-specific context. + */ +void otP2pSetEventCallback(otInstance *aInstance, otP2pEventCallback aCallback, void *aContext); + +/** + * @} + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_PROVISIONAL_P2P_H_ diff --git a/script/test b/script/test index 9fb11c475..2f481190f 100755 --- a/script/test +++ b/script/test @@ -135,6 +135,7 @@ build_simulation() options+=("-DOT_LINK_METRICS_INITIATOR=ON") options+=("-DOT_LINK_METRICS_SUBJECT=ON") options+=("-DOT_LINK_METRICS_MANAGER=ON") + options+=("-DOT_P2P=ON") options+=("-DOT_WAKEUP_COORDINATOR=ON") options+=("-DOT_WAKEUP_END_DEVICE=ON") fi @@ -194,6 +195,9 @@ build_posix() options+=("-DOT_LINK_METRICS_INITIATOR=ON") options+=("-DOT_LINK_METRICS_SUBJECT=ON") options+=("-DOT_LINK_METRICS_MANAGER=ON") + options+=("-DOT_P2P=ON") + options+=("-DOT_WAKEUP_COORDINATOR=ON") + options+=("-DOT_WAKEUP_END_DEVICE=ON") fi if [[ ${FULL_LOGS} == 1 ]]; then @@ -479,10 +483,14 @@ do_expect() test_patterns=(-name 'posix-*.exp' -o -name 'cli-*.exp') if [[ ${THREAD_VERSION} != "1.1" ]]; then test_patterns+=(-o -name 'v1_2-*.exp') + test_patterns+=(-o -name 'v1_5-*.exp') fi fi else test_patterns=(-name 'cli-*.exp' -o -name 'simulation-*.exp' -o -name 'cli_non_rcp-*.exp') + if [[ ${THREAD_VERSION} != "1.1" ]]; then + test_patterns+=(-o -name 'v1_5-*.exp') + fi fi if [[ $# != 0 ]]; then diff --git a/src/cli/README.md b/src/cli/README.md index cb57d90f2..5518a608c 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -89,6 +89,7 @@ Done - [networkname](#networkname) - [networktime](#networktime) - [nexthop](#nexthop) +- [p2p](#p2p-link-extaddr-extaddr) - [panid](#panid) - [parent](#parent) - [parentpriority](#parentpriority) @@ -3113,6 +3114,17 @@ nexthop 0x8001 Done ``` +### p2p link extaddr \ + +Wakes up the peer identified by the extended address and establishes a peer-to-peer link with the peer. + +`OPENTHREAD_CONFIG_P2P_ENABLE` and `OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE` are required. + +```bash +> p2p link extaddr dead00beef00cafe +Done +``` + ### panid Get the IEEE 802.15.4 PAN ID value. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index a4b9654f4..28eb69d40 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -8088,6 +8088,58 @@ exit: #endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE +#if OPENTHREAD_CONFIG_P2P_ENABLE && OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgs[0] == "link") + { + otP2pRequest p2pRequest; + + /** + * @cli link + * @code + * link extaddr dead00beef00cafe + * Done + * @endcode + * @cparam link extaddr @ca{extended-address} + * @par + * `OPENTHREAD_CONFIG_P2P_ENABLE` and `OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE` are required. + * @par + * Wakes up the Wake-up Listener identified by the extended address and establishes a peer-to-peer link with the + * peer. + */ + if (aArgs[1] == "extaddr") + { + SuccessOrExit(error = aArgs[2].ParseAsHexString(p2pRequest.mWakeupRequest.mShared.mExtAddress.m8)); + p2pRequest.mWakeupRequest.mType = OT_WAKEUP_TYPE_EXT_ADDRESS; + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + + SuccessOrExit(error = otP2pWakeupAndLink(GetInstancePtr(), &p2pRequest, HandleP2pLinkedResult, this)); + error = OT_ERROR_PENDING; + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + +exit: + return error; +} + +void Interpreter::HandleP2pLinkedResult(void *aContext) +{ + static_cast(aContext)->HandleP2pLinkedResult(); +} + +void Interpreter::HandleP2pLinkedResult(void) { OutputResult(OT_ERROR_NONE); } +#endif // OPENTHREAD_CONFIG_P2P_ENABLE && OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { @@ -8436,6 +8488,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #endif #if OPENTHREAD_FTD CmdEntry("nexthop"), +#endif +#if OPENTHREAD_CONFIG_P2P_ENABLE && OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + CmdEntry("p2p"), #endif CmdEntry("panid"), CmdEntry("parent"), diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 46d714577..6e80d6c66 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -310,6 +310,11 @@ private: static void HandleIp6Receive(otMessage *aMessage, void *aContext); #endif +#if OPENTHREAD_CONFIG_P2P_ENABLE && OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + static void HandleP2pLinkedResult(void *aContext); + void HandleP2pLinkedResult(void); +#endif + #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE static void HandleWakeupResult(otError aError, void *aContext); void HandleWakeupResult(otError aError); diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index b8ed6a513..771391dc7 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -345,6 +345,7 @@ openthread_core_files = [ "api/netdata_publisher_api.cpp", "api/netdiag_api.cpp", "api/network_time_api.cpp", + "api/p2p_api.cpp", "api/ping_sender_api.cpp", "api/radio_stats_api.cpp", "api/random_crypto_api.cpp", @@ -671,6 +672,7 @@ openthread_core_files = [ "thread/mle.cpp", "thread/mle.hpp", "thread/mle_ftd.cpp", + "thread/mle_p2p.cpp", "thread/mle_tlvs.cpp", "thread/mle_tlvs.hpp", "thread/mle_types.cpp", @@ -705,6 +707,10 @@ openthread_core_files = [ "thread/network_diagnostic_tlvs.hpp", "thread/panid_query_server.cpp", "thread/panid_query_server.hpp", + "thread/peer.cpp", + "thread/peer.hpp", + "thread/peer_table.cpp", + "thread/peer_table.hpp", "thread/radio_selector.cpp", "thread/radio_selector.hpp", "thread/router.cpp", @@ -845,6 +851,7 @@ source_set("libopenthread_core_config") { "config/netdata_publisher.h", "config/network_diagnostic.h", "config/openthread-core-config-check.h", + "config/p2p.h", "config/parent_search.h", "config/ping_sender.h", "config/platform.h", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c88f371e8..85632a0a7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -71,6 +71,7 @@ set(COMMON_SOURCES api/netdata_publisher_api.cpp api/netdiag_api.cpp api/network_time_api.cpp + api/p2p_api.cpp api/ping_sender_api.cpp api/radio_stats_api.cpp api/random_crypto_api.cpp @@ -230,6 +231,7 @@ set(COMMON_SOURCES thread/message_framer.cpp thread/mle.cpp thread/mle_ftd.cpp + thread/mle_p2p.cpp thread/mle_tlvs.cpp thread/mle_types.cpp thread/mlr_manager.cpp @@ -247,6 +249,8 @@ set(COMMON_SOURCES thread/network_diagnostic.cpp thread/network_diagnostic_tlvs.cpp thread/panid_query_server.cpp + thread/peer.cpp + thread/peer_table.cpp thread/radio_selector.cpp thread/router.cpp thread/router_table.cpp diff --git a/src/core/api/p2p_api.cpp b/src/core/api/p2p_api.cpp new file mode 100644 index 000000000..3d48d5e89 --- /dev/null +++ b/src/core/api/p2p_api.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * This file implements the OpenThread Peer-to-Peer API. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_P2P_ENABLE + +#include "instance/instance.hpp" + +using namespace ot; + +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +otError otP2pWakeupAndLink(otInstance *aInstance, + const otP2pRequest *aP2pRequest, + otP2pLinkDoneCallback aCallback, + void *aContext) +{ + return AsCoreType(aInstance).Get().P2pWakeupAndLink(AsCoreType(aP2pRequest), aCallback, aContext); +} +#endif + +void otP2pSetEventCallback(otInstance *aInstance, otP2pEventCallback aCallback, void *aContext) +{ + AsCoreType(aInstance).Get().P2pSetEventCallback(aCallback, aContext); +} +#endif diff --git a/src/core/config/p2p.h b/src/core/config/p2p.h new file mode 100644 index 000000000..9f3debb7d --- /dev/null +++ b/src/core/config/p2p.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * This file includes compile-time configurations for the peer-to-peer (P2P). + */ + +#ifndef CONFIG_P2P_H_ +#define CONFIG_P2P_H_ + +/** + * @addtogroup config-p2p + * + * @brief + * This module includes configuration variables for the peer-to-peer (P2P). + * + * @{ + */ + +/** + * @def OPENTHREAD_CONFIG_P2P_ENABLE + * + * Define to 1 to enable the P2P feature. + */ +#ifndef OPENTHREAD_CONFIG_P2P_ENABLE +#define OPENTHREAD_CONFIG_P2P_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_P2P_MAX_PEERS + * + * The maximum number of P2P peers. + */ +#ifndef OPENTHREAD_CONFIG_P2P_MAX_PEERS +#define OPENTHREAD_CONFIG_P2P_MAX_PEERS 10 +#endif + +/** + * @} + */ + +#endif // CONFIG_P2P_H_ diff --git a/src/core/config/wakeup.h b/src/core/config/wakeup.h index cb6fc7e65..08636f782 100644 --- a/src/core/config/wakeup.h +++ b/src/core/config/wakeup.h @@ -89,6 +89,24 @@ #define OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_WAKEUP_TX_INTERVAL + * + * The default time interval between (start of) wake-up frame transmissions in microseconds. + */ +#ifndef OPENTHREAD_CONFIG_WAKEUP_TX_INTERVAL +#define OPENTHREAD_CONFIG_WAKEUP_TX_INTERVAL 7500 +#endif + +/** + * @def OPENTHREAD_CONFIG_WAKEUP_MAX_DURATION + * + * The default duration of time that wake-up frames are periodically transmitted in milliseconds. + */ +#ifndef OPENTHREAD_CONFIG_WAKEUP_MAX_DURATION +#define OPENTHREAD_CONFIG_WAKEUP_MAX_DURATION 1090 +#endif + /** * @def OPENTHREAD_CONFIG_WED_LISTEN_INTERVAL * diff --git a/src/core/instance/instance.hpp b/src/core/instance/instance.hpp index b3f19f8fc..858a45ead 100644 --- a/src/core/instance/instance.hpp +++ b/src/core/instance/instance.hpp @@ -788,6 +788,10 @@ template <> inline ChildTable &Instance::Get(void) { return mMle.mChildTable; } template <> inline RouterTable &Instance::Get(void) { return mMle.mRouterTable; } #endif +#if OPENTHREAD_CONFIG_P2P_ENABLE +template <> inline PeerTable &Instance::Get(void) { return mMle.mP2p.mPeerTable; } +#endif + #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE template <> inline WakeupTxScheduler &Instance::Get(void) { return mMle.mWakeupTxScheduler; } #endif diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 9dcfccea4..edc455531 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -2699,18 +2699,22 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame) { Error error = kErrorNone; const ConnectionIe *connectionIe; + Address srcAddress; + WakeupInfo wakeupInfo; uint32_t rvTimeUs; uint64_t rvTimestampUs; - uint32_t attachDelayMs; uint64_t radioNowUs; - uint8_t retryInterval; - uint8_t retryCount; VerifyOrExit(mWakeupListenEnabled && aFrame.IsWakeupFrame()); - connectionIe = aFrame.GetConnectionIe(); - retryInterval = connectionIe->GetRetryInterval(); - retryCount = connectionIe->GetRetryCount(); - VerifyOrExit(retryInterval > 0 && retryCount > 0, error = kErrorInvalidArgs); + + SuccessOrExit(error = aFrame.GetSrcAddr(srcAddress)); + VerifyOrExit(srcAddress.IsExtended(), error = kErrorDrop); + + wakeupInfo.mExtAddress = srcAddress.GetExtended(); + connectionIe = aFrame.GetConnectionIe(); + wakeupInfo.mRetryInterval = connectionIe->GetRetryInterval(); + wakeupInfo.mRetryCount = connectionIe->GetRetryCount(); + VerifyOrExit(wakeupInfo.mRetryInterval > 0 && wakeupInfo.mRetryCount > 0, error = kErrorInvalidArgs); radioNowUs = otPlatRadioGetNow(&GetInstance()); rvTimeUs = aFrame.GetRendezvousTimeIe()->GetRendezvousTime() * kUsPerTenSymbols; @@ -2718,12 +2722,12 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame) if (rvTimestampUs > radioNowUs + kCslRequestAhead) { - attachDelayMs = static_cast(rvTimestampUs - radioNowUs - kCslRequestAhead); - attachDelayMs = attachDelayMs / 1000; + wakeupInfo.mAttachDelayMs = static_cast(rvTimestampUs - radioNowUs - kCslRequestAhead); + wakeupInfo.mAttachDelayMs = wakeupInfo.mAttachDelayMs / Time::kOneMsecInUsec; } else { - attachDelayMs = 0; + wakeupInfo.mAttachDelayMs = 0; } #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) @@ -2732,15 +2736,14 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame) IgnoreError(aFrame.GetFrameCounter(frameCounter)); LogInfo("Received wake-up frame, fc:%lu, rendezvous:%luus, retries:%u/%u", ToUlong(frameCounter), - ToUlong(rvTimeUs), retryCount, retryInterval); + ToUlong(rvTimeUs), wakeupInfo.mRetryCount, wakeupInfo.mRetryInterval); } #endif // Stop receiving more wake up frames IgnoreError(SetWakeupListenEnabled(false)); - // TODO: start MLE attach process with the WC - OT_UNUSED_VARIABLE(attachDelayMs); + Get().HandleWakeupFrame(wakeupInfo); exit: return error; diff --git a/src/core/mac/mac_types.cpp b/src/core/mac/mac_types.cpp index 9944623c4..cef4570c0 100644 --- a/src/core/mac/mac_types.cpp +++ b/src/core/mac/mac_types.cpp @@ -404,5 +404,24 @@ bool KeyMaterial::operator==(const KeyMaterial &aOther) const #endif } +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +void WakeupRequest::SetExtAddress(const ExtAddress &aExtAddress) +{ + SetType(kTypeExtAddress); + aExtAddress.CopyTo(mShared.mExtAddress.m8); +} + +const ExtAddress &WakeupRequest::GetExtAddress(void) const { return AsCoreType(&mShared.mExtAddress); } + +ExtAddress &WakeupRequest::GetExtAddress(void) { return AsCoreType(&mShared.mExtAddress); } + +void WakeupRequest::SetType(Type aType) { mType = MapEnum(aType); } + +bool WakeupRequest::IsWakeupByExtAddress(void) const { return MapEnum(mType) == kTypeExtAddress; } + +bool WakeupRequest::IsWakeupById(void) const { return MapEnum(mType) == kTypeWakeupId; } + +bool WakeupRequest::IsWakeupByGroupId(void) const { return MapEnum(mType) == kTypeGroupWakeupId; } +#endif } // namespace Mac } // namespace ot diff --git a/src/core/mac/mac_types.hpp b/src/core/mac/mac_types.hpp index e7b2a59b6..657b5653f 100644 --- a/src/core/mac/mac_types.hpp +++ b/src/core/mac/mac_types.hpp @@ -41,6 +41,7 @@ #include #include +#include #include "common/as_core_type.hpp" #include "common/clearable.hpp" @@ -959,6 +960,96 @@ private: uint8_t mUncertainty; }; +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +/** + * Represents a wake-up request. + */ +class WakeupRequest : public otWakeupRequest +{ +public: + /** + * Represents a wake-up request type. + */ + enum Type : uint8_t + { + kTypeExtAddress = OT_WAKEUP_TYPE_EXT_ADDRESS, + kTypeWakeupId = OT_WAKEUP_TYPE_IDENTIFIER, + kTypeGroupWakeupId = OT_WAKEUP_TYPE_GROUP_IDENTIFIER, + }; + + /** + * Sets the wake-up request with an Extended Address. + * + * The type is also updated to indicate that the wake-up request type is `kTypeExtAddress`. + * + * @param[in] aExtAddress An Extended Address. + */ + void SetExtAddress(const ExtAddress &aExtAddress); + + /** + * Gets the Extended Address of the wake-up request. + * + * MUST be used only if the wake-up request type is `kTypeExtAddress`. + * + * @returns A constant reference to the Extended Address. + */ + const ExtAddress &GetExtAddress(void) const; + + /** + * Gets the Extended Address of the wake-up request. + * + * MUST be used only if the wake-up request type is `kTypeExtAddress`. + * + * @returns A reference to the Extended Address. + */ + ExtAddress &GetExtAddress(void); + + /** + * Sets the wake-up request type. + * + * @param[in] aType The wake-up request type. + */ + void SetType(Type aType); + + /** + * Indicates whether the peer is set to be woken up by the extended address. + * + * @retval TRUE If the peer is set to be woken up by the extended address. + * @retval FALSE If the peer is not not set to be woken up by the extended address. + */ + bool IsWakeupByExtAddress(void) const; + + /** + * Indicates whether the peer is set to be woken up by the wake-up identifier. + * + * @retval TRUE If the peer is set to be woken up by the wake-up identifier. + * @retval FALSE If the peer is not set to be woken up by the wake-up identifier. + */ + bool IsWakeupById(void) const; + + /** + * Indicates whether the peer is set to be woken up by the group wake-up identifier. + * + * @retval TRUE If the peer is set to be woken up by the group wake-up identifier. + * @retval FALSE If the peer is not set to be woken up by the group wake-up identifier. + */ + bool IsWakeupByGroupId(void) const; +}; +#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +/** + * Represents the information of the received wake-up frame. + */ +struct WakeupInfo +{ + ExtAddress mExtAddress; ///< The extended address of the Wake-up Coordinator. + uint32_t mAttachDelayMs; ///< The delay before linking to the peer. + uint8_t mRetryInterval : 2; ///< The interval of the periodic connection windows. + uint8_t mRetryCount : 4; ///< The maximum number of retries the action by the Wake-up Listener. +}; +#endif + /** * @} */ @@ -967,6 +1058,10 @@ private: DefineCoreType(otExtAddress, Mac::ExtAddress); DefineCoreType(otMacKey, Mac::Key); +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +DefineCoreType(otWakeupRequest, Mac::WakeupRequest); +DefineMapEnum(otWakeupType, Mac::WakeupRequest::Type); +#endif } // namespace ot diff --git a/src/core/mac/wakeup_tx_scheduler.cpp b/src/core/mac/wakeup_tx_scheduler.cpp index 2ae158f16..a0a1a8dde 100644 --- a/src/core/mac/wakeup_tx_scheduler.cpp +++ b/src/core/mac/wakeup_tx_scheduler.cpp @@ -51,19 +51,21 @@ WakeupTxScheduler::WakeupTxScheduler(Instance &aInstance) UpdateFrameRequestAhead(); } -Error WakeupTxScheduler::WakeUp(const Mac::ExtAddress &aWedAddress, uint16_t aIntervalUs, uint16_t aDurationMs) +Error WakeupTxScheduler::WakeUp(const Mac::WakeupRequest &aWakeupRequest, uint16_t aIntervalUs, uint16_t aDurationMs) { Error error = kErrorNone; VerifyOrExit(!mIsRunning, error = kErrorInvalidState); + // TODO: Add support for wake-up identifiers. + VerifyOrExit(aWakeupRequest.IsWakeupByExtAddress(), error = kErrorInvalidState); - mWedAddress = aWedAddress; - mTxTimeUs = TimerMicro::GetNow() + mTxRequestAheadTimeUs; - mTxEndTimeUs = mTxTimeUs + aDurationMs * Time::kOneMsecInUsec + aIntervalUs; - mIntervalUs = aIntervalUs; - mIsRunning = true; + mWakeupRequest = aWakeupRequest; + mTxTimeUs = TimerMicro::GetNow() + mTxRequestAheadTimeUs; + mTxEndTimeUs = mTxTimeUs + aDurationMs * Time::kOneMsecInUsec + aIntervalUs; + mIntervalUs = aIntervalUs; + mIsRunning = true; - LogInfo("Started wake-up sequence to %s", aWedAddress.ToString().AsCString()); + LogInfo("Started wake-up sequence to %s", aWakeupRequest.GetExtAddress().ToString().AsCString()); ScheduleTimer(); @@ -87,7 +89,7 @@ Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames) VerifyOrExit(mIsRunning); - target.SetExtended(mWedAddress); + target.SetExtended(mWakeupRequest.GetExtAddress()); source.SetExtended(Get().GetExtAddress()); VerifyOrExit(mTxTimeUs >= nowUs); radioTxDelay = mTxTimeUs - nowUs; diff --git a/src/core/mac/wakeup_tx_scheduler.hpp b/src/core/mac/wakeup_tx_scheduler.hpp index 650934a23..aec94c8d0 100644 --- a/src/core/mac/wakeup_tx_scheduler.hpp +++ b/src/core/mac/wakeup_tx_scheduler.hpp @@ -60,14 +60,14 @@ public: /** * Initiates the wake-up sequence to a Wake-up End Device. * - * @param[in] aWedAddress The extended address of the Wake-up End Device. - * @param[in] aIntervalUs An interval between consecutive wake-up frames (in microseconds). - * @param[in] aDurationMs Duration of the wake-up sequence (in milliseconds). + * @param[in] aWakeupRequest The wake-up request. + * @param[in] aIntervalUs An interval between consecutive wake-up frames (in microseconds). + * @param[in] aDurationMs Duration of the wake-up sequence (in milliseconds). * * @retval kErrorNone Successfully started the wake-up sequence. * @retval kErrorInvalidState This or another device is currently being woken-up. */ - Error WakeUp(const Mac::ExtAddress &aWedAddress, uint16_t aIntervalUs, uint16_t aDurationMs); + Error WakeUp(const Mac::WakeupRequest &aWakeupRequest, uint16_t aIntervalUs, uint16_t aDurationMs); /** * Returns the connection window used by this device. @@ -99,6 +99,11 @@ public: */ void UpdateFrameRequestAhead(void); + /** + * Returns the wake-up request. + */ + const Mac::WakeupRequest &GetWakeupRequest(void) const { return mWakeupRequest; } + private: constexpr static uint8_t kConnectionRetryInterval = OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_INTERVAL; constexpr static uint8_t kConnectionRetryCount = OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_COUNT; @@ -116,13 +121,13 @@ private: using WakeupTimer = TimerMicroIn; - Mac::ExtAddress mWedAddress; - TimeMicro mTxTimeUs; // Point in time when the next TX occurs. - TimeMicro mTxEndTimeUs; // Point in time when the wake-up sequence is over. - uint32_t mTxRequestAheadTimeUs; // How much ahead the TX MAC operation needs to be requested. - uint16_t mIntervalUs; // Interval between consecutive wake-up frames. - WakeupTimer mTimer; - bool mIsRunning; + Mac::WakeupRequest mWakeupRequest; + TimeMicro mTxTimeUs; // Point in time when the next TX occurs. + TimeMicro mTxEndTimeUs; // Point in time when the wake-up sequence is over. + uint32_t mTxRequestAheadTimeUs; // How much ahead the TX MAC operation needs to be requested. + uint16_t mIntervalUs; // Interval between consecutive wake-up frames. + WakeupTimer mTimer; + bool mIsRunning; }; } // namespace ot diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h index 18ac37e0f..7c44167b1 100644 --- a/src/core/openthread-core-config.h +++ b/src/core/openthread-core-config.h @@ -97,6 +97,7 @@ #include "config/nat64.h" #include "config/netdata_publisher.h" #include "config/network_diagnostic.h" +#include "config/p2p.h" #include "config/parent_search.h" #include "config/ping_sender.h" #include "config/platform.h" diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index d9f410246..823180462 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -107,6 +107,9 @@ Mle::Mle(Instance &aInstance) , mChildTable(aInstance) , mRouterTable(aInstance) #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_P2P_ENABLE + , mP2p(aInstance) +#endif { mParent.Init(aInstance); @@ -1761,6 +1764,24 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn break; #endif +#if OPENTHREAD_CONFIG_P2P_ENABLE +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + case kCommandP2pLinkRequest: + mP2p.HandleP2pLinkRequest(rxInfo); + break; + + case kCommandP2pLinkAccept: + mP2p.HandleP2pLinkAccept(rxInfo); + break; +#endif + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + case kCommandP2pLinkAcceptAndRequest: + mP2p.HandleP2pLinkAcceptAndRequest(rxInfo); + break; +#endif +#endif // OPENTHREAD_CONFIG_P2P_ENABLE + default: ExitNow(error = kErrorDrop); } @@ -2869,6 +2890,11 @@ const char *Mle::MessageTypeToString(MessageType aType) #endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE "Time Sync", // (31) kTypeTimeSync +#endif +#if OPENTHREAD_CONFIG_P2P_ENABLE + "P2P Link Request", // (32) kTypeP2pLinkRequest + "P2P Link Accept and Request", // (33) kTypeP2pLinkAcceptAndRequest + "P2P Link Accept", // (34) kTypeP2pLinkAccept #endif }; @@ -2912,6 +2938,11 @@ const char *Mle::MessageTypeToString(MessageType aType) #endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE ValidateNextEnum(kTypeTimeSync); +#endif +#if OPENTHREAD_CONFIG_P2P_ENABLE + ValidateNextEnum(kTypeP2pLinkRequest); + ValidateNextEnum(kTypeP2pLinkAcceptAndRequest); + ValidateNextEnum(kTypeP2pLinkAccept); #endif }; @@ -3025,13 +3056,15 @@ Error Mle::Wakeup(const Mac::ExtAddress &aWedAddress, WakeupCallback aCallback, void *aCallbackContext) { - Error error; + Error error = kErrorNone; + Mac::WakeupRequest wakeupRequest; VerifyOrExit((aIntervalUs > 0) && (aDurationMs > 0), error = kErrorInvalidArgs); VerifyOrExit(aIntervalUs < aDurationMs * Time::kOneMsecInUsec, error = kErrorInvalidArgs); VerifyOrExit(mWedAttachState == kWedDetached, error = kErrorInvalidState); - SuccessOrExit(error = mWakeupTxScheduler.WakeUp(aWedAddress, aIntervalUs, aDurationMs)); + wakeupRequest.SetExtAddress(aWedAddress); + SuccessOrExit(error = mWakeupTxScheduler.WakeUp(wakeupRequest, aIntervalUs, aDurationMs)); mWedAttachState = kWedAttaching; mWakeupCallback.Set(aCallback, aCallbackContext); @@ -3045,6 +3078,17 @@ exit: } #endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +void Mle::HandleWakeupFrame(const Mac::WakeupInfo &aWakeupInfo) +{ + OT_UNUSED_VARIABLE(aWakeupInfo); + +#if OPENTHREAD_CONFIG_P2P_ENABLE + mP2p.HandleP2pWakeup(aWakeupInfo); +#endif +} +#endif + //--------------------------------------------------------------------------------------------------------------------- // TlvList diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 90a014b96..b1eaf0980 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -37,8 +37,10 @@ #include "openthread-core-config.h" #include +#include #include "coap/coap_message.hpp" +#include "common/as_core_type.hpp" #include "common/callback.hpp" #include "common/encoding.hpp" #include "common/locator.hpp" @@ -67,6 +69,8 @@ #include "thread/mle_types.hpp" #include "thread/neighbor_table.hpp" #include "thread/network_data_types.hpp" +#include "thread/peer.hpp" +#include "thread/peer_table.hpp" #include "thread/router.hpp" #include "thread/router_table.hpp" #include "thread/thread_tlvs.hpp" @@ -130,8 +134,9 @@ class Mle : public InstanceLocator, private NonCopyable public: typedef otDetachGracefullyCallback DetachCallback; ///< Callback to signal end of graceful detach. - - typedef otWakeupCallback WakeupCallback; ///< Callback to communicate the result of waking a Wake-up End Device + typedef otP2pLinkDoneCallback P2pLinkDoneCallback; ///< Callback to inform the result of establishing P2P links. + typedef otP2pEventCallback P2pEventCallback; ///< Callback to signal events of the P2P link. + typedef otWakeupCallback WakeupCallback; ///< Callback to communicate the result of waking a Wake-up End Device /** * Initializes the MLE object. @@ -1158,6 +1163,49 @@ public: #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_P2P_ENABLE +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + /** + * Attempts to wake up peers and establish P2P links with peers. + * + * If the @p aP2pRequest indicates a group identifier, this method establishes multiple P2P links with peers. + * Otherwise, it establishes at most one P2P link. + * + * @param[in] aP2pRequest A constant reference to the P2P request. + * @param[in] aCallback A pointer to the function that is called when the P2P link succeeds or fails. + * @param[in] aContext A pointer to the callback application-specific context. + * + * @retval kErrorNone Successfully started to establish P2P links. + * @retval kErrorBusy Establishing a P2P link in progress. + * @retval kErrorInvalidState Device was disabled or not fully configured. + * @retval kErrorNoBufs Insufficient buffer space to establish a P2P link. + */ + Error P2pWakeupAndLink(const P2pRequest &aP2pRequest, P2pLinkDoneCallback aCallback, void *aContext) + { + return mP2p.WakeupAndLink(aP2pRequest, aCallback, aContext); + } +#endif + + /** + * Sets the callback function to notify event changes of P2P links. + * + * A subsequent call to this function will replace any previously set callback. + * + * @param[in] aCallback The callback function pointer. + * @param[in] aContext A pointer to the callback application-specific context. + */ + void P2pSetEventCallback(P2pEventCallback aCallback, void *aContext) { mP2p.SetEventCallback(aCallback, aContext); } +#endif // OPENTHREAD_CONFIG_P2P_ENABLE + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** + * Notifies MLE that a wake-up frame was received successfully. + * + * @param[in] aWakeupInfo A reference to the wake-up frame information. + */ + void HandleWakeupFrame(const Mac::WakeupInfo &aWakeupInfo); +#endif + private: //------------------------------------------------------------------------------------------------------------------ // Constants @@ -1420,6 +1468,11 @@ private: #endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE kTypeTimeSync, +#endif +#if OPENTHREAD_CONFIG_P2P_ENABLE + kTypeP2pLinkRequest, + kTypeP2pLinkAcceptAndRequest, + kTypeP2pLinkAccept, #endif }; @@ -1574,7 +1627,9 @@ private: Mac::ExtAddress mChildExtAddress; // The child extended address. RxChallenge mRxChallenge; // The challenge from the Parent Request. }; +#endif +#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_P2P_ENABLE struct LinkAcceptInfo { Mac::ExtAddress mExtAddress; // The neighbor/router extended address. @@ -1582,7 +1637,9 @@ private: RxChallenge mRxChallenge; // The challenge in Link Request. uint8_t mLinkMargin; // Link margin of the received Link Request. }; +#endif +#if OPENTHREAD_FTD struct DiscoveryResponseInfo { Mac::PanId mPanId; @@ -2090,6 +2147,69 @@ private: uint8_t mJitter; }; +#endif + + //------------------------------------------------------------------------------------------------------------------ +#if OPENTHREAD_CONFIG_P2P_ENABLE + void HandleP2pLinkTimer(void) { mP2p.HandleLinkTimer(); } + + class P2p : public InstanceLocator + { + friend class ot::Instance; + + public: + P2p(Instance &aInstance); + +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + Error WakeupAndLink(const P2pRequest &aP2pRequest, P2pLinkDoneCallback aCallback, void *aContext); + void HandleP2pLinkRequest(RxInfo &aRxInfo); + void HandleP2pLinkAccept(RxInfo &aRxInfo); +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + void HandleP2pWakeup(const Mac::WakeupInfo &aWakeupInfo); + void HandleP2pLinkAcceptAndRequest(RxInfo &aRxInfo); +#endif + + void SetEventCallback(P2pEventCallback aCallback, void *aContext); + void HandleLinkTimer(void); + + private: + static constexpr uint16_t kWakeupMaxDuration = OPENTHREAD_CONFIG_WAKEUP_MAX_DURATION; + static constexpr uint16_t kWakeupTxInterval = OPENTHREAD_CONFIG_WAKEUP_TX_INTERVAL; + static constexpr uint32_t kEstablishP2pLinkTimeoutUs = 500000; + + enum State : uint8_t + { + kStateIdle, + kStateWakingUp, + kStateWaitingLinkAccept, + kStateAttachDelay, + kStateWaitingLinkAcceptAndRequest, + }; + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + void SendP2pLinkRequest(Peer *aPeer); + Error SendP2pLinkAccept(const LinkAcceptInfo &aInfo); +#endif + +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + Error SendP2pLinkAcceptAndRequest(const LinkAcceptInfo &aInfo); +#endif + + Error SendP2pLinkAcceptVariant(const LinkAcceptInfo &aInfo, bool aIsLinkAcceptorRequest); + void HandleP2pLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType); + void SetWakeupListenerEnabled(void); + void ClearPeersInLinkRequestState(void); + + using P2pLinkTimer = TimerMicroIn; + + State mState; + PeerTable mPeerTable; + P2pLinkTimer mTimer; + Callback mLinkedCallback; + Callback mEventCallback; + Peer *mPeer; + }; #endif //------------------------------------------------------------------------------------------------------------------ @@ -2374,6 +2494,10 @@ private: Callback mDiscoveryRequestCallback; #endif // OPENTHREAD_FTD + +#if OPENTHREAD_CONFIG_P2P_ENABLE + P2p mP2p; +#endif }; #if OPENTHREAD_FTD diff --git a/src/core/thread/mle_p2p.cpp b/src/core/thread/mle_p2p.cpp new file mode 100644 index 000000000..5dd5c56ef --- /dev/null +++ b/src/core/thread/mle_p2p.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * This file implements MLE functionality required for the Thread peer-to-peer (P2P) link. + */ + +#include "mle.hpp" + +#if OPENTHREAD_CONFIG_P2P_ENABLE +#if !(OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE) +#error "OPENTHREAD_CONFIG_P2P_ENABLE requires OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE or "\ + "OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE" +#endif + +#include "common/code_utils.hpp" +#include "instance/instance.hpp" +#include "openthread/platform/toolchain.h" + +namespace ot { +namespace Mle { + +RegisterLogModule("P2p"); + +Mle::P2p::P2p(Instance &aInstance) + : InstanceLocator(aInstance) + , mState(kStateIdle) + , mPeerTable(aInstance) + , mTimer(aInstance) + , mLinkedCallback() + , mEventCallback() + , mPeer(nullptr) +{ +} + +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +Error Mle::P2p::WakeupAndLink(const P2pRequest &aP2pRequest, P2pLinkDoneCallback aCallback, void *aContext) +{ + Error error = kErrorNone; + + VerifyOrExit(!Get().GetPromiscuous(), error = kErrorInvalidState); + VerifyOrExit(Get().IsUp(), error = kErrorInvalidState); + VerifyOrExit(!Get().IsDisabled(), error = kErrorInvalidState); + VerifyOrExit(mState == kStateIdle, error = kErrorBusy); + VerifyOrExit(!mPeerTable.IsFull(), error = kErrorNoBufs); + + // TODO: support rx-off-when-idle device later. + VerifyOrExit(Get().IsRxOnWhenIdle(), error = kErrorInvalidState); + + SuccessOrExit( + error = Get().WakeUp(aP2pRequest.GetWakeupRequest(), kWakeupTxInterval, kWakeupMaxDuration)); + + mState = kStateWakingUp; + mLinkedCallback.Set(aCallback, aContext); + mTimer.FireAt(Get().GetTxEndTime() + Get().GetConnectionWindowUs()); + +exit: + return error; +} + +void Mle::P2p::HandleP2pLinkRequest(RxInfo &aRxInfo) +{ + Error error = kErrorNone; + LinkAcceptInfo info; + DeviceMode mode; + Peer *peer; + uint16_t version; + + Log(kMessageReceive, kTypeP2pLinkRequest, aRxInfo.mMessageInfo.GetPeerAddr()); + VerifyOrExit(mState == kStateWakingUp, error = kErrorInvalidState); + VerifyOrExit(aRxInfo.mMessageInfo.GetPeerAddr().IsLinkLocalUnicast(), error = kErrorDrop); + + info.mExtAddress.SetFromIid(aRxInfo.mMessageInfo.GetPeerAddr().GetIid()); + VerifyOrExit(Get().FindNeighbor(info.mExtAddress, Neighbor::kInStateAnyExceptInvalid) == nullptr, + error = kErrorDrop); + + SuccessOrExit(error = aRxInfo.mMessage.ReadModeTlv(mode)); + // TODO: support the rx-off-when-idle device later. + VerifyOrExit(mode.IsRxOnWhenIdle(), error = kErrorDrop); + + SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(info.mRxChallenge)); + SuccessOrExit(error = aRxInfo.mMessage.ReadVersionTlv(version)); + + Get().ProcessKeySequence(aRxInfo); + + VerifyOrExit((peer = mPeerTable.GetNewPeer()) != nullptr, error = kErrorNoBufs); + + Get().InitNeighbor(*peer, aRxInfo); + peer->SetDeviceMode(mode); + peer->SetVersion(version); + peer->SetState(Neighbor::kStateLinkRequest); + + SuccessOrExit(error = SendP2pLinkAcceptAndRequest(info)); + Get().Stop(); + + mState = kStateWaitingLinkAccept; + mTimer.Start(kEstablishP2pLinkTimeoutUs); + +exit: + LogProcessError(kTypeP2pLinkRequest, error); +} + +Error Mle::P2p::SendP2pLinkAcceptAndRequest(const LinkAcceptInfo &aInfo) +{ + return SendP2pLinkAcceptVariant(aInfo, true /* aIsLinkAcceptAndRequest*/); +} + +void Mle::P2p::HandleP2pLinkAccept(RxInfo &aRxInfo) { HandleP2pLinkAcceptVariant(aRxInfo, kTypeP2pLinkAccept); } +#endif + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +void Mle::P2p::HandleP2pWakeup(const Mac::WakeupInfo &aWakeupInfo) +{ + Peer *peer = nullptr; + + VerifyOrExit(mState == kStateIdle); + peer = mPeerTable.FindPeer(aWakeupInfo.mExtAddress, Peer::kInStateAnyExceptInvalid); + VerifyOrExit(peer == nullptr); + + peer = mPeerTable.GetNewPeer(); + VerifyOrExit(peer != nullptr); + + peer->GetLinkInfo().Clear(); + peer->ResetLinkFailures(); + peer->SetLastHeard(TimerMilli::GetNow()); + peer->SetExtAddress(aWakeupInfo.mExtAddress); + peer->SetState(Neighbor::kStateRestored); + + mPeer = peer; + mState = kStateAttachDelay; + mTimer.Start(aWakeupInfo.mAttachDelayMs * Time::kOneMsecInUsec); + +exit: + if (peer == nullptr) + { + SetWakeupListenerEnabled(); + } + + return; +} + +void Mle::P2p::SendP2pLinkRequest(Peer *aPeer) +{ + Error error = kErrorNone; + TxMessage *message = nullptr; + Ip6::Address destination; + + VerifyOrExit(aPeer != nullptr, error = kErrorInvalidArgs); + VerifyOrExit((message = Get().NewMleMessage(kCommandP2pLinkRequest)) != nullptr, error = kErrorNoBufs); + SuccessOrExit(error = message->AppendModeTlv(Get().GetDeviceMode())); + SuccessOrExit(error = message->AppendVersionTlv()); + + aPeer->GenerateChallenge(); + SuccessOrExit(error = message->AppendChallengeTlv(aPeer->GetChallenge())); + + aPeer->GetLinkLocalIp6Address(destination); + SuccessOrExit(error = message->SendTo(destination)); + aPeer->SetState(Neighbor::kStateLinkRequest); + + mState = kStateWaitingLinkAcceptAndRequest; + mTimer.Start(kEstablishP2pLinkTimeoutUs); + + Log(kMessageSend, kTypeP2pLinkRequest, destination); + +exit: + if (error != kErrorNone) + { + if (aPeer != nullptr) + { + aPeer->SetState(Neighbor::kStateInvalid); + } + mState = kStateIdle; + SetWakeupListenerEnabled(); + } + + FreeMessageOnError(message, error); +} + +void Mle::P2p::HandleP2pLinkAcceptAndRequest(RxInfo &aRxInfo) +{ + HandleP2pLinkAcceptVariant(aRxInfo, kTypeP2pLinkAcceptAndRequest); +} + +Error Mle::P2p::SendP2pLinkAccept(const LinkAcceptInfo &aInfo) +{ + return SendP2pLinkAcceptVariant(aInfo, false /* aIsLinkAcceptAndRequest*/); +} +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + +Error Mle::P2p::SendP2pLinkAcceptVariant(const LinkAcceptInfo &aInfo, bool aIsLinkAcceptAndRequest) +{ + Error error = kErrorNone; + Command command = aIsLinkAcceptAndRequest ? kCommandP2pLinkAcceptAndRequest : kCommandP2pLinkAccept; + TxMessage *message = nullptr; + Peer *peer = nullptr; + Ip6::Address destination; + + VerifyOrExit((message = Get().NewMleMessage(command)) != nullptr, error = kErrorNoBufs); + if (command == kCommandP2pLinkAcceptAndRequest) + { + SuccessOrExit(error = message->AppendModeTlv(Get().GetDeviceMode())); + SuccessOrExit(error = message->AppendVersionTlv()); + } + + SuccessOrExit(error = message->AppendResponseTlv(aInfo.mRxChallenge)); + if (command == kCommandP2pLinkAcceptAndRequest) + { + VerifyOrExit((peer = mPeerTable.FindPeer(aInfo.mExtAddress, Peer::kInStateLinkRequest)) != nullptr, + error = kErrorNotFound); + + peer->GenerateChallenge(); + SuccessOrExit(error = message->AppendChallengeTlv(peer->GetChallenge())); + message->SetDirectTransmission(); + } + else + { + peer = mPeerTable.FindPeer(aInfo.mExtAddress, Peer::kInStateValid); + VerifyOrExit(peer != nullptr, error = kErrorNotFound); + } + + SuccessOrExit(error = message->AppendLinkMarginTlv(aInfo.mLinkMargin)); + SuccessOrExit(error = message->AppendLinkAndMleFrameCounterTlvs()); + + peer->GetLinkLocalIp6Address(destination); + SuccessOrExit(error = message->SendTo(destination)); + + if (command == kCommandP2pLinkAccept) + { + mState = kStateIdle; + mTimer.Stop(); + SetWakeupListenerEnabled(); + + LogInfo("P2P link to %s is established", aInfo.mExtAddress.ToString().AsCString()); + mEventCallback.InvokeIfSet(OT_P2P_EVENT_LINKED, &aInfo.mExtAddress); + } + + Log(kMessageSend, (command == kCommandP2pLinkAccept) ? kTypeP2pLinkAccept : kTypeP2pLinkAcceptAndRequest, + destination); + +exit: + FreeMessageOnError(message, error); + return error; +} + +void Mle::P2p::HandleP2pLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType) +{ + Error error = kErrorNone; + DeviceMode mode; + uint16_t version; + RxChallenge response; + uint32_t linkFrameCounter; + uint32_t mleFrameCounter; + Peer *peer; + LinkAcceptInfo info; + uint8_t linkMargin; + + Log(kMessageReceive, aMessageType, aRxInfo.mMessageInfo.GetPeerAddr()); + + info.mExtAddress.SetFromIid(aRxInfo.mMessageInfo.GetPeerAddr().GetIid()); + VerifyOrExit((peer = mPeerTable.FindPeer(info.mExtAddress, Peer::kInStateLinkRequest)) != nullptr, + error = kErrorNotFound); + aRxInfo.mNeighbor = peer; + + if (aMessageType == kTypeP2pLinkAcceptAndRequest) + { + SuccessOrExit(error = aRxInfo.mMessage.ReadModeTlv(mode)); + SuccessOrExit(error = aRxInfo.mMessage.ReadVersionTlv(version)); + peer->SetDeviceMode(mode); + peer->SetVersion(version); + } + + SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response)); + VerifyOrExit(response == peer->GetChallenge(), error = kErrorSecurity); + SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter)); + SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, linkMargin)); + + Get().InitNeighbor(*peer, aRxInfo); + + peer->SetState(Neighbor::kStateValid); + peer->GetLinkFrameCounters().SetAll(linkFrameCounter); + peer->SetLinkAckFrameCounter(linkFrameCounter); + peer->SetMleFrameCounter(mleFrameCounter); + peer->SetKeySequence(aRxInfo.mKeySequence); + aRxInfo.mClass = RxInfo::kAuthoritativeMessage; + + Get().ProcessKeySequence(aRxInfo); + + if (aMessageType == kTypeP2pLinkAcceptAndRequest) + { + SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(info.mRxChallenge)); + + info.mExtAddress = aRxInfo.mNeighbor->GetExtAddress(); + info.mLinkMargin = Get().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss()); + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + SuccessOrExit(error = SendP2pLinkAccept(info)); +#endif + } + else + { + LogInfo("P2P link to %s is established", peer->GetExtAddress().ToString().AsCString()); + + mEventCallback.InvokeIfSet(OT_P2P_EVENT_LINKED, &peer->GetExtAddress()); + + if (!mPeerTable.HasPeers(Peer::kInStateLinkRequest)) + { + // All P2P links have been established. + mState = kStateIdle; + mTimer.Stop(); + mLinkedCallback.InvokeAndClearIfSet(); + } + } + +exit: + LogProcessError(aMessageType, error); +} + +void Mle::P2p::HandleLinkTimer(void) +{ + switch (mState) + { +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + case kStateWakingUp: + case kStateWaitingLinkAccept: + { + if (mState == kStateWakingUp) + { + LogInfo("Connection window closed"); + } + + mState = kStateIdle; + ClearPeersInLinkRequestState(); + mLinkedCallback.InvokeAndClearIfSet(); + } + break; +#endif + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + case kStateAttachDelay: + SendP2pLinkRequest(mPeer); + break; + + case kStateWaitingLinkAcceptAndRequest: + LogWarn("Waiting for the P2P link accept and request timeout"); + + mState = kStateIdle; + ClearPeersInLinkRequestState(); + SetWakeupListenerEnabled(); + break; +#endif + + default: + break; + } +} + +void Mle::P2p::SetWakeupListenerEnabled(void) +{ +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + // The wake-up listener is disabled after a wake-up frame is received, here enables the wake-up listener again. + IgnoreError(Get().SetWakeupListenEnabled(true)); +#endif +} + +void Mle::P2p::ClearPeersInLinkRequestState(void) +{ + for (Peer &peer : mPeerTable.Iterate(Peer::kInStateLinkRequest)) + { + peer.SetState(Neighbor::kStateInvalid); + } +} + +void Mle::P2p::SetEventCallback(otP2pEventCallback aCallback, void *aContext) +{ + mEventCallback.Set(aCallback, aContext); +} + +} // namespace Mle +} // namespace ot +#endif // OPENTHREAD_CONFIG_P2P_ENABLE diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index eb207a371..ea5baee36 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -39,6 +39,9 @@ #include #include +#if OPENTHREAD_CONFIG_P2P_ENABLE +#include +#endif #include #if OPENTHREAD_FTD #include @@ -132,28 +135,31 @@ enum DeviceRole : uint8_t */ enum Command : uint8_t { - kCommandLinkRequest = 0, ///< Link Request command - kCommandLinkAccept = 1, ///< Link Accept command - kCommandLinkAcceptAndRequest = 2, ///< Link Accept And Request command - kCommandLinkReject = 3, ///< Link Reject command - kCommandAdvertisement = 4, ///< Advertisement command - kCommandUpdate = 5, ///< Update command - kCommandUpdateRequest = 6, ///< Update Request command - kCommandDataRequest = 7, ///< Data Request command - kCommandDataResponse = 8, ///< Data Response command - kCommandParentRequest = 9, ///< Parent Request command - kCommandParentResponse = 10, ///< Parent Response command - kCommandChildIdRequest = 11, ///< Child ID Request command - kCommandChildIdResponse = 12, ///< Child ID Response command - kCommandChildUpdateRequest = 13, ///< Child Update Request command - kCommandChildUpdateResponse = 14, ///< Child Update Response command - kCommandAnnounce = 15, ///< Announce command - kCommandDiscoveryRequest = 16, ///< Discovery Request command - kCommandDiscoveryResponse = 17, ///< Discovery Response command - kCommandLinkMetricsManagementRequest = 18, ///< Link Metrics Management Request command - kCommandLinkMetricsManagementResponse = 19, ///< Link Metrics Management Response command - kCommandLinkProbe = 20, ///< Link Probe command - kCommandTimeSync = 99, ///< Time Sync command + kCommandLinkRequest = 0, ///< Link Request command + kCommandLinkAccept = 1, ///< Link Accept command + kCommandLinkAcceptAndRequest = 2, ///< Link Accept And Request command + kCommandLinkReject = 3, ///< Link Reject command + kCommandAdvertisement = 4, ///< Advertisement command + kCommandUpdate = 5, ///< Update command + kCommandUpdateRequest = 6, ///< Update Request command + kCommandDataRequest = 7, ///< Data Request command + kCommandDataResponse = 8, ///< Data Response command + kCommandParentRequest = 9, ///< Parent Request command + kCommandParentResponse = 10, ///< Parent Response command + kCommandChildIdRequest = 11, ///< Child ID Request command + kCommandChildIdResponse = 12, ///< Child ID Response command + kCommandChildUpdateRequest = 13, ///< Child Update Request command + kCommandChildUpdateResponse = 14, ///< Child Update Response command + kCommandAnnounce = 15, ///< Announce command + kCommandDiscoveryRequest = 16, ///< Discovery Request command + kCommandDiscoveryResponse = 17, ///< Discovery Response command + kCommandLinkMetricsManagementRequest = 18, ///< Link Metrics Management Request command + kCommandLinkMetricsManagementResponse = 19, ///< Link Metrics Management Response command + kCommandLinkProbe = 20, ///< Link Probe command + kCommandTimeSync = 99, ///< Time Sync command + kCommandP2pLinkRequest = 100, ///< P2P Link Request command + kCommandP2pLinkAccept = 101, ///< P2P Link Accept command + kCommandP2pLinkAcceptAndRequest = 102, ///< P2P Link Accept And Request command }; /** @@ -838,6 +844,22 @@ inline bool IsChildRloc16(uint16_t aRloc16) { return ChildIdFromRloc16(aRloc16) */ const char *RoleToString(DeviceRole aRole); +#if OPENTHREAD_CONFIG_P2P_ENABLE && OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +/** + * Represents a P2P request. + */ +class P2pRequest : public otP2pRequest +{ +public: + /** + * Gets the wake-up request. + * + * @returns The wake-up request. + */ + const Mac::WakeupRequest &GetWakeupRequest(void) const { return AsCoreType(&mWakeupRequest); } +}; +#endif // OPENTHREAD_CONFIG_P2P_ENABLE && OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + /** * @} */ @@ -850,6 +872,9 @@ DefineMapEnum(otDeviceRole, Mle::DeviceRole); DefineCoreType(otDeviceProperties, Mle::DeviceProperties); DefineMapEnum(otPowerSupply, Mle::DeviceProperties::PowerSupply); #endif +#if OPENTHREAD_CONFIG_P2P_ENABLE && OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +DefineCoreType(otP2pRequest, Mle::P2pRequest); +#endif } // namespace ot diff --git a/src/core/thread/neighbor.cpp b/src/core/thread/neighbor.cpp index d325eb9d7..121227ca1 100644 --- a/src/core/thread/neighbor.cpp +++ b/src/core/thread/neighbor.cpp @@ -164,6 +164,10 @@ bool Neighbor::MatchesFilter(StateFilter aFilter) const matches = !IsStateValidOrRestoring(); break; + case kInStateLinkRequest: + matches = IsStateLinkRequest(); + break; + case kInStateAny: matches = true; break; diff --git a/src/core/thread/neighbor.hpp b/src/core/thread/neighbor.hpp index 65480acf6..7bc4357ee 100644 --- a/src/core/thread/neighbor.hpp +++ b/src/core/thread/neighbor.hpp @@ -108,6 +108,7 @@ public: kInStateAnyExceptInvalid, ///< Accept neighbor in any state except `kStateInvalid`. kInStateAnyExceptValidOrRestoring, ///< Accept neighbor in any state except `IsStateValidOrRestoring()`. kInStateAny, ///< Accept neighbor in any state. + kInStateLinkRequest, ///< Accept neighbor only in `State::kStateLinkRequest`. }; /** diff --git a/src/core/thread/neighbor_table.cpp b/src/core/thread/neighbor_table.cpp index 7bef8947e..da17296af 100644 --- a/src/core/thread/neighbor_table.cpp +++ b/src/core/thread/neighbor_table.cpp @@ -90,6 +90,13 @@ Neighbor *NeighborTable::FindNeighbor(const Neighbor::AddressMatcher &aMatcher) neighbor = FindParent(aMatcher); } +#if OPENTHREAD_CONFIG_P2P_ENABLE + if (neighbor == nullptr) + { + neighbor = FindPeer(aMatcher); + } +#endif + return neighbor; } @@ -114,6 +121,13 @@ Neighbor *NeighborTable::FindNeighbor(const Mac::Address &aMacAddress, Neighbor: return FindNeighbor(Neighbor::AddressMatcher(aMacAddress, aFilter)); } +#if OPENTHREAD_CONFIG_P2P_ENABLE +Neighbor *NeighborTable::FindPeer(const Neighbor::AddressMatcher &aMatcher) +{ + return Get().FindPeer(aMatcher); +} +#endif + #if OPENTHREAD_FTD Neighbor *NeighborTable::FindChildOrRouter(const Neighbor::AddressMatcher &aMatcher) diff --git a/src/core/thread/neighbor_table.hpp b/src/core/thread/neighbor_table.hpp index 87a976727..ee890f4ca 100644 --- a/src/core/thread/neighbor_table.hpp +++ b/src/core/thread/neighbor_table.hpp @@ -152,6 +152,10 @@ public: Neighbor *FindNeighbor(const Mac::Address &aMacAddress, Neighbor::StateFilter aFilter = Neighbor::kInStateValidOrRestoring); +#if OPENTHREAD_CONFIG_P2P_ENABLE + Neighbor *FindPeer(const Neighbor::AddressMatcher &aMatcher); +#endif + #if OPENTHREAD_FTD /** diff --git a/src/core/thread/peer.cpp b/src/core/thread/peer.cpp new file mode 100644 index 000000000..7cfbf3e7b --- /dev/null +++ b/src/core/thread/peer.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * This file includes definitions for a Thread P2P `Peer`. + */ + +#include "peer.hpp" + +#if OPENTHREAD_CONFIG_P2P_ENABLE + +#include "instance/instance.hpp" + +namespace ot { + +void Peer::Clear(void) +{ + Instance &instance = GetInstance(); + + ClearAllBytes(*this); + Init(instance); +} + +void Peer::SetDeviceMode(Mle::DeviceMode aMode) +{ + VerifyOrExit(aMode != GetDeviceMode()); + + Neighbor::SetDeviceMode(aMode); + + VerifyOrExit(IsStateValid()); + +exit: + return; +} + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_P2P_ENABLE diff --git a/src/core/thread/peer.hpp b/src/core/thread/peer.hpp new file mode 100644 index 000000000..e7e5e5e15 --- /dev/null +++ b/src/core/thread/peer.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * This file includes definitions for a Thread P2P `Peer`. + */ + +#ifndef PEER_HPP_ +#define PEER_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_P2P_ENABLE + +#include "thread/neighbor.hpp" + +namespace ot { + +/** + * Represents a P2P Peer. + */ +class Peer : public CslNeighbor +{ +public: + /** + * Initializes the `Peer` object. + * + * @param[in] aInstance The OpenThread instance. + */ + void Init(Instance &aInstance) { Neighbor::Init(aInstance); } + + /** + * Clears the peer entry. + */ + void Clear(void); + + /** + * Sets the device mode flags. + * + * @param[in] aMode The device mode flags. + */ + void SetDeviceMode(Mle::DeviceMode aMode); + + /** + * Gets the link local IPv6 address of the peer. + * + * @returns The link local IPv6 address of the peer. + */ + void GetLinkLocalIp6Address(Ip6::Address &aIp6Address) const { aIp6Address.SetToLinkLocalAddress(GetExtAddress()); } + + /** + * Generates a new challenge value to use during a child attach. + */ + void GenerateChallenge(void) { mAttachChallenge.GenerateRandom(); } + + /** + * Gets the current challenge value used during attach. + * + * @returns The current challenge value. + */ + const Mle::TxChallenge &GetChallenge(void) const { return mAttachChallenge; } + +private: + Mle::TxChallenge mAttachChallenge; +}; + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_P2P_ENABLE + +#endif // PEER_HPP_ diff --git a/src/core/thread/peer_table.cpp b/src/core/thread/peer_table.cpp new file mode 100644 index 000000000..f4b19d645 --- /dev/null +++ b/src/core/thread/peer_table.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * This file includes definitions for the Thread Peer table. + */ + +#include "peer_table.hpp" + +#if OPENTHREAD_CONFIG_P2P_ENABLE + +#include "instance/instance.hpp" + +namespace ot { + +PeerTable::Iterator::Iterator(Instance &aInstance, Peer::StateFilter aFilter) + : InstanceLocator(aInstance) + , ItemPtrIterator(nullptr) + , mFilter(aFilter) +{ + Reset(); +} + +void PeerTable::Iterator::Reset(void) +{ + mItem = &Get().mPeers[0]; + + if (!mItem->MatchesFilter(mFilter)) + { + Advance(); + } +} + +void PeerTable::Iterator::Advance(void) +{ + VerifyOrExit(mItem != nullptr); + + do + { + mItem++; + VerifyOrExit(mItem < &Get().mPeers[Get().kMaxPeers], mItem = nullptr); + } while (!mItem->MatchesFilter(mFilter)); + +exit: + return; +} + +PeerTable::PeerTable(Instance &aInstance) + : InstanceLocator(aInstance) +{ + for (Peer &peer : mPeers) + { + peer.Init(aInstance); + peer.Clear(); + } +} + +void PeerTable::Clear(void) +{ + for (Peer &peer : mPeers) + { + peer.Clear(); + } +} + +Peer *PeerTable::GetNewPeer(void) +{ + Peer *peer = FindPeer(Peer::AddressMatcher(Peer::kInStateInvalid)); + + VerifyOrExit(peer != nullptr); + peer->Clear(); + +exit: + return peer; +} + +const Peer *PeerTable::FindPeer(const Peer::AddressMatcher &aMatcher) const +{ + const Peer *peer = mPeers; + + for (uint16_t num = kMaxPeers; num != 0; num--, peer++) + { + if (peer->Matches(aMatcher)) + { + ExitNow(); + } + } + + peer = nullptr; + +exit: + return peer; +} + +Peer *PeerTable::FindPeer(const Mac::ExtAddress &aExtAddress, Peer::StateFilter aFilter) +{ + return FindPeer(Peer::AddressMatcher(aExtAddress, aFilter)); +} + +bool PeerTable::HasPeers(Peer::StateFilter aFilter) const +{ + return (FindPeer(Peer::AddressMatcher(aFilter)) != nullptr); +} + +bool PeerTable::IsFull(void) const { return FindPeer(Peer::AddressMatcher(Peer::kInStateInvalid)) == nullptr; } + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_P2P_ENABLE diff --git a/src/core/thread/peer_table.hpp b/src/core/thread/peer_table.hpp new file mode 100644 index 000000000..8041d27a3 --- /dev/null +++ b/src/core/thread/peer_table.hpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2025, 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. + */ + +/** + * @file + * This file includes definitions for the Thread Peer table. + */ + +#ifndef PEER_TABLE_HPP_ +#define PEER_TABLE_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_P2P_ENABLE + +#include "common/const_cast.hpp" +#include "common/iterator_utils.hpp" +#include "common/locator.hpp" +#include "common/non_copyable.hpp" +#include "thread/peer.hpp" + +namespace ot { + +/** + * Represents the Thread Peer table. + */ +class PeerTable : public InstanceLocator, private NonCopyable +{ + friend class NeighborTable; + class IteratorBuilder; + +public: + /** + * Represents an iterator for iterating through the peer entries in the peer table. + */ + class Iterator : public InstanceLocator, public ItemPtrIterator + { + friend class ItemPtrIterator; + friend class IteratorBuilder; + + public: + /** + * Initializes an `Iterator` instance. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aFilter A peer state filter. + */ + Iterator(Instance &aInstance, Peer::StateFilter aFilter); + + /** + * Resets the iterator to start over. + */ + void Reset(void); + + /** + * Gets the `Peer` entry to which the iterator is currently pointing. + * + * @returns A pointer to the `Peer` entry, or `nullptr` if the iterator is done and/or empty. + */ + Peer *GetPeer(void) { return mItem; } + + private: + explicit Iterator(Instance &aInstance) + : InstanceLocator(aInstance) + , mFilter(Peer::StateFilter::kInStateValid) + { + } + + void Advance(void); + + Peer::StateFilter mFilter; + }; + + /** + * Initializes a `PeerTable` instance. + * + * @param[in] aInstance A reference to the OpenThread instance. + */ + explicit PeerTable(Instance &aInstance); + + /** + * Clears the peer table. + */ + void Clear(void); + + /** + * Gets a new/unused `Peer` entry from the peer table. + * + * @note The returned peer entry will be cleared (`memset` to zero). + * + * @returns A pointer to a new `Peer` entry, or `nullptr` if all `Peer` entries are in use. + */ + Peer *GetNewPeer(void); + + /** + * Searches the peer table for a `Peer` with a given extended address also matching a given state + * filter. + * + * @param[in] aExtAddress A reference to an extended address. + * @param[in] aFilter A peer state filter. + * + * @returns A pointer to the `Peer` entry if one is found, or `nullptr` otherwise. + */ + Peer *FindPeer(const Mac::ExtAddress &aExtAddress, Peer::StateFilter aFilter); + + /** + * Indicates whether the peer table contains any peer matching a given state filter. + * + * @param[in] aFilter A peer state filter. + * + * @returns TRUE if the table contains at least one peer table matching the given filter, FALSE otherwise. + */ + bool HasPeers(Peer::StateFilter aFilter) const; + + /** + * Enables range-based `for` loop iteration over all peer entries in the peer table matching a given + * state filter. + * + * Should be used as follows: + * + * for (Peer &peer : aPeerTable.Iterate(aFilter)) { ... } + * + * @param[in] aFilter A peer state filter. + * + * @returns An IteratorBuilder instance. + */ + IteratorBuilder Iterate(Peer::StateFilter aFilter) { return IteratorBuilder(GetInstance(), aFilter); } + + /** + * Indicates whether the peer table is full. + * + * @returns TRUE if the table is full, FALSE otherwise. + */ + bool IsFull(void) const; + +private: + static constexpr uint16_t kMaxPeers = OPENTHREAD_CONFIG_P2P_MAX_PEERS; + + class IteratorBuilder : public InstanceLocator + { + public: + IteratorBuilder(Instance &aInstance, Peer::StateFilter aFilter) + : InstanceLocator(aInstance) + , mFilter(aFilter) + { + } + + Iterator begin(void) { return Iterator(GetInstance(), mFilter); } + Iterator end(void) { return Iterator(GetInstance()); } + + private: + Peer::StateFilter mFilter; + }; + + Peer *FindPeer(const Peer::AddressMatcher &aMatcher) { return AsNonConst(AsConst(this)->FindPeer(aMatcher)); } + const Peer *FindPeer(const Peer::AddressMatcher &aMatcher) const; + + Peer mPeers[kMaxPeers]; +}; + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_P2P_ENABLE + +#endif // PEER_TABLE_HPP_ diff --git a/tests/scripts/expect/v1_5-cli-p2p-link.exp b/tests/scripts/expect/v1_5-cli-p2p-link.exp new file mode 100755 index 000000000..b8050fc55 --- /dev/null +++ b/tests/scripts/expect/v1_5-cli-p2p-link.exp @@ -0,0 +1,217 @@ +#!/usr/bin/expect -f + +# +# Copyright (c) 2025, 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" +source "tests/scripts/expect/_multinode.exp" + +set WL1 1 +set WL2 2 +set WC1 3 +set WC2 4 +set WL1_EXT_ADDRESS "deadbeefcafe0001" +set WL2_EXT_ADDRESS "deadbeefcafe0002" +set WC1_EXT_ADDRESS "deadbeefcafe0003" +set WC2_EXT_ADDRESS "deadbeefcafe0004" + + +send_user "\n\n>>> Setup WL1\n" +spawn_node $WL1 + +setup_default_network + +send "mode rdn\n" +expect_line "Done" + +send "extaddr $WL1_EXT_ADDRESS\n" +expect_line "Done" + +send "wakeup channel 11\n" +expect_line "Done" + +send "routerupgradethreshold 0\n" +expect_line "Done" + +send "ifconfig up\n" +expect_line "Done" + +send "thread start\n" +expect_line "Done" + +send "wakeup listen enable\n" +expect_line "Done" + +set wl1_link_local_addr [get_ipaddr linklocal] + + +send_user "\n\n>>> Setup WL2\n" +spawn_node $WL2 + +setup_default_network + +send "mode rdn\n" +expect_line "Done" + +send "extaddr $WL2_EXT_ADDRESS\n" +expect_line "Done" + +send "wakeup channel 11\n" +expect_line "Done" + +send "routerupgradethreshold 0\n" +expect_line "Done" + +send "ifconfig up\n" +expect_line "Done" + +send "thread start\n" +expect_line "Done" + +send "wakeup listen enable\n" +expect_line "Done" + +set wl2_link_local_addr [get_ipaddr linklocal] + + +send_user "\n\n>>> Setup WC1\n" +spawn_node $WC1 +setup_default_network + +send "mode rdn\n" +expect_line "Done" + +send "extaddr $WC1_EXT_ADDRESS\n" +expect_line "Done" + +send "wakeup channel 11\n" +expect_line "Done" + +send "routerupgradethreshold 0\n" +expect_line "Done" + +send "ifconfig up\n" +expect_line "Done" + +send "thread start\n" +expect_line "Done" + +set wc1_link_local_addr [get_ipaddr linklocal] + + +send_user "\n\n>>> Setup WC2\n" +spawn_node $WC2 +setup_default_network + +send "mode rdn\n" +expect_line "Done" + +send "extaddr $WC2_EXT_ADDRESS\n" +expect_line "Done" + +send "wakeup channel 11\n" +expect_line "Done" + +send "routerupgradethreshold 0\n" +expect_line "Done" + +send "ifconfig up\n" +expect_line "Done" + +send "thread start\n" +expect_line "Done" + +set wc2_link_local_addr [get_ipaddr linklocal] + + +send_user "\n\n>>> WC1 establishes a P2P link with WL1\n" +switch_node $WC1 +send "p2p link extaddr $WL1_EXT_ADDRESS\n" +expect_line "Done" + +send_user "\n\n>>> WC1 pings WL1\n" +send "ping $wl1_link_local_addr 20 5\n" +for {set i 1} {$i <= 5} {incr i} { + expect "28 bytes from $wl1_link_local_addr: icmp_seq=$i" +} + +sleep 1 + + +send_user "\n\n>>> WC1 establishes a P2P link with WL2\n" +send "p2p link extaddr $WL2_EXT_ADDRESS\n" +expect_line "Done" + +sleep 1 + +send_user "\n\n>>> WC1 pings WL2\n" +send "ping $wl2_link_local_addr 20 5\n" +for {set i 6} {$i <= 10} {incr i} { + expect "28 bytes from $wl2_link_local_addr: icmp_seq=$i" +} + + +send_user "\n\n>>> WC2 establishes a P2P link with WL1\n" +switch_node $WC2 +send "p2p link extaddr $WL1_EXT_ADDRESS\n" +expect_line "Done" + +sleep 1 + +send_user "\n\n>>> WC2 pings WL1\n" +send "ping $wl1_link_local_addr 20 5\n" +for {set i 1} {$i <= 5} {incr i} { + expect "28 bytes from $wl1_link_local_addr: icmp_seq=$i" +} + + +send_user "\n\n>>> WL1 pings WC1\n" +switch_node $WL1 + +send "ping $wc1_link_local_addr 20 5\n" +for {set i 1} {$i <= 5} {incr i} { + expect "28 bytes from $wc1_link_local_addr: icmp_seq=$i" +} + + +send_user "\n\n>>> WL1 pings WC2\n" +send "ping $wc2_link_local_addr 20 5\n" +for {set i 6} {$i <= 10} {incr i} { + expect "28 bytes from $wc2_link_local_addr: icmp_seq=$i" +} + + +send_user "\n\n>>> WL2 pings WC1\n" +switch_node $WL2 + +send "ping $wc1_link_local_addr 20 5\n" +for {set i 1} {$i <= 5} {incr i} { + expect "28 bytes from $wc1_link_local_addr: icmp_seq=$i" +} + +dispose_all diff --git a/tests/unit/test_child_table.cpp b/tests/unit/test_child_table.cpp index a5497bc92..ad4c69a58 100644 --- a/tests/unit/test_child_table.cpp +++ b/tests/unit/test_child_table.cpp @@ -108,6 +108,10 @@ static bool StateMatchesFilter(Child::State aState, Child::StateFilter aFilter) case Child::kInStateAny: rval = true; break; + + case Child::kInStateLinkRequest: + rval = false; + break; } return rval;