[test] add test_dtls to nexus test cases (#10996)

This commit adds `test_dtls` using the nexus framework, which covers
basic functionality of `Dtls` (`SecureTransport). The test covers:

- Devices acting as DTLS server or client
- Establishing a DTLS session and data transfer
- `ConnectedHandler` events when the peer or local side disconnects
- Failed connection attempts (e.g., using an incorrect PSK)
- Setting max allowed attempts and auto-close behavior
This commit is contained in:
Abtin Keshavarzian
2024-12-05 18:47:48 -08:00
committed by GitHub
parent 49657d4bb7
commit 6bbb258023
3 changed files with 374 additions and 0 deletions
+1
View File
@@ -108,5 +108,6 @@ endmacro()
#----------------------------------------------------------------------------------------------------------------------
ot_nexus_test(dtls)
ot_nexus_test(form_join)
ot_nexus_test(large_network)
+2
View File
@@ -72,6 +72,8 @@ public:
return *this;
}
uint32_t GetId(void) { return GetInstance().GetId(); }
static Node &From(otInstance *aInstance) { return static_cast<Node &>(*aInstance); }
Node *mNext;
+371
View File
@@ -0,0 +1,371 @@
/*
* 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.
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "platform/nexus_core.hpp"
#include "platform/nexus_node.hpp"
namespace ot {
namespace Nexus {
typedef MeshCoP::Dtls Dtls;
static constexpr uint16_t kMaxNodes = 3;
static constexpr uint16_t kUdpPort = 1234;
static constexpr uint16_t kMessageSize = 100;
static constexpr uint16_t kMaxAttempts = 3;
static const uint8_t kPsk[] = {0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16};
static Dtls::ConnectEvent sDtlsEvent[kMaxNodes];
static Array<uint8_t, kMessageSize> sDtlsLastReceive[kMaxNodes];
static bool sDtlsAutoClosed[kMaxNodes];
const char *ConnectEventToString(Dtls::ConnectEvent aEvent)
{
const char *str = "";
switch (aEvent)
{
case Dtls::kConnected:
str = "kConnected";
break;
case Dtls::kDisconnectedPeerClosed:
str = "kDisconnectedPeerClosed";
break;
case Dtls::kDisconnectedLocalClosed:
str = "kDisconnectedLocalClosed";
break;
case Dtls::kDisconnectedMaxAttempts:
str = "kDisconnectedMaxAttempts";
break;
case Dtls::kDisconnectedError:
str = "kDisconnectedError";
break;
}
return str;
}
void HandleReceive(void *aContext, uint8_t *aBuf, uint16_t aLength)
{
Node *node = static_cast<Node *>(aContext);
VerifyOrQuit(node != nullptr);
VerifyOrQuit(node->GetId() < kMaxNodes);
Log(" node%u: HandleReceive(aLength:%u)", node->GetId(), aLength);
sDtlsLastReceive[node->GetId()].Clear();
for (; aLength > 0; aLength--, aBuf++)
{
SuccessOrQuit(sDtlsLastReceive[node->GetId()].PushBack(*aBuf));
}
}
void HandleConnectEvent(Dtls::ConnectEvent aEvent, void *aContext)
{
Node *node = static_cast<Node *>(aContext);
VerifyOrQuit(node != nullptr);
VerifyOrQuit(node->GetId() < kMaxNodes);
sDtlsEvent[node->GetId()] = aEvent;
Log(" node%u: HandleConnectEvent(%s)", node->GetId(), ConnectEventToString(aEvent));
}
void HandleAutoClose(void *aContext)
{
Node *node = static_cast<Node *>(aContext);
VerifyOrQuit(node != nullptr);
VerifyOrQuit(node->GetId() < kMaxNodes);
sDtlsAutoClosed[node->GetId()] = true;
Log(" node%u: HandleAutoClose()", node->GetId());
}
OwnedPtr<Message> PrepareMessage(Node &aNode)
{
Message *message = aNode.Get<MessagePool>().Allocate(Message::kTypeOther);
uint16_t length;
VerifyOrQuit(message != nullptr);
length = Random::NonCrypto::GetUint16InRange(1, kMessageSize);
for (uint16_t i = 0; i < length; i++)
{
SuccessOrQuit(message->Append(Random::NonCrypto::GetUint8()));
}
return OwnedPtr<Message>(message);
}
void TestDtls(void)
{
Core nexus;
Node &node0 = nexus.CreateNode();
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
nexus.AdvanceTime(0);
// Form the topology: node0 leader, with node1 & node2 as its FTD children
node0.Form();
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
SuccessOrQuit(node1.Get<Mle::MleRouter>().SetRouterEligible(false));
node1.Join(node0);
nexus.AdvanceTime(20 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Mle::Mle>().IsChild());
SuccessOrQuit(node2.Get<Mle::MleRouter>().SetRouterEligible(false));
node2.Join(node0);
nexus.AdvanceTime(20 * Time::kOneSecondInMsec);
VerifyOrQuit(node2.Get<Mle::Mle>().IsChild());
Log("------------------------------------------------------------------------------------------------------");
{
Dtls dtls0(node0.GetInstance(), kWithLinkSecurity);
Dtls dtls1(node1.GetInstance(), kWithLinkSecurity);
Dtls dtsl2(node2.GetInstance(), kWithLinkSecurity);
Ip6::SockAddr sockAddr;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Start DTLS (server) on node0 bound to port %u", kUdpPort);
SuccessOrQuit(dtls0.SetPsk(kPsk, sizeof(kPsk)));
SuccessOrQuit(dtls0.Open(HandleReceive, HandleConnectEvent, &node0));
SuccessOrQuit(dtls0.Bind(kUdpPort));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls0.GetUdpPort() == kUdpPort);
VerifyOrQuit(!dtls0.IsConnectionActive());
sockAddr.SetAddress(node0.Get<Mle::Mle>().GetMeshLocalRloc());
sockAddr.SetPort(kUdpPort);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Try to establish a DTLS connection from node 1 using a wrong PSK multiple times");
SuccessOrQuit(dtls1.SetPsk(kPsk, sizeof(kPsk) - 1));
SuccessOrQuit(dtls1.Open(HandleReceive, HandleConnectEvent, &node1));
for (uint16_t iter = 0; iter <= kMaxAttempts + 1; iter++)
{
memset(sDtlsEvent, Dtls::kConnected, sizeof(sDtlsEvent));
SuccessOrQuit(dtls1.Connect(sockAddr));
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!dtls0.IsConnected());
VerifyOrQuit(!dtls1.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::kDisconnectedError);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::kDisconnectedError);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Establish a DTLS connection from node1 with node0 using the correct PSK");
dtls1.Close();
SuccessOrQuit(dtls1.SetPsk(kPsk, sizeof(kPsk)));
SuccessOrQuit(dtls1.Open(HandleReceive, HandleConnectEvent, &node1));
SuccessOrQuit(dtls1.Connect(sockAddr));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls0.IsConnected());
VerifyOrQuit(dtls1.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::kConnected);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::kConnected);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Send message (random data and length) over DTLS session from node0 to node1");
for (uint16_t iter = 0; iter < 20; iter++)
{
OwnedPtr<Message> msg(PrepareMessage(node0));
SuccessOrQuit(dtls0.Send(*msg->Clone(), msg->GetLength()));
nexus.AdvanceTime(100);
VerifyOrQuit(sDtlsLastReceive[node1.GetId()].GetLength() == msg->GetLength());
VerifyOrQuit(msg->CompareBytes(0, sDtlsLastReceive[node1.GetId()].GetArrayBuffer(), msg->GetLength()));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Now send from node1 to node0");
for (uint16_t iter = 0; iter < 20; iter++)
{
OwnedPtr<Message> msg(PrepareMessage(node1));
SuccessOrQuit(dtls1.Send(*msg->Clone(), msg->GetLength()));
nexus.AdvanceTime(100);
VerifyOrQuit(sDtlsLastReceive[node0.GetId()].GetLength() == msg->GetLength());
VerifyOrQuit(msg->CompareBytes(0, sDtlsLastReceive[node0.GetId()].GetArrayBuffer(), msg->GetLength()));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disconnect from node1 - validate the disconnect events (local/peer)");
dtls1.Disconnect();
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!dtls0.IsConnected());
VerifyOrQuit(!dtls1.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::kDisconnectedPeerClosed);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::kDisconnectedLocalClosed);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Establish a DTLS connection again");
SuccessOrQuit(dtls1.Connect(sockAddr));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls0.IsConnected());
VerifyOrQuit(dtls1.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::kConnected);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::kConnected);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Try to connect from node2 - validate that it fails to connect since already connected");
SuccessOrQuit(dtsl2.SetPsk(kPsk, sizeof(kPsk)));
SuccessOrQuit(dtsl2.Open(HandleReceive, HandleConnectEvent, &node2));
SuccessOrQuit(dtsl2.Connect(sockAddr));
nexus.AdvanceTime(20 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls0.IsConnected());
VerifyOrQuit(dtls1.IsConnected());
VerifyOrQuit(!dtsl2.IsConnected());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disconnect from node0 - validate the disconnect events");
dtls0.Disconnect();
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!dtls0.IsConnected());
VerifyOrQuit(!dtls1.IsConnected());
VerifyOrQuit(!dtsl2.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::kDisconnectedLocalClosed);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::kDisconnectedPeerClosed);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
dtls0.Close();
dtls1.Close();
dtsl2.Close();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Start DTLS (server) on node0 bound to port %u with auto-close max attempt %u", kUdpPort, kMaxAttempts);
memset(sDtlsAutoClosed, false, sizeof(sDtlsAutoClosed));
SuccessOrQuit(dtls0.SetMaxConnectionAttempts(kMaxAttempts, HandleAutoClose, &node0));
SuccessOrQuit(dtls0.SetPsk(kPsk, sizeof(kPsk)));
SuccessOrQuit(dtls0.Open(HandleReceive, HandleConnectEvent, &node0));
SuccessOrQuit(dtls0.Bind(kUdpPort));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls0.GetUdpPort() == kUdpPort);
VerifyOrQuit(!dtls0.IsConnectionActive());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Using wrong PSK try to establish DTLS connection with node0 %u times", kMaxAttempts - 1);
SuccessOrQuit(dtls1.SetPsk(kPsk, sizeof(kPsk) - 1));
SuccessOrQuit(dtls1.Open(HandleReceive, HandleConnectEvent, &node1));
for (uint16_t iter = 0; iter < kMaxAttempts - 1; iter++)
{
memset(sDtlsEvent, Dtls::kConnected, sizeof(sDtlsEvent));
SuccessOrQuit(dtls1.Connect(sockAddr));
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!dtls0.IsConnected());
VerifyOrQuit(!dtls1.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::kDisconnectedError);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::kDisconnectedError);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Using wrong PSK try one last time, validate the auto-close behavior");
memset(sDtlsEvent, Dtls::kConnected, sizeof(sDtlsEvent));
SuccessOrQuit(dtls1.Connect(sockAddr));
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::kDisconnectedMaxAttempts);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::kDisconnectedError);
VerifyOrQuit(sDtlsAutoClosed[node0.GetId()]);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
dtls0.Close();
dtls1.Close();
dtsl2.Close();
}
}
} // namespace Nexus
} // namespace ot
int main(void)
{
ot::Nexus::TestDtls();
printf("All tests passed\n");
return 0;
}