diff --git a/include/openthread/border_agent.h b/include/openthread/border_agent.h index 33578e77a..38bc1dc9c 100644 --- a/include/openthread/border_agent.h +++ b/include/openthread/border_agent.h @@ -110,6 +110,38 @@ typedef enum otBorderAgentState OT_BORDER_AGENT_STATE_ACTIVE = 2, ///< Border agent is connected with external commissioner. } otBorderAgentState; +typedef struct otBorderAgentCounters +{ + uint32_t mEpskcActivations; ///< The number of ePSKc activations + uint32_t mEpskcDeactivationClears; ///< The number of ePSKc deactivations via API + uint32_t mEpskcDeactivationTimeouts; ///< The number of ePSKc deactivations due to timeout + uint32_t mEpskcDeactivationMaxAttempts; ///< The number of ePSKc deactivations due to reached max attempts + uint32_t mEpskcDeactivationDisconnects; ///< The number of ePSKc deactivations due to commissioner disconnected + uint32_t mEpskcInvalidBaStateErrors; ///< The number of invalid border agent state errors at ePSKc activation + uint32_t mEpskcInvalidArgsErrors; ///< The number of invalid args errors at ePSKc activation + uint32_t mEpskcStartSecureSessionErrors; ///< The number of start secure session errors at ePSKc activation + uint32_t mEpskcSecureSessionSuccesses; ///< The number of established secure sessions with ePSKc + uint32_t mEpskcSecureSessionFailures; ///< The number of failed secure sessions with ePSKc + uint32_t mEpskcCommissionerPetitions; ///< The number of successful commissioner petitions with ePSKc + + uint32_t mPskcSecureSessionSuccesses; ///< The number of established secure sessions with PSKc + uint32_t mPskcSecureSessionFailures; ///< The number of failed secure sessions with PSKc + uint32_t mPskcCommissionerPetitions; ///< The number of successful commissioner petitions with PSKc + + uint32_t mMgmtActiveGets; ///< The number of MGMT_ACTIVE_GET.req sent over secure sessions + uint32_t mMgmtPendingGets; ///< The number of MGMT_PENDING_GET.req sent over secure sessions +} otBorderAgentCounters; + +/** + * Gets the counters of the Thread Border Agent. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns A pointer to the Border Agent counters. + * + */ +const otBorderAgentCounters *otBorderAgentGetCounters(otInstance *aInstance); + /** * Gets the #otBorderAgentState of the Thread Border Agent role. * diff --git a/include/openthread/coap_secure.h b/include/openthread/coap_secure.h index 25b56bf40..012750fc0 100644 --- a/include/openthread/coap_secure.h +++ b/include/openthread/coap_secure.h @@ -67,14 +67,27 @@ extern "C" { #define OT_DEFAULT_COAP_SECURE_PORT 5684 ///< Default CoAP Secure port, as specified in RFC 7252 +/** + * CoAP secure connection event types. + * + */ +typedef enum otCoapSecureConnectEvent +{ + OT_COAP_SECURE_CONNECTED = 0, ///< Connection established + OT_COAP_SECURE_DISCONNECTED_PEER_CLOSED, ///< Disconnected by peer + 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 +} otCoapSecureConnectEvent; + /** * Pointer is called when the DTLS connection state changes. * - * @param[in] aConnected true, if a connection was established, false otherwise. + * @param[in] aEvent The connection event. * @param[in] aContext A pointer to arbitrary context information. * */ -typedef void (*otHandleCoapSecureClientConnect)(bool aConnected, void *aContext); +typedef void (*otHandleCoapSecureClientConnect)(otCoapSecureConnectEvent aEvent, void *aContext); /** * Callback function pointer to notify when the CoAP secure agent is automatically stopped due to reaching the maximum @@ -368,17 +381,17 @@ void otCoapSecureRemoveBlockWiseResource(otInstance *aInstance, otCoapBlockwiseR void otCoapSecureSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandler, void *aContext); /** - * Sets the connected callback to indicate, when - * a Client connect to the CoAP Secure server. + * Sets the connect event callback to indicate when + * a Client connection to the CoAP Secure server has changed. * * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aHandler A pointer to a function that will be called once DTLS connection is established. + * @param[in] aHandler A pointer to a function that will be called once DTLS connection has changed. * @param[in] aContext A pointer to arbitrary context information. May be NULL if not used. * */ -void otCoapSecureSetClientConnectedCallback(otInstance *aInstance, - otHandleCoapSecureClientConnect aHandler, - void *aContext); +void otCoapSecureSetClientConnectEventCallback(otInstance *aInstance, + otHandleCoapSecureClientConnect aHandler, + void *aContext); /** * Sends a CoAP response block-wise from the CoAP Secure server. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 48b1203a3..bbc4d4d72 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (435) +#define OPENTHREAD_API_VERSION (436) /** * @addtogroup api-instance diff --git a/src/cli/README.md b/src/cli/README.md index c633f229a..23f9f1664 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -453,6 +453,33 @@ Disables callback from Border Agent for ephemeral key state changes. Done ``` +### ba counters + +Get the border agent counter values. + +Note that it requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE` to output the ePSKc counters. + +```bash +> ba counters +epskcActivation: 2 +epskcApiDeactivation: 1 +epskcTimeoutDeactivation: 1 +epskcMaxAttemptDeactivation: 0 +epskcDisconnectDeactivation: 0 +epskcInvalidBaStateError: 1 +epskcInvalidArgsError: 1 +epskcStartSecureSessionError: 0 +epskcSecureSessionSuccess: 0 +epskcSecureSessionFailure: 0 +epskcCommissionerPetition: 0 +pskcSecureSessionSuccess: 0 +pskcSecureSessionFailure: 0 +pskcCommissionerPetition: 0 +mgmtActiveGet: 0 +mgmtPendingGet: 0 +Done +``` + ### bufferinfo Show the current message buffer information. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 5d32f8f31..be6e144b8 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -541,6 +541,36 @@ template <> otError Interpreter::Process(Arg aArgs[]) } } #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + /** + * @cli ba counters + * @code + * ba counters + * epskcActivation: 0 + * epskcApiDeactivation: 0 + * epskcTimeoutDeactivation: 0 + * epskcMaxAttemptDeactivation: 0 + * epskcDisconnectDeactivation: 0 + * epskcInvalidBaStateError: 0 + * epskcInvalidArgsError: 0 + * epskcStartSecureSessionError: 0 + * epskcSecureSessionSuccess: 0 + * epskcSecureSessionFailure: 0 + * epskcCommissionerPetition: 0 + * pskcSecureSessionSuccess: 0 + * pskcSecureSessionFailure: 0 + * pskcCommissionerPetition: 0 + * mgmtActiveGet: 0 + * mgmtPendingGet: 0 + * Done + * @endcode + * @par + * Gets the border agent counters. + * @sa otBorderAgentGetCounters + */ + else if (aArgs[0] == "counters") + { + OutputBorderAgentCounters(*otBorderAgentGetCounters(GetInstancePtr())); + } else { ExitNow(error = OT_ERROR_INVALID_COMMAND); @@ -550,6 +580,28 @@ exit: return error; } +void Interpreter::OutputBorderAgentCounters(const otBorderAgentCounters &aCounters) +{ +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + OutputLine("epskcActivation: %lu ", ToUlong(aCounters.mEpskcActivations)); + OutputLine("epskcApiDeactivation: %lu ", ToUlong(aCounters.mEpskcDeactivationClears)); + OutputLine("epskcTimeoutDeactivation: %lu ", ToUlong(aCounters.mEpskcDeactivationTimeouts)); + OutputLine("epskcMaxAttemptDeactivation: %lu ", ToUlong(aCounters.mEpskcDeactivationMaxAttempts)); + OutputLine("epskcDisconnectDeactivation: %lu ", ToUlong(aCounters.mEpskcDeactivationDisconnects)); + OutputLine("epskcInvalidBaStateError: %lu ", ToUlong(aCounters.mEpskcInvalidBaStateErrors)); + OutputLine("epskcInvalidArgsError: %lu ", ToUlong(aCounters.mEpskcInvalidArgsErrors)); + OutputLine("epskcStartSecureSessionError: %lu ", ToUlong(aCounters.mEpskcStartSecureSessionErrors)); + OutputLine("epskcSecureSessionSuccess: %lu ", ToUlong(aCounters.mEpskcSecureSessionSuccesses)); + OutputLine("epskcSecureSessionFailure: %lu ", ToUlong(aCounters.mEpskcSecureSessionFailures)); + OutputLine("epskcCommissionerPetition: %lu ", ToUlong(aCounters.mEpskcCommissionerPetitions)); +#endif + OutputLine("pskcSecureSessionSuccess: %lu ", ToUlong(aCounters.mPskcSecureSessionSuccesses)); + OutputLine("pskcSecureSessionFailure: %lu ", ToUlong(aCounters.mPskcSecureSessionFailures)); + OutputLine("pskcCommissionerPetition: %lu ", ToUlong(aCounters.mPskcCommissionerPetitions)); + OutputLine("mgmtActiveGet: %lu ", ToUlong(aCounters.mMgmtActiveGets)); + OutputLine("mgmtPendingGet: %lu", ToUlong(aCounters.mMgmtPendingGets)); +} + #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void *aContext) { diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index c8e3e4d87..a8d89a4dd 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -40,6 +40,7 @@ #include +#include #include #include #include @@ -314,9 +315,12 @@ private: void HandleSntpResponse(uint64_t aTime, otError aResult); #endif -#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE + void OutputBorderAgentCounters(const otBorderAgentCounters &aCounters); +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE static void HandleBorderAgentEphemeralKeyStateChange(void *aContext); void HandleBorderAgentEphemeralKeyStateChange(void); +#endif #endif static void HandleDetachGracefullyResult(void *aContext); diff --git a/src/cli/cli_coap_secure.cpp b/src/cli/cli_coap_secure.cpp index 2ff4f6819..e0e5115b8 100644 --- a/src/cli/cli_coap_secure.cpp +++ b/src/cli/cli_coap_secure.cpp @@ -210,7 +210,7 @@ exit: * Starts the CoAP Secure service. @moreinfo{@coaps}. * @sa otCoapSecureStart * @sa otCoapSecureSetSslAuthMode - * @sa otCoapSecureSetClientConnectedCallback + * @sa otCoapSecureSetClientConnectEventCallback */ template <> otError CoapSecure::Process(Arg aArgs[]) { @@ -235,7 +235,7 @@ template <> otError CoapSecure::Process(Arg aArgs[]) } otCoapSecureSetSslAuthMode(GetInstancePtr(), verifyPeerCert); - otCoapSecureSetClientConnectedCallback(GetInstancePtr(), &CoapSecure::HandleConnected, this); + otCoapSecureSetClientConnectEventCallback(GetInstancePtr(), &CoapSecure::HandleConnectEvent, this); #if CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER otCoapSecureSetDefaultHandler(GetInstancePtr(), &CoapSecure::DefaultHandler, this); @@ -629,7 +629,7 @@ template <> otError CoapSecure::Process(Arg aArgs[]) SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); } - SuccessOrExit(error = otCoapSecureConnect(GetInstancePtr(), &sockaddr, &CoapSecure::HandleConnected, this)); + SuccessOrExit(error = otCoapSecureConnect(GetInstancePtr(), &sockaddr, &CoapSecure::HandleConnectEvent, this)); exit: return error; @@ -783,14 +783,14 @@ void CoapSecure::Stop(void) otCoapSecureStop(GetInstancePtr()); } -void CoapSecure::HandleConnected(bool aConnected, void *aContext) +void CoapSecure::HandleConnectEvent(otCoapSecureConnectEvent aEvent, void *aContext) { - static_cast(aContext)->HandleConnected(aConnected); + static_cast(aContext)->HandleConnectEvent(aEvent); } -void CoapSecure::HandleConnected(bool aConnected) +void CoapSecure::HandleConnectEvent(otCoapSecureConnectEvent aEvent) { - if (aConnected) + if (aEvent == OT_COAP_SECURE_CONNECTED) { OutputLine("coaps connected"); } diff --git a/src/cli/cli_coap_secure.hpp b/src/cli/cli_coap_secure.hpp index 92a7a95e5..fb1e8e399 100644 --- a/src/cli/cli_coap_secure.hpp +++ b/src/cli/cli_coap_secure.hpp @@ -137,8 +137,8 @@ private: void DefaultHandler(otMessage *aMessage, const otMessageInfo *aMessageInfo); #endif // CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER - static void HandleConnected(bool aConnected, void *aContext); - void HandleConnected(bool aConnected); + static void HandleConnectEvent(otCoapSecureConnectEvent aEvent, void *aContext); + void HandleConnectEvent(otCoapSecureConnectEvent aEvent); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE otCoapBlockwiseResource mResource; diff --git a/src/core/api/border_agent_api.cpp b/src/core/api/border_agent_api.cpp index c6d321ee9..98a7bb37b 100644 --- a/src/core/api/border_agent_api.cpp +++ b/src/core/api/border_agent_api.cpp @@ -110,4 +110,9 @@ void otBorderAgentSetEphemeralKeyCallback(otInstance *aIns #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE +const otBorderAgentCounters *otBorderAgentGetCounters(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetCounters(); +} + #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE diff --git a/src/core/api/coap_secure_api.cpp b/src/core/api/coap_secure_api.cpp index e5ac4c014..d83afc3cf 100644 --- a/src/core/api/coap_secure_api.cpp +++ b/src/core/api/coap_secure_api.cpp @@ -178,11 +178,11 @@ void otCoapSecureRemoveResource(otInstance *aInstance, otCoapResource *aResource AsCoreType(aInstance).GetApplicationCoapSecure().RemoveResource(AsCoreType(aResource)); } -void otCoapSecureSetClientConnectedCallback(otInstance *aInstance, - otHandleCoapSecureClientConnect aHandler, - void *aContext) +void otCoapSecureSetClientConnectEventCallback(otInstance *aInstance, + otHandleCoapSecureClientConnect aHandler, + void *aContext) { - AsCoreType(aInstance).GetApplicationCoapSecure().SetConnectedCallback(aHandler, aContext); + AsCoreType(aInstance).GetApplicationCoapSecure().SetConnectEventCallback(aHandler, aContext); } void otCoapSecureSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandler, void *aContext) diff --git a/src/core/coap/coap_secure.cpp b/src/core/coap/coap_secure.cpp index 875d6c0f6..7b54058ca 100644 --- a/src/core/coap/coap_secure.cpp +++ b/src/core/coap/coap_secure.cpp @@ -85,8 +85,8 @@ Error CoapSecure::Open(uint16_t aMaxAttempts, AutoStopCallback aCallback, void * SuccessOrExit(mDtls.SetMaxConnectionAttempts(aMaxAttempts, HandleDtlsAutoClose, this)); mAutoStopCallback.Set(aCallback, aContext); - mConnectedCallback.Clear(); - SuccessOrExit(mDtls.Open(HandleDtlsReceive, HandleDtlsConnected, this)); + mConnectEventCallback.Clear(); + SuccessOrExit(mDtls.Open(HandleDtlsReceive, HandleDtlsConnectEvent, this)); error = kErrorNone; @@ -102,9 +102,9 @@ void CoapSecure::Stop(void) ClearRequestsAndResponses(); } -Error CoapSecure::Connect(const Ip6::SockAddr &aSockAddr, ConnectedCallback aCallback, void *aContext) +Error CoapSecure::Connect(const Ip6::SockAddr &aSockAddr, ConnectEventCallback aCallback, void *aContext) { - mConnectedCallback.Set(aCallback, aContext); + mConnectEventCallback.Set(aCallback, aContext); return mDtls.Connect(aSockAddr); } @@ -178,12 +178,15 @@ Error CoapSecure::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageIn return kErrorNone; } -void CoapSecure::HandleDtlsConnected(void *aContext, bool aConnected) +void CoapSecure::HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext) { - return static_cast(aContext)->HandleDtlsConnected(aConnected); + return static_cast(aContext)->HandleDtlsConnectEvent(aEvent); } -void CoapSecure::HandleDtlsConnected(bool aConnected) { mConnectedCallback.InvokeIfSet(aConnected); } +void CoapSecure::HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent) +{ + mConnectEventCallback.InvokeIfSet(aEvent); +} void CoapSecure::HandleDtlsAutoClose(void *aContext) { diff --git a/src/core/coap/coap_secure.hpp b/src/core/coap/coap_secure.hpp index 0565ef562..a2517adc3 100644 --- a/src/core/coap/coap_secure.hpp +++ b/src/core/coap/coap_secure.hpp @@ -53,13 +53,10 @@ class CoapSecure : public CoapBase { public: /** - * Pointer is called once DTLS connection is established. - * - * @param[in] aConnected TRUE if a connection was established, FALSE otherwise. - * @param[in] aContext A pointer to arbitrary context information. + * Function pointer which is called reporting a connection event (when connection established or disconnected) * */ - typedef void (*ConnectedCallback)(bool aConnected, void *aContext); + typedef otHandleCoapSecureClientConnect ConnectEventCallback; /** * Callback to notify when the agent is automatically stopped due to reaching the maximum number of connection @@ -122,9 +119,9 @@ public: * @param[in] aContext A pointer to arbitrary context information. * */ - void SetConnectedCallback(ConnectedCallback aCallback, void *aContext) + void SetConnectEventCallback(ConnectEventCallback aCallback, void *aContext) { - mConnectedCallback.Set(aCallback, aContext); + mConnectEventCallback.Set(aCallback, aContext); } /** @@ -143,7 +140,7 @@ public: * @retval kErrorNone Successfully started DTLS connection. * */ - Error Connect(const Ip6::SockAddr &aSockAddr, ConnectedCallback aCallback, void *aContext); + Error Connect(const Ip6::SockAddr &aSockAddr, ConnectEventCallback aCallback, void *aContext); /** * Indicates whether or not the DTLS session is active. @@ -421,8 +418,8 @@ private: } Error Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleDtlsConnected(void *aContext, bool aConnected); - void HandleDtlsConnected(bool aConnected); + static void HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent); static void HandleDtlsAutoClose(void *aContext); void HandleDtlsAutoClose(void); @@ -433,11 +430,11 @@ private: static void HandleTransmit(Tasklet &aTasklet); void HandleTransmit(void); - MeshCoP::SecureTransport mDtls; - Callback mConnectedCallback; - Callback mAutoStopCallback; - ot::MessageQueue mTransmitQueue; - TaskletContext mTransmitTask; + MeshCoP::SecureTransport mDtls; + Callback mConnectEventCallback; + Callback mAutoStopCallback; + ot::MessageQueue mTransmitQueue; + TaskletContext mTransmitTask; }; } // namespace Coap diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index f4eb18351..6ae830f50 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -205,6 +205,17 @@ void BorderAgent::HandleCoapResponse(const ForwardContext &aForwardContext, IgnoreError(Get().AddReceiver(mUdpReceiver)); mState = kStateAccepted; +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + if (mUsingEphemeralKey) + { + mCounters.mEpskcCommissionerPetitions++; + } + else +#endif + { + mCounters.mPskcCommissionerPetitions++; + } + LogInfo("Commissioner accepted - SessionId:%u ALOC:%s", sessionId, mCommissionerAloc.GetAddress().ToString().AsCString()); } @@ -252,6 +263,7 @@ BorderAgent::BorderAgent(Instance &aInstance) #endif { mCommissionerAloc.InitAsThreadOriginMeshLocal(); + ClearAllBytes(mCounters); } #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE @@ -468,6 +480,7 @@ void BorderAgent::HandleTmf(Coap::Message &aMessage, const template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { HandleTmfDatasetGet(aMessage, aMessageInfo, Dataset::kActive); + mCounters.mMgmtActiveGets++; } template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) @@ -478,6 +491,7 @@ template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { HandleTmfDatasetGet(aMessage, aMessageInfo, Dataset::kPending); + mCounters.mMgmtPendingGets++; } template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) @@ -635,18 +649,28 @@ exit: FreeMessageOnError(response, error); } -void BorderAgent::HandleConnected(bool aConnected, void *aContext) +void BorderAgent::HandleConnected(SecureTransport::ConnectEvent aEvent, void *aContext) { - static_cast(aContext)->HandleConnected(aConnected); + static_cast(aContext)->HandleConnected(aEvent); } -void BorderAgent::HandleConnected(bool aConnected) +void BorderAgent::HandleConnected(SecureTransport::ConnectEvent aEvent) { - if (aConnected) + if (aEvent == SecureTransport::kConnected) { LogInfo("SecureSession connected"); mState = kStateConnected; mTimer.Start(kKeepAliveTimeout); +#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + if (mUsingEphemeralKey) + { + mCounters.mEpskcSecureSessionSuccesses++; + } + else +#endif + { + mCounters.mPskcSecureSessionSuccesses++; + } } else { @@ -658,12 +682,24 @@ void BorderAgent::HandleConnected(bool aConnected) if (mUsingEphemeralKey) { RestartAfterRemovingEphemeralKey(); + if (aEvent == SecureTransport::kDisconnectedError) + { + mCounters.mEpskcSecureSessionFailures++; + } + if (aEvent == SecureTransport::kDisconnectedPeerClosed) + { + mCounters.mEpskcDeactivationDisconnects++; + } } else #endif { mState = kStateStarted; mUdpProxyPort = 0; + if (aEvent == SecureTransport::kDisconnectedError) + { + mCounters.mPskcSecureSessionFailures++; + } } } } @@ -702,7 +738,7 @@ Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLen SuccessOrExit(error = Get().SetPsk(aPsk, aPskLength)); - Get().SetConnectedCallback(HandleConnected, this); + Get().SetConnectEventCallback(HandleConnected, this); mState = kStateStarted; mUdpProxyPort = 0; @@ -776,6 +812,7 @@ Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, ui { mUsingEphemeralKey = false; IgnoreError(Start(mOldUdpPort)); + mCounters.mEpskcStartSecureSessionErrors++; ExitNow(); } @@ -789,10 +826,23 @@ Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, ui aTimeout = Min(aTimeout, kMaxEphemeralKeyTimeout); mEphemeralKeyTimer.Start(aTimeout); + mCounters.mEpskcActivations++; LogInfo("Allow ephemeral key for %lu msec on port %u", ToUlong(aTimeout), GetUdpPort()); exit: + switch (error) + { + case kErrorInvalidState: + mCounters.mEpskcInvalidBaStateErrors++; + break; + case kErrorInvalidArgs: + mCounters.mEpskcInvalidArgsErrors++; + break; + default: + break; + } + return error; } @@ -801,6 +851,16 @@ void BorderAgent::ClearEphemeralKey(void) VerifyOrExit(mUsingEphemeralKey); LogInfo("Clearing ephemeral key"); + + if (mEphemeralKeyTimer.IsRunning()) + { + mCounters.mEpskcDeactivationClears++; + } + else + { + mCounters.mEpskcDeactivationTimeouts++; + } + mEphemeralKeyTimer.Stop(); switch (mState) @@ -847,6 +907,7 @@ void BorderAgent::HandleSecureAgentStopped(void) { LogInfo("Reached max allowed connection attempts with ephemeral key"); RestartAfterRemovingEphemeralKey(); + mCounters.mEpskcDeactivationMaxAttempts++; } #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index 4e52ad179..1cc57e55f 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -238,6 +238,14 @@ public: #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + /** + * Gets the set of border agent counters. + * + * @returns The border agent counters. + * + */ + const otBorderAgentCounters *GetCounters(void) { return &mCounters; } + /** * Returns the UDP Proxy port to which the commissioner is currently * bound. @@ -285,8 +293,8 @@ private: void SendErrorMessage(const ForwardContext &aForwardContext, Error aError); void SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError); - static void HandleConnected(bool aConnected, void *aContext); - void HandleConnected(bool aConnected); + static void HandleConnected(SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleConnected(SecureTransport::ConnectEvent aEvent); template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); @@ -333,6 +341,7 @@ private: EphemeralKeyTask mEphemeralKeyTask; Callback mEphemeralKeyCallback; #endif + otBorderAgentCounters mCounters; }; DeclareTmfHandler(BorderAgent, kUriRelayRx); diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index ce379dc99..8d71ca311 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -127,19 +127,20 @@ exit: return; } -void Commissioner::HandleSecureAgentConnected(bool aConnected, void *aContext) +void Commissioner::HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent, void *aContext) { - static_cast(aContext)->HandleSecureAgentConnected(aConnected); + static_cast(aContext)->HandleSecureAgentConnectEvent(aEvent); } -void Commissioner::HandleSecureAgentConnected(bool aConnected) +void Commissioner::HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent) { - if (!aConnected) + bool isConnected = (aEvent == SecureTransport::kConnected); + if (!isConnected) { mJoinerSessionTimer.Stop(); } - SignalJoinerEvent(aConnected ? kJoinerEventConnected : kJoinerEventEnd, mActiveJoiner); + SignalJoinerEvent(isConnected ? kJoinerEventConnected : kJoinerEventEnd, mActiveJoiner); } Commissioner::Joiner *Commissioner::GetUnusedJoinerEntry(void) @@ -287,7 +288,7 @@ Error Commissioner::Start(StateCallback aStateCallback, JoinerCallback aJoinerCa #endif SuccessOrExit(error = Get().Start(SendRelayTransmit, this)); - Get().SetConnectedCallback(&Commissioner::HandleSecureAgentConnected, this); + Get().SetConnectEventCallback(&Commissioner::HandleSecureAgentConnectEvent, this); mStateCallback.Set(aStateCallback, aCallbackContext); mJoinerCallback.Set(aJoinerCallback, aCallbackContext); diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp index fda668c44..adfff53a8 100644 --- a/src/core/meshcop/commissioner.hpp +++ b/src/core/meshcop/commissioner.hpp @@ -440,8 +440,8 @@ private: Error aResult); void HandleLeaderKeepAliveResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleSecureAgentConnected(bool aConnected, void *aContext); - void HandleSecureAgentConnected(bool aConnected); + static void HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent); template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index a1ff9bf1c..dcd55aa3a 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -382,16 +382,16 @@ exit: return error; } -void Joiner::HandleSecureCoapClientConnect(bool aConnected, void *aContext) +void Joiner::HandleSecureCoapClientConnect(SecureTransport::ConnectEvent aEvent, void *aContext) { - static_cast(aContext)->HandleSecureCoapClientConnect(aConnected); + static_cast(aContext)->HandleSecureCoapClientConnect(aEvent); } -void Joiner::HandleSecureCoapClientConnect(bool aConnected) +void Joiner::HandleSecureCoapClientConnect(SecureTransport::ConnectEvent aEvent) { VerifyOrExit(mState == kStateConnect); - if (aConnected) + if (aEvent == SecureTransport::kConnected) { SetState(kStateConnected); SendJoinerFinalize(); diff --git a/src/core/meshcop/joiner.hpp b/src/core/meshcop/joiner.hpp index ce430a737..c260258bd 100644 --- a/src/core/meshcop/joiner.hpp +++ b/src/core/meshcop/joiner.hpp @@ -199,8 +199,8 @@ private: static void HandleDiscoverResult(Mle::DiscoverScanner::ScanResult *aResult, void *aContext); void HandleDiscoverResult(Mle::DiscoverScanner::ScanResult *aResult); - static void HandleSecureCoapClientConnect(bool aConnected, void *aContext); - void HandleSecureCoapClientConnect(bool aConnected); + static void HandleSecureCoapClientConnect(SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleSecureCoapClientConnect(SecureTransport::ConnectEvent aEvent); static void HandleJoinerFinalizeResponse(void *aContext, otMessage *aMessage, diff --git a/src/core/meshcop/secure_transport.cpp b/src/core/meshcop/secure_transport.cpp index bfb5db6d9..3cb0333f7 100644 --- a/src/core/meshcop/secure_transport.cpp +++ b/src/core/meshcop/secure_transport.cpp @@ -1043,15 +1043,15 @@ void SecureTransport::HandleTimer(void) if ((mMaxConnectionAttempts > 0) && (mRemainingConnectionAttempts == 0)) { Close(); - mConnectedCallback.InvokeIfSet(false); + mConnectEvent = kDisconnectedMaxAttempts; mAutoCloseCallback.InvokeIfSet(); } else { SetState(kStateOpen); mTimer.Stop(); - mConnectedCallback.InvokeIfSet(false); } + mConnectedCallback.InvokeIfSet(mConnectEvent); } } @@ -1070,7 +1070,8 @@ void SecureTransport::Process(void) if (mSsl.MBEDTLS_PRIVATE(state) == MBEDTLS_SSL_HANDSHAKE_OVER) { SetState(kStateConnected); - mConnectedCallback.InvokeIfSet(true); + mConnectEvent = kConnected; + mConnectedCallback.InvokeIfSet(mConnectEvent); } } else @@ -1092,6 +1093,7 @@ void SecureTransport::Process(void) { case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: mbedtls_ssl_close_notify(&mSsl); + mConnectEvent = kDisconnectedPeerClosed; ExitNow(shouldDisconnect = true); OT_UNREACHABLE_CODE(break); @@ -1100,6 +1102,7 @@ void SecureTransport::Process(void) case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: mbedtls_ssl_close_notify(&mSsl); + mConnectEvent = kDisconnectedError; ExitNow(shouldDisconnect = true); OT_UNREACHABLE_CODE(break); @@ -1108,6 +1111,7 @@ void SecureTransport::Process(void) { mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC); + mConnectEvent = kDisconnectedError; ExitNow(shouldDisconnect = true); } @@ -1118,6 +1122,7 @@ void SecureTransport::Process(void) { mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + mConnectEvent = kDisconnectedError; ExitNow(shouldDisconnect = true); } diff --git a/src/core/meshcop/secure_transport.hpp b/src/core/meshcop/secure_transport.hpp index b5749a951..a4f577775 100644 --- a/src/core/meshcop/secure_transport.hpp +++ b/src/core/meshcop/secure_transport.hpp @@ -69,6 +69,8 @@ #endif #endif +#include + #include "common/callback.hpp" #include "common/locator.hpp" #include "common/log.hpp" @@ -87,8 +89,22 @@ namespace MeshCoP { class SecureTransport : public InstanceLocator { public: + typedef otCoapSecureConnectEvent ConnectEvent; ///< A connect event. + + static constexpr ConnectEvent kConnected = OT_COAP_SECURE_CONNECTED; + static constexpr ConnectEvent kDisconnectedPeerClosed = OT_COAP_SECURE_DISCONNECTED_PEER_CLOSED; + 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 uint8_t kPskMaxLength = 32; ///< Maximum PSK length. + /** + * Function pointer which is called reporting a connection event (when connection established or disconnected) + * + */ + typedef otHandleCoapSecureClientConnect ConnectedHandler; + /** * Initializes the SecureTransport object. * @@ -99,15 +115,6 @@ public: */ explicit SecureTransport(Instance &aInstance, bool aLayerTwoSecurity, bool aDatagramTransport = true); - /** - * Pointer is called when a connection is established or torn down. - * - * @param[in] aContext A pointer to application-specific context. - * @param[in] aConnected TRUE if a connection was established, FALSE otherwise. - * - */ - typedef void (*ConnectedHandler)(void *aContext, bool aConnected); - /** * Pointer is called when data is received from the session. * @@ -670,6 +677,8 @@ private: Message::SubType mMessageSubType; Message::SubType mMessageDefaultSubType; + + ConnectEvent mConnectEvent; }; } // namespace MeshCoP diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp index dab82e125..e18c3fd07 100644 --- a/src/core/radio/ble_secure.cpp +++ b/src/core/radio/ble_secure.cpp @@ -83,7 +83,7 @@ Error BleSecure::Start(ConnectCallback aConnectHandler, ReceiveCallback aReceive SuccessOrExit(error = otPlatBleGapAdvSetData(&GetInstance(), advertisementData, advertisementLen)); SuccessOrExit(error = otPlatBleGapAdvStart(&GetInstance(), OT_BLE_ADV_INTERVAL_DEFAULT)); - SuccessOrExit(error = mTls.Open(&BleSecure::HandleTlsReceive, &BleSecure::HandleTlsConnected, this)); + SuccessOrExit(error = mTls.Open(&BleSecure::HandleTlsReceive, &BleSecure::HandleTlsConnectEvent, this)); SuccessOrExit(error = mTls.Bind(HandleTransport, this)); exit: @@ -321,14 +321,14 @@ Error BleSecure::HandleBleMtuUpdate(uint16_t aMtu) return error; } -void BleSecure::HandleTlsConnected(void *aContext, bool aConnected) +void BleSecure::HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext) { - return static_cast(aContext)->HandleTlsConnected(aConnected); + return static_cast(aContext)->HandleTlsConnectEvent(aEvent); } -void BleSecure::HandleTlsConnected(bool aConnected) +void BleSecure::HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent) { - if (aConnected) + if (aEvent == MeshCoP::SecureTransport::kConnected) { if (mReceivedMessage == nullptr) { @@ -358,7 +358,7 @@ void BleSecure::HandleTlsConnected(bool aConnected) } } - mConnectCallback.InvokeIfSet(&GetInstance(), aConnected, true); + mConnectCallback.InvokeIfSet(&GetInstance(), aEvent == MeshCoP::SecureTransport::kConnected, true); exit: return; diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp index 84c9aa9ee..4ddf351bb 100644 --- a/src/core/radio/ble_secure.hpp +++ b/src/core/radio/ble_secure.hpp @@ -464,8 +464,8 @@ private: static constexpr uint8_t kPacketBufferSize = OT_BLE_ATT_MTU_MAX - kGattOverhead; static constexpr uint16_t kTxBleHandle = 0; // Characteristics Handle for TX (not used) - static void HandleTlsConnected(void *aContext, bool aConnected); - void HandleTlsConnected(bool aConnected); + static void HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext); + void HandleTlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent); static void HandleTlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength); void HandleTlsReceive(uint8_t *aBuf, uint16_t aLength); diff --git a/tests/scripts/thread-cert/border_router/test_ephemeral_key_counters.py b/tests/scripts/thread-cert/border_router/test_ephemeral_key_counters.py new file mode 100755 index 000000000..8bca67951 --- /dev/null +++ b/tests/scripts/thread-cert/border_router/test_ephemeral_key_counters.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +import logging +import unittest + +import config +import thread_cert + +# Test description: +# This test verifies the counters of ephemeral key activations/deactivations. +# +# Topology: +# -------------- +# | +# BR1 +# + +BR1 = 1 + + +class EphemeralKeyCountersTest(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + BR1: { + 'name': 'BR_1', + 'is_otbr': True, + 'version': '1.4', + 'network_name': 'ot-br1', + }, + } + + def test(self): + br1 = self.nodes[BR1] + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 0) + self.assertEqual(counters['epskcApiDeactivation'], 0) + self.assertEqual(counters['epskcTimeoutDeactivation'], 0) + self.assertEqual(counters['epskcMaxAttemptDeactivation'], 0) + self.assertEqual(counters['epskcDisconnectDeactivation'], 0) + self.assertEqual(counters['epskcInvalidBaStateError'], 0) + self.assertEqual(counters['epskcInvalidArgsError'], 0) + self.assertEqual(counters['epskcStartSecureSessionError'], 0) + self.assertEqual(counters['epskcSecureSessionSuccess'], 0) + self.assertEqual(counters['epskcSecureSessionFailure'], 0) + self.assertEqual(counters['epskcCommissionerPetition'], 0) + self.assertEqual(counters['pskcSecureSessionSuccess'], 0) + self.assertEqual(counters['pskcSecureSessionFailure'], 0) + self.assertEqual(counters['pskcCommissionerPetition'], 0) + self.assertEqual(counters['mgmtActiveGet'], 0) + self.assertEqual(counters['mgmtPendingGet'], 0) + + # activate epskc before border agent is up returns an error + br1.set_epskc('123456789') + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 0) + self.assertEqual(counters['epskcInvalidBaStateError'], 1) + + br1.set_active_dataset(updateExisting=True, network_name='ot-br1') + br1.start() + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) + self.assertEqual('leader', br1.get_state()) + + # activate epskc and let it timeout + br1.set_epskc('123456789', 10) + self.simulator.go(1) + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 1) + self.assertEqual(counters['epskcApiDeactivation'], 0) + self.assertEqual(counters['epskcTimeoutDeactivation'], 1) + + # activate epskc and clear it + br1.set_epskc('123456789', 10000) + self.simulator.go(1) + br1.clear_epskc() + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 2) + self.assertEqual(counters['epskcApiDeactivation'], 1) + self.assertEqual(counters['epskcTimeoutDeactivation'], 1) + + # set epskc with invalid passcode + br1.set_epskc('123') + self.simulator.go(1) + + counters = br1.get_border_agent_counters() + self.assertEqual(counters['epskcActivation'], 2) + self.assertEqual(counters['epskcApiDeactivation'], 1) + self.assertEqual(counters['epskcTimeoutDeactivation'], 1) + self.assertEqual(counters['epskcInvalidArgsError'], 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 9139541c8..da1d96b1f 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1451,6 +1451,31 @@ class NodeImpl: self.send_command(cmd) self._expect_done() + def set_epskc(self, keystring: str, timeout=120000, port=0): + cmd = 'ba ephemeralkey set ' + keystring + ' ' + str(timeout) + ' ' + str(port) + self.send_command(cmd) + self._expect(r"(Done|Error .*)") + + def clear_epskc(self): + cmd = 'ba ephemeralkey clear' + self.send_command(cmd) + self._expect_done() + + def get_border_agent_counters(self): + cmd = 'ba counters' + self.send_command(cmd) + result = self._expect_command_output() + + counters = {} + for line in result: + m = re.match(r'(\w+)\: (\d+)', line) + if m: + counter_name = m.group(1) + counter_value = m.group(2) + + counters[counter_name] = int(counter_value) + return counters + def _encode_txt_entry(self, entry): """Encodes the TXT entry to the DNS-SD TXT record format as a HEX string.