diff --git a/include/openthread/coap_secure.h b/include/openthread/coap_secure.h index 349e5ca22..67043f55d 100644 --- a/include/openthread/coap_secure.h +++ b/include/openthread/coap_secure.h @@ -76,6 +76,7 @@ typedef enum otCoapSecureConnectEvent OT_COAP_SECURE_DISCONNECTED_LOCAL_CLOSED, ///< Disconnected locally OT_COAP_SECURE_DISCONNECTED_MAX_ATTEMPTS, ///< Disconnected due to reaching the max connection attempts OT_COAP_SECURE_DISCONNECTED_ERROR, ///< Disconnected due to an error + OT_COAP_SECURE_DISCONNECTED_TIMEOUT, ///< Disconnected locally due to session timeout } otCoapSecureConnectEvent; /** diff --git a/include/openthread/history_tracker.h b/include/openthread/history_tracker.h index ec9280560..e48e071d3 100644 --- a/include/openthread/history_tracker.h +++ b/include/openthread/history_tracker.h @@ -235,6 +235,26 @@ typedef struct otHistoryTrackerExternalRouteInfo otHistoryTrackerNetDataEvent mEvent; ///< Indicates the event (added/removed). } otHistoryTrackerExternalRouteInfo; +/** + * Represents events during the Border Agent's ePSKc journey. + */ +typedef enum +{ + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED, ///< ePSKc mode is activated. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED, ///< Secure session is connected. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED, ///< Commissioner petition is received. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_ACTIVE_DATASET, ///< Active dataset is retrieved. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_PENDING_DATASET, ///< Pending dataset is retrieved. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_KEEP_ALIVE, ///< Keep alive message is received. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_LOCAL_CLOSE, ///< Deactivated by a call to the API. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_REMOTE_CLOSE, ///< Disconnected by the peer. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_SESSION_ERROR, ///< Disconnected due to some error. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_SESSION_TIMEOUT, ///< Disconnected due to timeout. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_MAX_ATTEMPTS, ///< Max allowed attempts reached. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_EPSKC_TIMEOUT, ///< ePSKc mode timed out. + OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_UNKNOWN, ///< Deactivated for an unknown reason. +} otHistoryTrackerBorderAgentEpskcEvent; + /** * Initializes an `otHistoryTrackerIterator`. * @@ -397,6 +417,23 @@ const otHistoryTrackerExternalRouteInfo *otHistoryTrackerIterateExternalRouteHis otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge); +/** + * Iterates over the entries in the Border Agent ePSKc history list. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in,out] aIterator A pointer to an iterator. MUST be initialized or the behavior is undefined. + * @param[out] aEntryAge A pointer to a variable to output the entry's age. MUST NOT be NULL. + * Age is provided as the duration (in milliseconds) from when entry was recorded to + * @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries + * older than max age. + * + * @returns The `otHistoryTrackerBorderAgentEpskcEvent` entry or `NULL` if no more entries in the list. + */ +const otHistoryTrackerBorderAgentEpskcEvent *otHistoryTrackerIterateBorderAgentEpskcEventHistory( + otInstance *aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t *aEntryAge); + /** * Converts a given entry age to a human-readable string. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index ea90d556d..689979e71 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (510) +#define OPENTHREAD_API_VERSION (511) /** * @addtogroup api-instance diff --git a/src/core/api/history_tracker_api.cpp b/src/core/api/history_tracker_api.cpp index 5b4b5f0f5..5a002f7d3 100644 --- a/src/core/api/history_tracker_api.cpp +++ b/src/core/api/history_tracker_api.cpp @@ -129,6 +129,19 @@ const otHistoryTrackerExternalRouteInfo *otHistoryTrackerIterateExternalRouteHis *aEntryAge); } +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE +const otHistoryTrackerBorderAgentEpskcEvent *otHistoryTrackerIterateBorderAgentEpskcEventHistory( + otInstance *aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t *aEntryAge) +{ + AssertPointerIsNotNull(aEntryAge); + + return AsCoreType(aInstance).Get().IterateEpskcEventHistory(AsCoreType(aIterator), + *aEntryAge); +} +#endif + void otHistoryTrackerEntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize) { Utils::HistoryTracker::EntryAgeToString(aEntryAge, aBuffer, aSize); diff --git a/src/core/config/history_tracker.h b/src/core/config/history_tracker.h index c18386418..ef9c63721 100644 --- a/src/core/config/history_tracker.h +++ b/src/core/config/history_tracker.h @@ -160,6 +160,17 @@ #define OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE 32 #endif +/** + * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_EPSKC_EVENT_SIZE + * + * Specifies the maximum number of entries in Border Agent ePSKc history list. + * + * Can be set to zero to configure History Tracker module not to collect any ePSKc journey info. + */ +#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_EPSKC_EVENT_SIZE +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_EPSKC_EVENT_SIZE 64 +#endif + /** * @} */ diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index 3741aac14..45d1b30ad 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -746,6 +746,9 @@ exit: { case kErrorNone: Get().mCounters.mEpskcActivations++; +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordEpskcEvent(Utils::HistoryTracker::kEpskcActivated); +#endif break; case kErrorInvalidState: Get().mCounters.mEpskcInvalidBaStateErrors++; @@ -763,7 +766,7 @@ exit: void BorderAgent::EphemeralKeyManager::Stop(void) { Stop(kReasonLocalDisconnect); } -void BorderAgent::EphemeralKeyManager::Stop(StopReason aReason) +void BorderAgent::EphemeralKeyManager::Stop(DeactivationReason aReason) { switch (mState) { @@ -776,37 +779,71 @@ void BorderAgent::EphemeralKeyManager::Stop(StopReason aReason) ExitNow(); } - LogInfo("Stopping ephemeral key use - reason: %s", StopReasonToString(aReason)); + LogInfo("Stopping ephemeral key use - reason: %s", DeactivationReasonToString(aReason)); SetState(kStateStopped); mTimer.Stop(); mDtlsTransport.Close(); - switch (aReason) - { - case kReasonLocalDisconnect: - Get().mCounters.mEpskcDeactivationClears++; - break; - case kReasonPeerDisconnect: - Get().mCounters.mEpskcDeactivationDisconnects++; - break; - case kReasonSessionError: - Get().mCounters.mEpskcStartSecureSessionErrors++; - break; - case kReasonMaxFailedAttempts: - Get().mCounters.mEpskcDeactivationMaxAttempts++; - break; - case kReasonTimeout: - Get().mCounters.mEpskcDeactivationTimeouts++; - break; - case kReasonUnknown: - break; - } + UpdateCountersAndRecordEvent(aReason); exit: return; } +void BorderAgent::EphemeralKeyManager::UpdateCountersAndRecordEvent(DeactivationReason aReason) +{ + struct ReasonToCounterEventEntry + { + DeactivationReason mReason; + uint8_t mEvent; // Raw values of `Utils::HistoryTracker::Epskc` enum. + uint32_t Counters::*mCounterPtr; + }; + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE +#define ReasonEntry(kReason, kCounter, kEvent) \ + { \ + kReason, Utils::HistoryTracker::kEvent, &Counters::kCounter, \ + } +#else +#define ReasonEntry(kReason, kCounter, kEvent) \ + { \ + kReason, 0, &Counters::kCounter \ + } +#endif + + static const ReasonToCounterEventEntry kReasonToCounterEventEntries[] = { + ReasonEntry(kReasonLocalDisconnect, mEpskcDeactivationClears, kEpskcDeactivatedLocalClose), + ReasonEntry(kReasonSessionTimeout, mEpskcDeactivationClears, kEpskcDeactivatedSessionTimeout), + ReasonEntry(kReasonPeerDisconnect, mEpskcDeactivationDisconnects, kEpskcDeactivatedRemoteClose), + ReasonEntry(kReasonSessionError, mEpskcStartSecureSessionErrors, kEpskcDeactivatedSessionError), + ReasonEntry(kReasonMaxFailedAttempts, mEpskcDeactivationMaxAttempts, kEpskcDeactivatedMaxAttempts), + ReasonEntry(kReasonEpskcTimeout, mEpskcDeactivationTimeouts, kEpskcDeactivatedEpskcTimeout), + }; + +#undef ReasonEntry + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Utils::HistoryTracker::EpskcEvent event = Utils::HistoryTracker::kEpskcDeactivatedUnknown; +#endif + + for (const ReasonToCounterEventEntry &entry : kReasonToCounterEventEntries) + { + if (aReason == entry.mReason) + { + (Get().mCounters.*(entry.mCounterPtr))++; +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + event = static_cast(entry.mEvent); +#endif + break; + } + } + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordEpskcEvent(event); +#endif +} + void BorderAgent::EphemeralKeyManager::SetState(State aState) { #if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE @@ -868,14 +905,16 @@ void BorderAgent::EphemeralKeyManager::HandleSessionConnected(void) { SetState(kStateConnected); Get().mCounters.mEpskcSecureSessionSuccesses++; +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordEpskcEvent(Utils::HistoryTracker::kEpskcConnected); +#endif } void BorderAgent::EphemeralKeyManager::HandleSessionDisconnected(SecureSession::ConnectEvent aEvent) { - StopReason reason = kReasonUnknown; + DeactivationReason reason = kReasonUnknown; // The ephemeral key can be used once - VerifyOrExit((mState == kStateConnected) || (mState == kStateAccepted)); switch (aEvent) @@ -889,6 +928,9 @@ void BorderAgent::EphemeralKeyManager::HandleSessionDisconnected(SecureSession:: case SecureSession::kDisconnectedMaxAttempts: reason = kReasonMaxFailedAttempts; break; + case SecureSession::kDisconnectedTimeout: + reason = kReasonSessionTimeout; + break; default: break; } @@ -903,9 +945,12 @@ void BorderAgent::EphemeralKeyManager::HandleCommissionerPetitionAccepted(void) { SetState(kStateAccepted); Get().mCounters.mEpskcCommissionerPetitions++; +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordEpskcEvent(Utils::HistoryTracker::kEpskcPetitioned); +#endif } -void BorderAgent::EphemeralKeyManager::HandleTimer(void) { Stop(kReasonTimeout); } +void BorderAgent::EphemeralKeyManager::HandleTimer(void) { Stop(kReasonEpskcTimeout); } void BorderAgent::EphemeralKeyManager::HandleTask(void) { mCallback.InvokeIfSet(); } @@ -992,15 +1037,16 @@ const char *BorderAgent::EphemeralKeyManager::StateToString(State aState) #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) -const char *BorderAgent::EphemeralKeyManager::StopReasonToString(StopReason aReason) +const char *BorderAgent::EphemeralKeyManager::DeactivationReasonToString(DeactivationReason aReason) { static const char *const kReasonStrings[] = { "LocalDisconnect", // (0) kReasonLocalDisconnect "PeerDisconnect", // (1) kReasonPeerDisconnect "SessionError", // (2) kReasonSessionError - "MaxFailedAttempts", // (3) kReasonMaxFailedAttempts - "Timeout", // (4) kReasonTimeout - "Unknown", // (5) kReasonUnknown + "SessionTimeout", // (3) kReasonSessionTimeout + "MaxFailedAttempts", // (4) kReasonMaxFailedAttempts + "EpskcTimeout", // (5) kReasonTimeout + "Unknown", // (6) kReasonUnknown }; struct EnumCheck @@ -1009,8 +1055,9 @@ const char *BorderAgent::EphemeralKeyManager::StopReasonToString(StopReason aRea ValidateNextEnum(kReasonLocalDisconnect); ValidateNextEnum(kReasonPeerDisconnect); ValidateNextEnum(kReasonSessionError); + ValidateNextEnum(kReasonSessionTimeout); ValidateNextEnum(kReasonMaxFailedAttempts); - ValidateNextEnum(kReasonTimeout); + ValidateNextEnum(kReasonEpskcTimeout); ValidateNextEnum(kReasonUnknown); }; @@ -1124,6 +1171,12 @@ void BorderAgent::CoapDtlsSession::HandleTmfCommissionerKeepAlive(Coap::Message VerifyOrExit(mIsActiveCommissioner); SuccessOrExit(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderKeepAlive)); mTimer.Start(kKeepAliveTimeout); +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE && OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + if (Get().OwnsSession(*this)) + { + Get().RecordEpskcEvent(Utils::HistoryTracker::kEpskcKeepAlive); + } +#endif exit: return; @@ -1454,11 +1507,23 @@ void BorderAgent::CoapDtlsSession::HandleTmfDatasetGet(Coap::Message &aMessage, case kUriActiveGet: response = Get().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags); Get().mCounters.mMgmtActiveGets++; +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE && OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + if (Get().OwnsSession(*this)) + { + Get().RecordEpskcEvent(Utils::HistoryTracker::kEpskcRetrievedActiveDataset); + } +#endif break; case kUriPendingGet: response = Get().ProcessGetRequest(aMessage, DatasetManager::kIgnoreSecurityPolicyFlags); Get().mCounters.mMgmtPendingGets++; +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE && OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + if (Get().OwnsSession(*this)) + { + Get().RecordEpskcEvent(Utils::HistoryTracker::kEpskcRetrievedPendingDataset); + } +#endif break; case kUriCommissionerGet: @@ -1490,7 +1555,7 @@ void BorderAgent::CoapDtlsSession::HandleTimer(void) if (IsConnected()) { LogInfo("Session timed out - disconnecting"); - Disconnect(); + DisconnectTimeout(); } } diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index 029e510d0..2c1dd4c3f 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -39,6 +39,7 @@ #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE #include +#include #include "border_router/routing_manager.hpp" #include "common/appender.hpp" @@ -389,26 +390,28 @@ public: static_assert(kMaxKeyLength <= Dtls::Transport::kPskMaxLength, "Max e-key len is larger than max PSK len"); - enum StopReason : uint8_t + enum DeactivationReason : uint8_t { kReasonLocalDisconnect, kReasonPeerDisconnect, kReasonSessionError, + kReasonSessionTimeout, kReasonMaxFailedAttempts, - kReasonTimeout, + kReasonEpskcTimeout, kReasonUnknown, }; explicit EphemeralKeyManager(Instance &aInstance); void SetState(State aState); - void Stop(StopReason aReason); + void Stop(DeactivationReason aReason); void HandleTimer(void); void HandleTask(void); bool OwnsSession(CoapDtlsSession &aSession) const { return mCoapDtlsSession == &aSession; } void HandleSessionConnected(void); void HandleSessionDisconnected(SecureSession::ConnectEvent aEvent); void HandleCommissionerPetitionAccepted(void); + void UpdateCountersAndRecordEvent(DeactivationReason aReason); #if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE bool ShouldRegisterService(void) const; void RegisterOrUnregisterService(void); @@ -423,7 +426,7 @@ public: void HandleTransportClosed(void); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) - static const char *StopReasonToString(StopReason aReason); + static const char *DeactivationReasonToString(DeactivationReason aReason); #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE diff --git a/src/core/meshcop/secure_transport.hpp b/src/core/meshcop/secure_transport.hpp index dc32ce29b..f5ffd0d67 100644 --- a/src/core/meshcop/secure_transport.hpp +++ b/src/core/meshcop/secure_transport.hpp @@ -118,6 +118,7 @@ public: static constexpr ConnectEvent kDisconnectedLocalClosed = OT_COAP_SECURE_DISCONNECTED_LOCAL_CLOSED; static constexpr ConnectEvent kDisconnectedMaxAttempts = OT_COAP_SECURE_DISCONNECTED_MAX_ATTEMPTS; static constexpr ConnectEvent kDisconnectedError = OT_COAP_SECURE_DISCONNECTED_ERROR; + static constexpr ConnectEvent kDisconnectedTimeout = OT_COAP_SECURE_DISCONNECTED_TIMEOUT; /** * Function pointer which is called reporting a session connection event. @@ -176,6 +177,11 @@ public: */ void Disconnect(void) { Disconnect(kDisconnectedLocalClosed); } + /** + * Disconnects the session due to timeout. + */ + void DisconnectTimeout(void) { Disconnect(kDisconnectedTimeout); } + /** * Sends message to the secure session. * diff --git a/src/core/utils/history_tracker.cpp b/src/core/utils/history_tracker.cpp index 9ef01aefe..87060618e 100644 --- a/src/core/utils/history_tracker.cpp +++ b/src/core/utils/history_tracker.cpp @@ -425,6 +425,19 @@ exit: #endif // OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE +void HistoryTracker::RecordEpskcEvent(EpskcEvent aEvent) +{ + EpskcEvent *entry = mEpskcEventHistory.AddNewEntry(); + + VerifyOrExit(entry != nullptr); + *entry = aEvent; + +exit: + return; +} +#endif + void HistoryTracker::HandleNotifierEvents(Events aEvents) { if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadRlocAdded | kEventThreadRlocRemoved | @@ -451,7 +464,9 @@ void HistoryTracker::HandleTimer(void) mNeighborHistory.UpdateAgedEntries(); mOnMeshPrefixHistory.UpdateAgedEntries(); mExternalRouteHistory.UpdateAgedEntries(); - +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + mEpskcEventHistory.UpdateAgedEntries(); +#endif mTimer.Start(kAgeCheckPeriod); } diff --git a/src/core/utils/history_tracker.hpp b/src/core/utils/history_tracker.hpp index b17c587fb..236f2345d 100644 --- a/src/core/utils/history_tracker.hpp +++ b/src/core/utils/history_tracker.hpp @@ -47,6 +47,7 @@ #include "common/non_copyable.hpp" #include "common/notifier.hpp" #include "common/timer.hpp" +#include "meshcop/border_agent.hpp" #include "net/netif.hpp" #include "net/socket.hpp" #include "thread/mesh_forwarder.hpp" @@ -57,6 +58,7 @@ #include "thread/router_table.hpp" namespace ot { + namespace Utils { #ifdef OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA @@ -81,6 +83,10 @@ class HistoryTracker : public InstanceLocator, private NonCopyable #if OPENTHREAD_FTD friend class ot::RouterTable; #endif +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + friend class ot::MeshCoP::BorderAgent; + friend class ot::MeshCoP::BorderAgent::EphemeralKeyManager; +#endif public: /** @@ -134,6 +140,9 @@ public: typedef otHistoryTrackerRouterInfo RouterInfo; ///< Router info. typedef otHistoryTrackerOnMeshPrefixInfo OnMeshPrefixInfo; ///< Network Data on mesh prefix info. typedef otHistoryTrackerExternalRouteInfo ExternalRouteInfo; ///< Network Data external route info +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + typedef otHistoryTrackerBorderAgentEpskcEvent EpskcEvent; ///< Border Agent ePSKc Event. +#endif /** * Initializes the `HistoryTracker`. @@ -242,6 +251,13 @@ public: return mExternalRouteHistory.Iterate(aIterator, aEntryAge); } +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + const EpskcEvent *IterateEpskcEventHistory(Iterator &aIterator, uint32_t &aEntryAge) const + { + return mEpskcEventHistory.Iterate(aIterator, aEntryAge); + } +#endif + /** * Converts a given entry age to a human-readable string. * @@ -273,6 +289,7 @@ private: static constexpr uint16_t kRouterListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE; static constexpr uint16_t kOnMeshPrefixListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE; static constexpr uint16_t kExternalRouteListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE; + static constexpr uint16_t kEpskcEventListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_EPSKC_EVENT_SIZE; typedef otHistoryTrackerAddressEvent AddressEvent; @@ -300,6 +317,27 @@ private: static constexpr NetDataEvent kNetDataEntryAdded = OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED; static constexpr NetDataEvent kNetDataEntryRemoved = OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED; +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE +#define DefineEpskcEvent(aName, aPublicEnumName) \ + static constexpr EpskcEvent kEpskc##aName = OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_##aPublicEnumName + + DefineEpskcEvent(Activated, ACTIVATED); + DefineEpskcEvent(Connected, CONNECTED); + DefineEpskcEvent(Petitioned, PETITIONED); + DefineEpskcEvent(RetrievedActiveDataset, RETRIEVED_ACTIVE_DATASET); + DefineEpskcEvent(RetrievedPendingDataset, RETRIEVED_PENDING_DATASET); + DefineEpskcEvent(KeepAlive, KEEP_ALIVE); + DefineEpskcEvent(DeactivatedLocalClose, DEACTIVATED_LOCAL_CLOSE); + DefineEpskcEvent(DeactivatedRemoteClose, DEACTIVATED_REMOTE_CLOSE); + DefineEpskcEvent(DeactivatedSessionError, DEACTIVATED_SESSION_ERROR); + DefineEpskcEvent(DeactivatedSessionTimeout, DEACTIVATED_SESSION_TIMEOUT); + DefineEpskcEvent(DeactivatedMaxAttempts, DEACTIVATED_MAX_ATTEMPTS); + DefineEpskcEvent(DeactivatedEpskcTimeout, DEACTIVATED_EPSKC_TIMEOUT); + DefineEpskcEvent(DeactivatedUnknown, DEACTIVATED_UNKNOWN); + +#undef DefineEpskcEvent +#endif + class Timestamp { public: @@ -408,6 +446,9 @@ private: void RecordOnMeshPrefixEvent(NetDataEvent aEvent, const NetworkData::OnMeshPrefixConfig &aPrefix); void RecordExternalRouteEvent(NetDataEvent aEvent, const NetworkData::ExternalRouteConfig &aRoute); #endif +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + void RecordEpskcEvent(EpskcEvent aEvent); +#endif using TrackerTimer = TimerMilliIn; @@ -420,6 +461,9 @@ private: EntryList mRouterHistory; EntryList mOnMeshPrefixHistory; EntryList mExternalRouteHistory; +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + EntryList mEpskcEventHistory; +#endif TrackerTimer mTimer; diff --git a/tests/nexus/test_border_agent.cpp b/tests/nexus/test_border_agent.cpp index 0be4d4c2a..2752595e8 100644 --- a/tests/nexus/test_border_agent.cpp +++ b/tests/nexus/test_border_agent.cpp @@ -38,7 +38,10 @@ namespace Nexus { using ActiveDatasetManager = MeshCoP::ActiveDatasetManager; using BorderAgent = MeshCoP::BorderAgent; -using EphemeralKeyManager = ot::MeshCoP::BorderAgent::EphemeralKeyManager; +using EphemeralKeyManager = MeshCoP::BorderAgent::EphemeralKeyManager; +using HistoryTracker = Utils::HistoryTracker; +using EpskcEvent = HistoryTracker::EpskcEvent; +using Iterator = HistoryTracker::Iterator; using ExtendedPanIdManager = MeshCoP::ExtendedPanIdManager; using NameData = MeshCoP::NameData; using NetworkNameManager = MeshCoP::NetworkNameManager; @@ -754,6 +757,357 @@ void TestBorderAgentEphemeralKey(void) VerifyOrQuit(node0.Get().GetCounters().mEpskcInvalidArgsErrors == 2); } +EpskcEvent GetNewestEpskcEvent(Node &aNode) +{ + const EpskcEvent *epskcEvent = nullptr; + Iterator iter; + uint32_t age; + iter.Init(); + + epskcEvent = aNode.Get().IterateEpskcEventHistory(iter, age); + + VerifyOrQuit(epskcEvent != nullptr); + return *epskcEvent; +} + +void TestHistoryTrackerBorderAgentEpskcEvent(void) +{ + static const char kEphemeralKey[] = "nexus1234"; + + static constexpr uint16_t kEphemeralKeySize = sizeof(kEphemeralKey) - 1; + static constexpr uint16_t kUdpPort = 49155; + + Core nexus; + Node &node0 = nexus.CreateNode(); + Node &node1 = nexus.CreateNode(); + Ip6::SockAddr sockAddr; + Coap::Message *message; + EpskcEvent epskcEvent; + + Log("------------------------------------------------------------------------------------------------------"); + Log("TestHistoryTrackerBorderAgentEpskcEvent"); + + nexus.AdvanceTime(0); + + // Form the topology: + // - node0 leader acting as Border Agent. + // - node1 acting as candidate + + node0.Form(); + nexus.AdvanceTime(50 * Time::kOneSecondInMsec); + VerifyOrQuit(node0.Get().IsLeader()); + sockAddr.SetAddress(node0.Get().GetLinkLocalAddress()); + sockAddr.SetPort(kUdpPort); + + SuccessOrQuit(node1.Get().SetPanChannel(node0.Get().GetPanChannel())); + node1.Get().SetPanId(node0.Get().GetPanId()); + node1.Get().Up(); + + // Enable the Ephemeral Key feature. + node0.Get().SetEnabled(true); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStopped); + + Log("------------------------------------------------------------------------------------------------------"); + Log(" Check Ephemeral Key Journey 1: Epskc Mode is activated, then deactivated"); + + SuccessOrQuit(node0.Get().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort)); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED); + + node0.Get().Stop(); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_LOCAL_CLOSE); + + Log("------------------------------------------------------------------------------------------------------"); + Log(" Check Ephemeral Key Journey 2: Epskc Mode is activated, then deactivated after max failed connection " + "attempts"); + + SuccessOrQuit(node0.Get().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort)); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + SuccessOrQuit(node0.Get().AddUnsecurePort(kUdpPort)); + + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED); + + nexus.AdvanceTime(0); + + SuccessOrQuit(node1.Get().Open()); + SuccessOrQuit( + node1.Get().SetPsk(reinterpret_cast(kEphemeralKey), kEphemeralKeySize - 2)); + + for (uint8_t numAttempts = 1; numAttempts < 10; numAttempts++) + { + Log(" Attempt %u to connect with the wrong key", numAttempts); + + SuccessOrQuit(node1.Get().Connect(sockAddr)); + + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + + VerifyOrQuit(!node1.Get().IsConnected()); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + } + + Log(" Attempt 10 (final attempt) to connect with the wrong key, check that ephemeral key use is stopped"); + + SuccessOrQuit(node1.Get().Connect(sockAddr)); + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + + VerifyOrQuit(!node1.Get().IsConnected()); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStopped); + + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_MAX_ATTEMPTS); + + Log("------------------------------------------------------------------------------------------------------"); + Log(" Check Ephemeral Key Journey 3: The session is connected successfully, then disconnected by API"); + + SuccessOrQuit(node0.Get().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort)); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED); + + SuccessOrQuit( + node1.Get().SetPsk(reinterpret_cast(kEphemeralKey), kEphemeralKeySize)); + SuccessOrQuit(node1.Get().Connect(sockAddr)); + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + VerifyOrQuit(node1.Get().IsConnected()); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED); + + node0.Get().Stop(); + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_LOCAL_CLOSE); + + Log("------------------------------------------------------------------------------------------------------"); + Log(" Check Ephemeral Key Journey 4: The session is connected successfully. And the candidate petitioned as an " + "active commissioner. The session is disconnected by the remote."); + + SuccessOrQuit(node0.Get().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort)); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED); + + VerifyOrQuit(!node1.Get().IsConnected()); + SuccessOrQuit(node1.Get().Connect(sockAddr)); + + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + VerifyOrQuit(node1.Get().IsConnected()); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriCommissionerPetition); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED); + + node1.Get().Disconnect(); + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_REMOTE_CLOSE); + + Log("------------------------------------------------------------------------------------------------------"); + Log(" Check Ephemeral Key Journey 5: The session is connected successfully. And the active dataset is fetched. " + "The session is disconnected by the remote."); + + SuccessOrQuit(node0.Get().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort)); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED); + + VerifyOrQuit(!node1.Get().IsConnected()); + SuccessOrQuit(node1.Get().Connect(sockAddr)); + + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + VerifyOrQuit(node1.Get().IsConnected()); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriCommissionerPetition); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriActiveGet); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_ACTIVE_DATASET); + + node1.Get().Disconnect(); + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_REMOTE_CLOSE); + + Log("------------------------------------------------------------------------------------------------------"); + Log(" Check Ephemeral Key Journey 6: The session is connected successfully. And the pending dataset is fetched. " + "The session is disconnected by the remote."); + + SuccessOrQuit(node0.Get().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort)); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED); + + VerifyOrQuit(!node1.Get().IsConnected()); + SuccessOrQuit(node1.Get().Connect(sockAddr)); + + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + VerifyOrQuit(node1.Get().IsConnected()); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriCommissionerPetition); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriActiveGet); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_ACTIVE_DATASET); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriPendingGet); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_PENDING_DATASET); + + node1.Get().Disconnect(); + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_REMOTE_CLOSE); + + Log("------------------------------------------------------------------------------------------------------"); + Log(" Check Ephemeral Key Journey 7: The session is connected successfully. And the pending dataset is fetched. " + "The session is due to session timeout."); + SuccessOrQuit(node0.Get().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort)); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED); + + VerifyOrQuit(!node1.Get().IsConnected()); + SuccessOrQuit(node1.Get().Connect(sockAddr)); + + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + VerifyOrQuit(node1.Get().IsConnected()); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriCommissionerPetition); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriActiveGet); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_ACTIVE_DATASET); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriPendingGet); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_PENDING_DATASET); + + static constexpr uint32_t kKeepAliveTimeout = 50 * 1000; // Timeout to reject a commissioner (in msec) + nexus.AdvanceTime(kKeepAliveTimeout); // Wait for the session timeout + VerifyOrQuit(!node1.Get().IsConnected()); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_SESSION_TIMEOUT); + + Log("------------------------------------------------------------------------------------------------------"); + Log(" Check Ephemeral Key Journey 8: The session is connected successfully. The epskc mode is deactivated due to " + "timeout."); + SuccessOrQuit(node0.Get().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort)); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStarted); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED); + + VerifyOrQuit(!node1.Get().IsConnected()); + SuccessOrQuit(node1.Get().Connect(sockAddr)); + + nexus.AdvanceTime(3 * Time::kOneSecondInMsec); + VerifyOrQuit(node1.Get().IsConnected()); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED); + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriCommissionerPetition); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + nexus.AdvanceTime(1 * Time::kOneSecondInMsec); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED); + + // Send a KeepAlive message every 30s until ePSKc mode timed out + for (uint8_t i = 0; i < EphemeralKeyManager::kDefaultTimeout / (30 * Time::kOneSecondInMsec) - 1; i++) + { + if (!node1.Get().IsConnected()) + { + break; + } + message = node1.Get().NewPriorityConfirmablePostMessage(kUriCommissionerKeepAlive); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, MeshCoP::StateTlv::kAccept)); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + nexus.AdvanceTime(30 * Time::kOneSecondInMsec); + + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_KEEP_ALIVE); + } + + message = node1.Get().NewPriorityConfirmablePostMessage(kUriCommissionerKeepAlive); + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, MeshCoP::StateTlv::kAccept)); + SuccessOrQuit(Tlv::Append(*message, "node1")); + SuccessOrQuit(node1.Get().SendMessage(*message)); + nexus.AdvanceTime(30 * Time::kOneSecondInMsec); + + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateStopped); + epskcEvent = GetNewestEpskcEvent(node0); + VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_EPSKC_TIMEOUT); +} + //---------------------------------------------------------------------------------------------------------------------- struct TxtData @@ -1547,6 +1901,7 @@ int main(void) { ot::Nexus::TestBorderAgent(); ot::Nexus::TestBorderAgentEphemeralKey(); + ot::Nexus::TestHistoryTrackerBorderAgentEpskcEvent(); ot::Nexus::TestBorderAgentTxtDataCallback(); ot::Nexus::TestBorderAgentServiceRegistration(); printf("All tests passed\n");