mirror of
https://github.com/espressif/openthread.git
synced 2026-08-03 01:17:46 +00:00
[coap-secure] fix and test to validate max connection attempt limit (#9803)
This commit contains the following - It fixes `SecureTransport` to ensure we do the max connection attempt check when transitioning from `kStateCloseNotify`. - It adds new public `otCoapSecure` APIs to start the agent with a given max connection attempts, and to check the agent's state, whether it is connected, connecting or closed. - It adds CLI commands under `coaps` for the new APIs. - It adds `test-026-coaps-conn-limit.py` to validate the max connection attempt limit of CoAP secure agent.
This commit is contained in:
@@ -76,6 +76,15 @@ extern "C" {
|
||||
*/
|
||||
typedef void (*otHandleCoapSecureClientConnect)(bool aConnected, void *aContext);
|
||||
|
||||
/**
|
||||
* Callback function pointer to notify when the CoAP secure agent is automatically stopped due to reaching the maximum
|
||||
* number of connection attempts.
|
||||
*
|
||||
* @param[in] aContext A pointer to arbitrary context information.
|
||||
*
|
||||
*/
|
||||
typedef void (*otCoapSecureAutoStopCallback)(void *aContext);
|
||||
|
||||
/**
|
||||
* Starts the CoAP Secure service.
|
||||
*
|
||||
@@ -87,6 +96,26 @@ typedef void (*otHandleCoapSecureClientConnect)(bool aConnected, void *aContext)
|
||||
*/
|
||||
otError otCoapSecureStart(otInstance *aInstance, uint16_t aPort);
|
||||
|
||||
/**
|
||||
* Starts the CoAP secure service and sets the maximum number of allowed connection attempts before stopping the
|
||||
* agent automatically.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aPort The local UDP port to bind to.
|
||||
* @param[in] aMaxAttempts Maximum number of allowed connection request attempts. Zero indicates no limit.
|
||||
* @param[in] aCallback Callback to notify if max number of attempts has reached and agent is stopped.
|
||||
* @param[in] aContext A pointer to arbitrary context to use with @p aCallback.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully started the CoAP agent.
|
||||
* @retval OT_ERROR_ALREADY Already started.
|
||||
*
|
||||
*/
|
||||
otError otCoapSecureStartWithMaxConnAttempts(otInstance *aInstance,
|
||||
uint16_t aPort,
|
||||
uint16_t aMaxAttempts,
|
||||
otCoapSecureAutoStopCallback aCallback,
|
||||
void *aContext);
|
||||
|
||||
/**
|
||||
* Stops the CoAP Secure server.
|
||||
*
|
||||
@@ -230,6 +259,17 @@ bool otCoapSecureIsConnected(otInstance *aInstance);
|
||||
*/
|
||||
bool otCoapSecureIsConnectionActive(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Indicates whether or not the DTLS session is closed.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
*
|
||||
* @retval TRUE The DTLS session is closed.
|
||||
* @retval FALSE The DTLS session is not closed.
|
||||
*
|
||||
*/
|
||||
bool otCoapSecureIsClosed(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Sends a CoAP request block-wise over secure DTLS connection.
|
||||
*
|
||||
|
||||
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (391)
|
||||
#define OPENTHREAD_API_VERSION (392)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
+36
-1
@@ -87,6 +87,9 @@ coaps response sent
|
||||
- [delete](#delete-uri-path-type-payload)
|
||||
- [disconnect](#disconnect)
|
||||
- [get](#get-uri-path-type)
|
||||
- [isclosed](#isclosed)
|
||||
- [isconnactive](#isconnactive)
|
||||
- [isconnected](#isconnected)
|
||||
- [post](#post-uri-path-type-payload)
|
||||
- [psk](#psk-psk-pskid)
|
||||
- [put](#put-uri-path-type-payload)
|
||||
@@ -102,11 +105,13 @@ coaps response sent
|
||||
|
||||
```bash
|
||||
> coaps help
|
||||
help
|
||||
connect
|
||||
delete
|
||||
disconnect
|
||||
get
|
||||
isclosed
|
||||
isconnactive
|
||||
isconnected
|
||||
post
|
||||
psk
|
||||
put
|
||||
@@ -251,6 +256,36 @@ Stops the application coaps service.
|
||||
Done
|
||||
```
|
||||
|
||||
### isconnected
|
||||
|
||||
Indicates whether or not the CoAP secure service is connected.
|
||||
|
||||
```bash
|
||||
> coaps isconnected
|
||||
yes
|
||||
Done
|
||||
```
|
||||
|
||||
### isconnactive
|
||||
|
||||
Indicates whether or not the CoAP secure service connection is active (already connected or establishing a connection).
|
||||
|
||||
```bash
|
||||
> coaps isconnactive
|
||||
yes
|
||||
Done
|
||||
```
|
||||
|
||||
### isclosed
|
||||
|
||||
Indicates whether or not the CoAP secure service is closed.
|
||||
|
||||
```bash
|
||||
> coaps isclosed
|
||||
no
|
||||
Done
|
||||
```
|
||||
|
||||
### x509
|
||||
|
||||
Set DTLS ciphersuite to `TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8`.
|
||||
|
||||
@@ -149,8 +149,9 @@ exit:
|
||||
|
||||
template <> otError CoapSecure::Process<Cmd("start")>(Arg aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
bool verifyPeerCert = true;
|
||||
otError error = OT_ERROR_NONE;
|
||||
bool verifyPeerCert = true;
|
||||
uint16_t maxConnAttempts = 0;
|
||||
|
||||
if (!aArgs[0].IsEmpty())
|
||||
{
|
||||
@@ -158,9 +159,13 @@ template <> otError CoapSecure::Process<Cmd("start")>(Arg aArgs[])
|
||||
{
|
||||
verifyPeerCert = false;
|
||||
}
|
||||
else if (aArgs[0] == "true")
|
||||
{
|
||||
verifyPeerCert = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
VerifyOrExit(aArgs[0] == "true", error = OT_ERROR_INVALID_ARGS);
|
||||
SuccessOrExit(error = aArgs[0].ParseAsUint16(maxConnAttempts));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +176,8 @@ template <> otError CoapSecure::Process<Cmd("start")>(Arg aArgs[])
|
||||
otCoapSecureSetDefaultHandler(GetInstancePtr(), &CoapSecure::DefaultHandler, this);
|
||||
#endif
|
||||
|
||||
error = otCoapSecureStart(GetInstancePtr(), OT_DEFAULT_COAP_SECURE_PORT);
|
||||
error = otCoapSecureStartWithMaxConnAttempts(GetInstancePtr(), OT_DEFAULT_COAP_SECURE_PORT, maxConnAttempts,
|
||||
nullptr, nullptr);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -200,6 +206,32 @@ template <> otError CoapSecure::Process<Cmd("stop")>(Arg aArgs[])
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
template <> otError CoapSecure::Process<Cmd("isclosed")>(Arg aArgs[])
|
||||
{
|
||||
return ProcessIsRequest(aArgs, otCoapSecureIsClosed);
|
||||
}
|
||||
|
||||
template <> otError CoapSecure::Process<Cmd("isconnected")>(Arg aArgs[])
|
||||
{
|
||||
return ProcessIsRequest(aArgs, otCoapSecureIsConnected);
|
||||
}
|
||||
|
||||
template <> otError CoapSecure::Process<Cmd("isconnactive")>(Arg aArgs[])
|
||||
{
|
||||
return ProcessIsRequest(aArgs, otCoapSecureIsConnectionActive);
|
||||
}
|
||||
|
||||
otError CoapSecure::ProcessIsRequest(Arg aArgs[], bool (*IsChecker)(otInstance *))
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
OutputLine("%s", IsChecker(GetInstancePtr()) ? "yes" : "no");
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
template <> otError CoapSecure::Process<Cmd("get")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); }
|
||||
|
||||
template <> otError CoapSecure::Process<Cmd("post")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); }
|
||||
@@ -442,11 +474,13 @@ otError CoapSecure::Process(Arg aArgs[])
|
||||
}
|
||||
|
||||
static constexpr Command kCommands[] = {
|
||||
CmdEntry("connect"), CmdEntry("delete"), CmdEntry("disconnect"), CmdEntry("get"), CmdEntry("post"),
|
||||
CmdEntry("connect"), CmdEntry("delete"), CmdEntry("disconnect"), CmdEntry("get"),
|
||||
CmdEntry("isclosed"), CmdEntry("isconnactive"), CmdEntry("isconnected"), CmdEntry("post"),
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
|
||||
CmdEntry("psk"),
|
||||
#endif
|
||||
CmdEntry("put"), CmdEntry("resource"), CmdEntry("set"), CmdEntry("start"), CmdEntry("stop"),
|
||||
CmdEntry("put"), CmdEntry("resource"), CmdEntry("set"), CmdEntry("start"),
|
||||
CmdEntry("stop"),
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
CmdEntry("x509"),
|
||||
#endif
|
||||
|
||||
@@ -103,6 +103,7 @@ private:
|
||||
template <CommandId kCommandId> otError Process(Arg aArgs[]);
|
||||
|
||||
otError ProcessRequest(Arg aArgs[], otCoapCode aCoapCode);
|
||||
otError ProcessIsRequest(Arg aArgs[], bool (*IsChecker)(otInstance *));
|
||||
|
||||
void Stop(void);
|
||||
|
||||
|
||||
@@ -50,6 +50,15 @@ otError otCoapSecureStart(otInstance *aInstance, uint16_t aPort)
|
||||
return AsCoreType(aInstance).GetApplicationCoapSecure().Start(aPort);
|
||||
}
|
||||
|
||||
otError otCoapSecureStartWithMaxConnAttempts(otInstance *aInstance,
|
||||
uint16_t aPort,
|
||||
uint16_t aMaxAttempts,
|
||||
otCoapSecureAutoStopCallback aCallback,
|
||||
void *aContext)
|
||||
{
|
||||
return AsCoreType(aInstance).GetApplicationCoapSecure().Start(aPort, aMaxAttempts, aCallback, aContext);
|
||||
}
|
||||
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
void otCoapSecureSetCertificate(otInstance *aInstance,
|
||||
const uint8_t *aX509Cert,
|
||||
@@ -122,6 +131,8 @@ bool otCoapSecureIsConnectionActive(otInstance *aInstance)
|
||||
return AsCoreType(aInstance).GetApplicationCoapSecure().IsConnectionActive();
|
||||
}
|
||||
|
||||
bool otCoapSecureIsClosed(otInstance *aInstance) { return AsCoreType(aInstance).GetApplicationCoapSecure().IsClosed(); }
|
||||
|
||||
void otCoapSecureStop(otInstance *aInstance) { AsCoreType(aInstance).GetApplicationCoapSecure().Stop(); }
|
||||
|
||||
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
|
||||
|
||||
@@ -65,10 +65,8 @@ public:
|
||||
* Callback to notify when the agent is automatically stopped due to reaching the maximum number of connection
|
||||
* attempts.
|
||||
*
|
||||
* @param[in] aContext A pointer to arbitrary context information.
|
||||
*
|
||||
*/
|
||||
typedef void (*AutoStopCallback)(void *aContext);
|
||||
typedef otCoapSecureAutoStopCallback AutoStopCallback;
|
||||
|
||||
/**
|
||||
* Initializes the object.
|
||||
@@ -165,6 +163,15 @@ public:
|
||||
*/
|
||||
bool IsConnected(void) const { return mDtls.IsConnected(); }
|
||||
|
||||
/**
|
||||
* Indicates whether or not the DTLS session is closed.
|
||||
*
|
||||
* @retval TRUE The DTLS session is closed
|
||||
* @retval FALSE The DTLS session is not closed.
|
||||
*
|
||||
*/
|
||||
bool IsClosed(void) const { return mDtls.IsClosed(); }
|
||||
|
||||
/**
|
||||
* Stops the DTLS connection.
|
||||
*
|
||||
|
||||
@@ -1053,9 +1053,18 @@ void SecureTransport::HandleTimer(void)
|
||||
}
|
||||
else if (IsStateCloseNotify())
|
||||
{
|
||||
SetState(kStateOpen);
|
||||
mTimer.Stop();
|
||||
mConnectedCallback.InvokeIfSet(false);
|
||||
if ((mMaxConnectionAttempts > 0) && (mRemainingConnectionAttempts == 0))
|
||||
{
|
||||
Close();
|
||||
mConnectedCallback.InvokeIfSet(false);
|
||||
mAutoCloseCallback.InvokeIfSet();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetState(kStateOpen);
|
||||
mTimer.Stop();
|
||||
mConnectedCallback.InvokeIfSet(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -234,6 +234,15 @@ public:
|
||||
*/
|
||||
bool IsConnected(void) const { return mState == kStateConnected; }
|
||||
|
||||
/**
|
||||
* Indicates whether or not the session is closed.
|
||||
*
|
||||
* @retval TRUE The session is closed.
|
||||
* @retval FALSE The session is not closed.
|
||||
*
|
||||
*/
|
||||
bool IsClosed(void) const { return mState == kStateClosed; }
|
||||
|
||||
/**
|
||||
* Disconnects the session.
|
||||
*
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
#!/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.
|
||||
|
||||
from cli import verify
|
||||
from cli import verify_within
|
||||
import cli
|
||||
import time
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test description:
|
||||
#
|
||||
# Validate CoaP secure agent related to the maximum number of allowed connection attempts before the socket is a
|
||||
# automatically closed and the agent is stopped.
|
||||
#
|
||||
#
|
||||
# Network topology
|
||||
#
|
||||
# r1 ---- r2
|
||||
#
|
||||
|
||||
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
|
||||
print('-' * 120)
|
||||
print('Starting \'{}\''.format(test_name))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Creating `cli.Node` instances
|
||||
|
||||
speedup = 40
|
||||
cli.Node.set_time_speedup_factor(speedup)
|
||||
|
||||
r1 = cli.Node()
|
||||
r2 = cli.Node()
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Form topology
|
||||
|
||||
r1.form('coaps-conn-lmt')
|
||||
r2.join(r1)
|
||||
|
||||
verify(r1.get_state() == 'leader')
|
||||
verify(r2.get_state() == 'router')
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test Implementation
|
||||
|
||||
r1.cli('coaps psk RIGHT 1234')
|
||||
|
||||
# Set max connection attempts to 3
|
||||
r1.cli('coaps start 3')
|
||||
|
||||
r1_mleid = r1.get_mleid_ip_addr()
|
||||
|
||||
r2.cli('coaps psk WRONG 1234')
|
||||
r2.cli('coaps start')
|
||||
|
||||
# First connection attempt
|
||||
r2.cli('coaps connect', r1_mleid)
|
||||
time.sleep(0.1)
|
||||
verify(r1.cli('coaps isconnactive')[-1] == 'no')
|
||||
verify(r1.cli('coaps isclosed')[-1] == 'no')
|
||||
|
||||
# Second connection attempt
|
||||
r2.cli('coaps connect', r1_mleid)
|
||||
time.sleep(0.1)
|
||||
verify(r1.cli('coaps isconnactive')[-1] == 'no')
|
||||
verify(r1.cli('coaps isclosed')[-1] == 'no')
|
||||
|
||||
# Third connection attempt
|
||||
r2.cli('coaps connect', r1_mleid)
|
||||
time.sleep(0.1)
|
||||
verify(r1.cli('coaps isconnactive')[-1] == 'no')
|
||||
verify(r1.cli('coaps isclosed')[-1] == 'yes')
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test finished
|
||||
|
||||
cli.Node.finalize_all_nodes()
|
||||
|
||||
print('\'{}\' passed.'.format(test_name))
|
||||
@@ -45,6 +45,10 @@
|
||||
|
||||
#endif
|
||||
|
||||
#define OPENTHREAD_CONFIG_COAP_API_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE 0
|
||||
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1
|
||||
|
||||
@@ -190,6 +190,7 @@ if [ "$TORANJ_CLI" = 1 ]; then
|
||||
run cli/test-023-mesh-diag.py
|
||||
run cli/test-024-mle-adv-imax-change.py
|
||||
run cli/test-025-mesh-local-prefix-change.py
|
||||
run cli/test-026-coaps-conn-limit.py
|
||||
run cli/test-400-srp-client-server.py
|
||||
run cli/test-601-channel-manager-channel-change.py
|
||||
# Skip the "channel-select" test on a TREL only radio link, since it
|
||||
|
||||
Reference in New Issue
Block a user