[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.
This commit is contained in:
Zhanglong Xia
2025-09-02 10:12:13 -07:00
committed by GitHub
parent 6657de95b8
commit 4cb0de4233
37 changed files with 1986 additions and 59 deletions
+12
View File
@@ -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
*
* @}
*
* @}
*/
+1
View File
@@ -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
+1
View File
@@ -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")
+2
View File
@@ -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",
+1 -1
View File
@@ -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
+89
View File
@@ -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 <openthread/link.h>
#include <openthread/platform/radio.h>
#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_
+131
View File
@@ -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 <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/platform/radio.h>
#include <openthread/provisional/link.h>
#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_
+8
View File
@@ -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
+12
View File
@@ -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 \<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.
+55
View File
@@ -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<Cmd("p2p")>(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<Interpreter *>(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<Cmd("wakeup")>(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"),
+5
View File
@@ -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);
+7
View File
@@ -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",
+4
View File
@@ -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
+56
View File
@@ -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<Mle::Mle>().P2pWakeupAndLink(AsCoreType(aP2pRequest), aCallback, aContext);
}
#endif
void otP2pSetEventCallback(otInstance *aInstance, otP2pEventCallback aCallback, void *aContext)
{
AsCoreType(aInstance).Get<Mle::Mle>().P2pSetEventCallback(aCallback, aContext);
}
#endif
+68
View File
@@ -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_
+18
View File
@@ -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
*
+4
View File
@@ -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
+16 -13
View File
@@ -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<uint32_t>(rvTimestampUs - radioNowUs - kCslRequestAhead);
attachDelayMs = attachDelayMs / 1000;
wakeupInfo.mAttachDelayMs = static_cast<uint32_t>(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<Mle::Mle>().HandleWakeupFrame(wakeupInfo);
exit:
return error;
+19
View File
@@ -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
+95
View File
@@ -41,6 +41,7 @@
#include <openthread/link.h>
#include <openthread/thread.h>
#include <openthread/provisional/link.h>
#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
+10 -8
View File
@@ -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<Mac::Mac>().GetExtAddress());
VerifyOrExit(mTxTimeUs >= nowUs);
radioTxDelay = mTxTimeUs - nowUs;
+16 -11
View File
@@ -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<WakeupTxScheduler, &WakeupTxScheduler::RequestWakeupFrameTransmission>;
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
+1
View File
@@ -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"
+46 -2
View File
@@ -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
+126 -2
View File
@@ -37,8 +37,10 @@
#include "openthread-core-config.h"
#include <openthread/thread_ftd.h>
#include <openthread/provisional/p2p.h>
#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<Mle, &Mle::HandleP2pLinkTimer>;
State mState;
PeerTable mPeerTable;
P2pLinkTimer mTimer;
Callback<P2pLinkDoneCallback> mLinkedCallback;
Callback<P2pEventCallback> mEventCallback;
Peer *mPeer;
};
#endif
//------------------------------------------------------------------------------------------------------------------
@@ -2374,6 +2494,10 @@ private:
Callback<otThreadDiscoveryRequestCallback> mDiscoveryRequestCallback;
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_P2P_ENABLE
P2p mP2p;
#endif
};
#if OPENTHREAD_FTD
+406
View File
@@ -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<Radio>().GetPromiscuous(), error = kErrorInvalidState);
VerifyOrExit(Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
VerifyOrExit(!Get<Mle>().IsDisabled(), error = kErrorInvalidState);
VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
VerifyOrExit(!mPeerTable.IsFull(), error = kErrorNoBufs);
// TODO: support rx-off-when-idle device later.
VerifyOrExit(Get<Mle>().IsRxOnWhenIdle(), error = kErrorInvalidState);
SuccessOrExit(
error = Get<WakeupTxScheduler>().WakeUp(aP2pRequest.GetWakeupRequest(), kWakeupTxInterval, kWakeupMaxDuration));
mState = kStateWakingUp;
mLinkedCallback.Set(aCallback, aContext);
mTimer.FireAt(Get<WakeupTxScheduler>().GetTxEndTime() + Get<WakeupTxScheduler>().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<NeighborTable>().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<Mle>().ProcessKeySequence(aRxInfo);
VerifyOrExit((peer = mPeerTable.GetNewPeer()) != nullptr, error = kErrorNoBufs);
Get<Mle>().InitNeighbor(*peer, aRxInfo);
peer->SetDeviceMode(mode);
peer->SetVersion(version);
peer->SetState(Neighbor::kStateLinkRequest);
SuccessOrExit(error = SendP2pLinkAcceptAndRequest(info));
Get<WakeupTxScheduler>().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<Mle>().NewMleMessage(kCommandP2pLinkRequest)) != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = message->AppendModeTlv(Get<Mle>().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<Mle>().NewMleMessage(command)) != nullptr, error = kErrorNoBufs);
if (command == kCommandP2pLinkAcceptAndRequest)
{
SuccessOrExit(error = message->AppendModeTlv(Get<Mle>().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<LinkMarginTlv>(aRxInfo.mMessage, linkMargin));
Get<Mle>().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<Mle>().ProcessKeySequence(aRxInfo);
if (aMessageType == kTypeP2pLinkAcceptAndRequest)
{
SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(info.mRxChallenge));
info.mExtAddress = aRxInfo.mNeighbor->GetExtAddress();
info.mLinkMargin = Get<Mac::Mac>().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<Mac::Mac>().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
+47 -22
View File
@@ -39,6 +39,9 @@
#include <stdint.h>
#include <string.h>
#if OPENTHREAD_CONFIG_P2P_ENABLE
#include <openthread/provisional/p2p.h>
#endif
#include <openthread/thread.h>
#if OPENTHREAD_FTD
#include <openthread/thread_ftd.h>
@@ -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
+4
View File
@@ -164,6 +164,10 @@ bool Neighbor::MatchesFilter(StateFilter aFilter) const
matches = !IsStateValidOrRestoring();
break;
case kInStateLinkRequest:
matches = IsStateLinkRequest();
break;
case kInStateAny:
matches = true;
break;
+1
View File
@@ -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`.
};
/**
+14
View File
@@ -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<PeerTable>().FindPeer(aMatcher);
}
#endif
#if OPENTHREAD_FTD
Neighbor *NeighborTable::FindChildOrRouter(const Neighbor::AddressMatcher &aMatcher)
+4
View File
@@ -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
/**
+64
View File
@@ -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
+97
View File
@@ -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_
+135
View File
@@ -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<PeerTable>().mPeers[0];
if (!mItem->MatchesFilter(mFilter))
{
Advance();
}
}
void PeerTable::Iterator::Advance(void)
{
VerifyOrExit(mItem != nullptr);
do
{
mItem++;
VerifyOrExit(mItem < &Get<PeerTable>().mPeers[Get<PeerTable>().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
+190
View File
@@ -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<Peer, Iterator>
{
friend class ItemPtrIterator<Peer, Iterator>;
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_
+217
View File
@@ -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
+4
View File
@@ -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;