mirror of
https://github.com/espressif/openthread.git
synced 2026-08-16 15:47:46 +00:00
[tcat] add TCAT_ENABLE.req TMF command (#12013)
This adds support for the TMF command to enable TCAT remotely. A test is added that uses the 'UDP send' mechanism to send the new TMF command to a target node. Some fixes/additions to the test framework are made to support the new test, including a new argument for udp_send() to send a specific byte array and udp_rx() to receive data by a UDP client on a node.
This commit is contained in:
@@ -94,6 +94,7 @@ public:
|
||||
kJoinerIid = OT_MESHCOP_TLV_JOINER_IID, ///< Joiner IID TLV
|
||||
kJoinerRouterLocator = OT_MESHCOP_TLV_JOINER_RLOC, ///< Joiner Router Locator TLV
|
||||
kJoinerRouterKek = OT_MESHCOP_TLV_JOINER_ROUTER_KEK, ///< Joiner Router KEK TLV
|
||||
kDuration = OT_MESHCOP_TLV_DURATION, ///< Duration TLV
|
||||
kProvisioningUrl = OT_MESHCOP_TLV_PROVISIONING_URL, ///< Provisioning URL TLV
|
||||
kVendorName = OT_MESHCOP_TLV_VENDOR_NAME_TLV, ///< meshcop Vendor Name TLV
|
||||
kVendorModel = OT_MESHCOP_TLV_VENDOR_MODEL_TLV, ///< meshcop Vendor Model TLV
|
||||
@@ -205,6 +206,11 @@ typedef UintTlvInfo<Tlv::kJoinerRouterLocator, uint16_t> JoinerRouterLocatorTlv;
|
||||
*/
|
||||
typedef SimpleTlvInfo<Tlv::kJoinerRouterKek, Kek> JoinerRouterKekTlv;
|
||||
|
||||
/**
|
||||
* Defines Duration TLV constants and types.
|
||||
*/
|
||||
typedef UintTlvInfo<Tlv::kDuration, uint16_t> DurationTlv;
|
||||
|
||||
/**
|
||||
* Defines Count TLV constants and types.
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,7 @@ TcatAgent::TcatAgent(Instance &aInstance)
|
||||
, mVendorInfo(nullptr)
|
||||
, mState(kStateDisabled)
|
||||
, mNextState(kStateDisabled)
|
||||
, mTimerSetsToActive(false)
|
||||
, mActiveOrStandbyTimer(aInstance)
|
||||
, mTcatActiveDurationMs(0)
|
||||
{
|
||||
@@ -95,7 +96,6 @@ Error TcatAgent::Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallb
|
||||
mState = kStateActive;
|
||||
mNextState = kStateActive;
|
||||
mTcatActiveDurationMs = 0;
|
||||
mActiveOrStandbyTimer.Stop();
|
||||
LogInfo("Start");
|
||||
|
||||
exit:
|
||||
@@ -109,6 +109,7 @@ void TcatAgent::Stop(void)
|
||||
mAppDataReceiveCallback.Clear();
|
||||
mJoinCallback.Clear();
|
||||
mState = kStateDisabled;
|
||||
mActiveOrStandbyTimer.Stop();
|
||||
ClearCommissionerState();
|
||||
LogInfo("Stop");
|
||||
}
|
||||
@@ -122,7 +123,7 @@ Error TcatAgent::Standby(void)
|
||||
mTcatActiveDurationMs = 0;
|
||||
mActiveOrStandbyTimer.Stop();
|
||||
mNextState = kStateStandby;
|
||||
if (!IsConnected())
|
||||
if (!IsConnected() && mState != kStateStandby)
|
||||
{
|
||||
// if already TLS-connected, only move to 'standby' once the connection is done.
|
||||
// if not yet fully connected, go to 'standby' immediately (ignoring a TLS handshake that may be ongoing)
|
||||
@@ -141,14 +142,17 @@ Error TcatAgent::Activate(const uint32_t aDelayMs, const uint32_t aDurationMs)
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(IsStarted(), error = kErrorInvalidState);
|
||||
VerifyOrExit(mState != kStateActive);
|
||||
|
||||
mTcatActiveDurationMs = aDurationMs;
|
||||
mTimerSetsToActive = true;
|
||||
if (aDelayMs > 0)
|
||||
{
|
||||
mActiveOrStandbyTimer.Start(aDelayMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
mActiveOrStandbyTimer.Stop();
|
||||
HandleTimer();
|
||||
}
|
||||
|
||||
@@ -219,8 +223,7 @@ Error TcatAgent::Connected(MeshCoP::Tls::Extension &aTls)
|
||||
}
|
||||
|
||||
// A temporary enablement stops after disconnect: to standby.
|
||||
// For others, return to prior state, upon disconnect.
|
||||
mNextState = (mState == kStateActiveTemporary) ? kStateStandby : mState;
|
||||
mNextState = (mState == kStateActiveTemporary) ? kStateStandby : kStateActive;
|
||||
mState = kStateConnected;
|
||||
NotifyStateChange();
|
||||
LogInfo("Connected");
|
||||
@@ -237,8 +240,8 @@ void TcatAgent::Disconnected(void)
|
||||
if (mState != kStateDisabled)
|
||||
{
|
||||
mState = mNextState;
|
||||
NotifyStateChange();
|
||||
LogInfo("Disconnected");
|
||||
NotifyStateChange();
|
||||
ClearCommissionerState();
|
||||
}
|
||||
}
|
||||
@@ -1071,28 +1074,30 @@ void TcatAgent::HandleTimer(void)
|
||||
{
|
||||
case kStateStandby:
|
||||
case kStateStandbyTemporary:
|
||||
if (mTcatActiveDurationMs > 0)
|
||||
case kStateActiveTemporary:
|
||||
if (mTimerSetsToActive)
|
||||
{
|
||||
mActiveOrStandbyTimer.Start(mTcatActiveDurationMs);
|
||||
mState = kStateActiveTemporary;
|
||||
mTimerSetsToActive = false;
|
||||
if (mTcatActiveDurationMs > 0)
|
||||
{
|
||||
mState = kStateActiveTemporary;
|
||||
mActiveOrStandbyTimer.Start(mTcatActiveDurationMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
mState = kStateActive;
|
||||
}
|
||||
NotifyStateChange();
|
||||
LogInfo("Active");
|
||||
}
|
||||
else
|
||||
{
|
||||
mState = kStateActive;
|
||||
IgnoreError(Standby());
|
||||
}
|
||||
NotifyStateChange();
|
||||
LogInfo("Active");
|
||||
break;
|
||||
|
||||
case kStateActiveTemporary:
|
||||
IgnoreError(Standby());
|
||||
break;
|
||||
|
||||
case kStateConnected:
|
||||
mNextState = (mTcatActiveDurationMs > 0) ? kStateStandby : kStateActive;
|
||||
break;
|
||||
|
||||
// kStateActive: will not go to standby, based on timer. Application has forced it to 'active'.
|
||||
// kStateConnected: no change here, mNextState already set.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1105,6 +1110,80 @@ void TcatAgent::NotifyStateChange(void)
|
||||
mState == kStateConnected);
|
||||
}
|
||||
|
||||
template <> void TcatAgent::HandleTmf<kUriTcatEnable>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Coap::Message *message = nullptr;
|
||||
uint32_t delayTimerMs = 0;
|
||||
uint16_t durationSec = 0;
|
||||
uint32_t durationMs;
|
||||
|
||||
VerifyOrExit(aMessage.IsConfirmablePostRequest());
|
||||
LogInfo("Received %s from %s", UriToString<kUriTcatEnable>(), aMessageInfo.GetPeerAddr().ToString().AsCString());
|
||||
message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
|
||||
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
||||
|
||||
SuccessOrExit(error = Tlv::Find<DelayTimerTlv>(aMessage, delayTimerMs));
|
||||
switch (Tlv::Find<DurationTlv>(aMessage, durationSec))
|
||||
{
|
||||
case kErrorNone:
|
||||
break;
|
||||
case kErrorNotFound:
|
||||
durationSec = kTcatTmfEnableDefaultSec; // If Duration TLV absent: use default duration
|
||||
break;
|
||||
default:
|
||||
ExitNow(error = kErrorParse);
|
||||
}
|
||||
durationMs = Time::SecToMsec(durationSec);
|
||||
|
||||
// if an existing activation is ongoing, adapt the requested one to be compatible.
|
||||
AdaptToExistingActivePeriod(delayTimerMs, durationMs);
|
||||
|
||||
error = Activate(delayTimerMs, durationMs);
|
||||
|
||||
exit:
|
||||
if (message != nullptr)
|
||||
{
|
||||
error =
|
||||
Tlv::Append<StateTlv>(*message, error == kErrorNone ? StateTlv::State::kAccept : StateTlv::State::kReject);
|
||||
if (error == kErrorNone)
|
||||
{
|
||||
error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo);
|
||||
}
|
||||
FreeMessageOnError(message, error);
|
||||
}
|
||||
LogWarnOnError(error, "send TCAT_ENABLE.rsp");
|
||||
}
|
||||
|
||||
// Adapts delay/duration parameters of the given TCAT temporary activation period to the
|
||||
// already-ongoing temporary TCAT activation (if any). The goals of the adaptation are:
|
||||
// - not shorten the duration of an existing (scheduled/ongoing) activation
|
||||
// - not set the start of activation later than the existing scheduled activation time
|
||||
void TcatAgent::AdaptToExistingActivePeriod(uint32_t &aPeriodDelayMs, uint32_t &aPeriodDurationMs)
|
||||
{
|
||||
VerifyOrExit(mState != kStateActive);
|
||||
|
||||
if (mActiveOrStandbyTimer.IsRunning())
|
||||
{
|
||||
TimeMilli now = TimerMilli::GetNow();
|
||||
uint32_t remainingMs;
|
||||
remainingMs = (mActiveOrStandbyTimer.GetFireTime() > now) ? mActiveOrStandbyTimer.GetFireTime() - now : 0;
|
||||
if (mTimerSetsToActive)
|
||||
{
|
||||
aPeriodDelayMs = Min(aPeriodDelayMs, remainingMs);
|
||||
aPeriodDurationMs = Max(aPeriodDurationMs, remainingMs + mTcatActiveDurationMs - aPeriodDelayMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
aPeriodDelayMs = 0;
|
||||
aPeriodDurationMs = Max(aPeriodDurationMs, remainingMs);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void SerializeTcatAdvertisementTlv(uint8_t *aBuffer,
|
||||
uint16_t &aOffset,
|
||||
TcatAdvertisementTlvType aType,
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "meshcop/meshcop.hpp"
|
||||
#include "meshcop/meshcop_tlvs.hpp"
|
||||
#include "meshcop/secure_transport.hpp"
|
||||
#include "thread/tmf.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
@@ -330,7 +331,7 @@ public:
|
||||
* Activate TCAT functions of the TCAT agent.
|
||||
*
|
||||
* This requires the TCAT agent to be already started.
|
||||
* The state transitions to kStateActive of kStateActiveTemporary. In these states, TCAT Advertisements
|
||||
* The state transitions to kStateActive or kStateActiveTemporary. In these states, TCAT Advertisements
|
||||
* are actively sent and TCAT Commissioners are able to connect. From here, TCAT can be set to standby
|
||||
* again using Standby().
|
||||
* If a connection is ongoing and aDurationMs==0, this call will ensure that kStateActive will
|
||||
@@ -338,7 +339,7 @@ public:
|
||||
* This function will override any ongoing temporary activation of TCAT, or any
|
||||
* previously scheduled activation for a future time.
|
||||
*
|
||||
* @param[in] aDelayMs Delay in ms before activating. If 0, activate immediately.
|
||||
* @param[in] aDelayMs Delay in ms before activating. If 0, activate immediately.
|
||||
* @param[in] aDurationMs Duration in ms of the activation. If 0, activate indefinitely.
|
||||
*
|
||||
* @retval kErrorNone Successfully set the TCAT agent to kStateActive now, OR scheduled
|
||||
@@ -413,6 +414,8 @@ public:
|
||||
*/
|
||||
bool GetApplicationResponsePending(void) const { return mApplicationResponsePending; }
|
||||
|
||||
template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
|
||||
private:
|
||||
void NotifyApplicationResponseSent(void) { mApplicationResponsePending = false; }
|
||||
void NotifyStateChange(void);
|
||||
@@ -456,7 +459,7 @@ private:
|
||||
TcatApplicationProtocol aApplicationProtocol,
|
||||
bool &aResponse);
|
||||
void HandleTimer(void);
|
||||
|
||||
void AdaptToExistingActivePeriod(uint32_t &aPeriodDelayMs, uint32_t &aPeriodDurationMs);
|
||||
Error VerifyHash(const Message &aIncomingMessage,
|
||||
uint16_t aOffset,
|
||||
uint16_t aLength,
|
||||
@@ -478,6 +481,7 @@ private:
|
||||
static constexpr uint16_t kBufferReserve = 2048 / (Buffer::kSize - sizeof(otMessageBuffer)) + 1;
|
||||
static constexpr uint8_t kServiceNameMaxLength = OT_TCAT_SERVICE_NAME_MAX_LENGTH;
|
||||
static constexpr uint8_t kApplicationLayerMaxCount = OT_TCAT_APPLICATION_LAYER_MAX_COUNT;
|
||||
static constexpr uint16_t kTcatTmfEnableDefaultSec = OT_TCAT_ENABLE_MAX;
|
||||
|
||||
const VendorInfo *mVendorInfo;
|
||||
Callback<JoinCallback> mJoinCallback;
|
||||
@@ -488,7 +492,8 @@ private:
|
||||
NetworkName mCommissionerDomainName;
|
||||
ExtendedPanId mCommissionerExtendedPanId;
|
||||
State mState;
|
||||
State mNextState;
|
||||
State mNextState; //< desired state after client disconnects
|
||||
bool mTimerSetsToActive : 1;
|
||||
bool mCommissionerHasNetworkName : 1;
|
||||
bool mCommissionerHasDomainName : 1;
|
||||
bool mCommissionerHasExtendedPanId : 1;
|
||||
@@ -503,15 +508,10 @@ private:
|
||||
uint32_t mTcatActiveDurationMs;
|
||||
};
|
||||
|
||||
} // namespace MeshCoP
|
||||
|
||||
DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo);
|
||||
|
||||
DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol);
|
||||
DefineMapEnum(otTcatAdvertisedDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType);
|
||||
DeclareTmfHandler(TcatAgent, kUriTcatEnable);
|
||||
|
||||
// Command class TLVs
|
||||
typedef UintTlvInfo<MeshCoP::TcatAgent::kTlvResponseWithStatus, uint8_t> ResponseWithStatusTlv;
|
||||
typedef UintTlvInfo<TcatAgent::kTlvResponseWithStatus, uint8_t> ResponseWithStatusTlv;
|
||||
|
||||
/**
|
||||
* Represent TCAT Device Type and Status
|
||||
@@ -545,6 +545,13 @@ enum TcatAdvertisementTlvType : uint8_t
|
||||
kTlvVendorIanaPen = 6, ///< TCAT Vendor IANA PEN
|
||||
};
|
||||
|
||||
} // namespace MeshCoP
|
||||
|
||||
DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo);
|
||||
|
||||
DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol);
|
||||
DefineMapEnum(otTcatAdvertisedDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType);
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
|
||||
|
||||
@@ -179,6 +179,9 @@ bool Agent::HandleResource(const char *aUriPath, Message &aMessage, const Ip6::M
|
||||
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
|
||||
Case(kUriDuaRegistrationRequest, BackboneRouter::Manager);
|
||||
#endif
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
|
||||
Case(kUriTcatEnable, MeshCoP::TcatAgent);
|
||||
#endif
|
||||
|
||||
default:
|
||||
|
||||
@@ -83,6 +83,7 @@ static constexpr Entry kEntries[] = {
|
||||
{"c/pq"}, // kUriPanIdQuery
|
||||
{"c/ps"}, // kUriPendingSet
|
||||
{"c/rx"}, // kUriRelayRx
|
||||
{"c/te"}, // kUriTcatEnable
|
||||
{"c/tx"}, // kUriRelayTx
|
||||
{"c/ur"}, // kUriProxyRx
|
||||
{"c/ut"}, // kUriProxyTx
|
||||
@@ -128,6 +129,7 @@ static_assert(AreConstStringsEqual(kEntries[kUriPendingGet].mPath, "c/pg"), "kEn
|
||||
static_assert(AreConstStringsEqual(kEntries[kUriPanIdQuery].mPath, "c/pq"), "kEntries is invalid");
|
||||
static_assert(AreConstStringsEqual(kEntries[kUriPendingSet].mPath, "c/ps"), "kEntries is invalid");
|
||||
static_assert(AreConstStringsEqual(kEntries[kUriRelayRx].mPath, "c/rx"), "kEntries is invalid");
|
||||
static_assert(AreConstStringsEqual(kEntries[kUriTcatEnable].mPath, "c/te"), "kEntries is invalid");
|
||||
static_assert(AreConstStringsEqual(kEntries[kUriRelayTx].mPath, "c/tx"), "kEntries is invalid");
|
||||
static_assert(AreConstStringsEqual(kEntries[kUriProxyRx].mPath, "c/ur"), "kEntries is invalid");
|
||||
static_assert(AreConstStringsEqual(kEntries[kUriProxyTx].mPath, "c/ut"), "kEntries is invalid");
|
||||
@@ -172,6 +174,7 @@ struct UriEnumCheck
|
||||
ValidateNextEnum(kUriPanIdQuery);
|
||||
ValidateNextEnum(kUriPendingSet);
|
||||
ValidateNextEnum(kUriRelayRx);
|
||||
ValidateNextEnum(kUriTcatEnable);
|
||||
ValidateNextEnum(kUriRelayTx);
|
||||
ValidateNextEnum(kUriProxyRx);
|
||||
ValidateNextEnum(kUriProxyTx);
|
||||
@@ -235,6 +238,7 @@ template <> const char *UriToString<kUriPendingGet>(void) { return "PendingGet";
|
||||
template <> const char *UriToString<kUriPanIdQuery>(void) { return "PanIdQuery"; }
|
||||
template <> const char *UriToString<kUriPendingSet>(void) { return "PendingSet"; }
|
||||
template <> const char *UriToString<kUriRelayRx>(void) { return "RelayRx"; }
|
||||
template <> const char *UriToString<kUriTcatEnable>(void) { return "TcatEnable"; }
|
||||
template <> const char *UriToString<kUriRelayTx>(void) { return "RelayTx"; }
|
||||
template <> const char *UriToString<kUriProxyRx>(void) { return "ProxyRx"; }
|
||||
template <> const char *UriToString<kUriProxyTx>(void) { return "ProxyTx"; }
|
||||
|
||||
@@ -75,6 +75,7 @@ enum Uri : uint8_t
|
||||
kUriPanIdQuery, ///< PAN ID Query ("c/pq")
|
||||
kUriPendingSet, ///< MGMT_PENDING_SET ("c/ps")
|
||||
kUriRelayRx, ///< Relay RX ("c/rx")
|
||||
kUriTcatEnable, ///< TCAT Enable ("c/te")
|
||||
kUriRelayTx, ///< Relay TX ("c/tx")
|
||||
kUriProxyRx, ///< Proxy RX ("c/ur")
|
||||
kUriProxyTx, ///< Proxy TX ("c/ut")
|
||||
@@ -146,6 +147,7 @@ template <> const char *UriToString<kUriPendingGet>(void);
|
||||
template <> const char *UriToString<kUriPanIdQuery>(void);
|
||||
template <> const char *UriToString<kUriPendingSet>(void);
|
||||
template <> const char *UriToString<kUriRelayRx>(void);
|
||||
template <> const char *UriToString<kUriTcatEnable>(void);
|
||||
template <> const char *UriToString<kUriRelayTx>(void);
|
||||
template <> const char *UriToString<kUriProxyRx>(void);
|
||||
template <> const char *UriToString<kUriProxyTx>(void);
|
||||
|
||||
Reference in New Issue
Block a user