mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
66c7272eef
This commit introduces the Border Agent Admitter feature , which enhances Thread MeshCoP. A Border Admitter is an enhanced Border Agent that functions as a traditional BA while enabling new behaviors. It acts as a proxy and dispatcher, allowing multiple external Enrollers to connect to it. It then petitions to become the single Active Commissioner on the Thread mesh, forwards new joiner requests to connected Enrollers, and manages the session between joiners and multiple Enrollers The implementation includes three main components: - `Admitter`: The main class that orchestrates the feature, managing enroller sessions and aggregating steering data. - `Arbitrator`: A distributed election mechanism that runs among Border Admitters on the mesh to select a single "Prime Admitter" by publishing a new Border Admitter service in the Thread Network Data. - `CommissionerPetitioner`: A sub-component responsible for petitioning the Leader to be granted the commissioner role. It handles conflicts if another commissioner is already active. New public APIs are added in `openthread/border_agent_admitter.h`. This commit also introduces a suite of comprehensive tests for the Border Admitter functionality under the `nexus` test framework. It covers various scenarios including: - Prime Admitter election and role management - Enroller registration, keep-alive, and timeout interactions - Handling of commissioner conflicts and petitioner retry mechanisms - Support for multiple enroller sessions and combined steering data - Forwarding of Joiner `RelayRx` and `UdpProxy` messages - Joiner acceptance, release, and expiration tracking
2150 lines
93 KiB
C++
2150 lines
93 KiB
C++
/*
|
|
* Copyright (c) 2025, 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 {
|
|
|
|
using ActiveDatasetManager = MeshCoP::ActiveDatasetManager;
|
|
using Manager = MeshCoP::BorderAgent::Manager;
|
|
using BaTxtData = MeshCoP::BorderAgent::TxtData;
|
|
using EphemeralKeyManager = MeshCoP::BorderAgent::EphemeralKeyManager;
|
|
using Admitter = MeshCoP::BorderAgent::Admitter;
|
|
using EpskcEvent = HistoryTracker::EpskcEvent;
|
|
using Iterator = HistoryTracker::Iterator;
|
|
using NetworkIdentity = MeshCoP::NetworkIdentity;
|
|
using NameData = MeshCoP::NameData;
|
|
using TxtEntry = Dns::TxtEntry;
|
|
|
|
void TestBorderAgent(void)
|
|
{
|
|
Core nexus;
|
|
Node &node0 = nexus.CreateNode();
|
|
Node &node1 = nexus.CreateNode();
|
|
Node &node2 = nexus.CreateNode();
|
|
Node &node3 = nexus.CreateNode();
|
|
Ip6::SockAddr sockAddr;
|
|
Pskc pskc;
|
|
Coap::Message *message;
|
|
Manager::SessionIterator iter;
|
|
Manager::SessionInfo sessionInfo;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAgent");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// Form the topology:
|
|
// - node0 leader acting as Border Agent,
|
|
// - node1-3 stay disconnected (acting as candidate)
|
|
|
|
node0.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
|
|
|
|
SuccessOrQuit(node1.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
|
|
node1.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
|
|
node1.Get<ThreadNetif>().Up();
|
|
|
|
SuccessOrQuit(node2.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
|
|
node2.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
|
|
node2.Get<ThreadNetif>().Up();
|
|
|
|
SuccessOrQuit(node3.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
|
|
node3.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
|
|
node3.Get<ThreadNetif>().Up();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check Border Agent initial state");
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().IsEnabled());
|
|
VerifyOrQuit(node0.Get<Manager>().IsRunning());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check disabling and re-enabling of Border Agent");
|
|
|
|
node0.Get<Manager>().SetEnabled(false);
|
|
VerifyOrQuit(!node0.Get<Manager>().IsEnabled());
|
|
VerifyOrQuit(!node0.Get<Manager>().IsRunning());
|
|
|
|
node0.Get<Manager>().SetEnabled(true);
|
|
VerifyOrQuit(node0.Get<Manager>().IsEnabled());
|
|
VerifyOrQuit(node0.Get<Manager>().IsRunning());
|
|
|
|
SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(node0.Get<Manager>().GetUdpPort()));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a DTLS connection to Border Agent");
|
|
|
|
sockAddr.SetAddress(node0.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
sockAddr.SetPort(node0.Get<Manager>().GetUdpPort());
|
|
|
|
node0.Get<KeyManager>().GetPskc(pskc);
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcSecureSessionSuccesses == 1);
|
|
|
|
iter.Init(node0.GetInstance());
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disconnect from candidate side");
|
|
|
|
node1.Get<Tmf::SecureAgent>().Close();
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<Manager>().IsRunning());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a secure connection again");
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcSecureSessionSuccesses == 2);
|
|
|
|
iter.Init(node0.GetInstance());
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send `Commissioner Petition` TMF command to become full commissioner");
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcSecureSessionSuccesses == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcCommissionerPetitions == 1);
|
|
|
|
iter.Init(node0.GetInstance());
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send `Commissioner Keep Alive` and check timeout behavior");
|
|
|
|
nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
Log(" Wait for 49 seconds (TIMEOUT_LEAD_PET is 50 seconds) and check BA state");
|
|
|
|
nexus.AdvanceTime(49 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
Log(" Wait for additional 5 seconds (ensuring TIMEOUT_LEAD_PET and session disconnect guard time expires)");
|
|
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().IsRunning());
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a secure session again and petition to become commissioner");
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcSecureSessionSuccesses == 3);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcCommissionerPetitions == 2);
|
|
|
|
iter.Init(node0.GetInstance());
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish two more secure sessions while the first session is still active");
|
|
|
|
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
SuccessOrQuit(node3.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
SuccessOrQuit(node3.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node3.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node3.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcSecureSessionSuccesses == 5);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcCommissionerPetitions == 2);
|
|
|
|
iter.Init(node0.GetInstance());
|
|
|
|
for (uint8_t numSessions = 0; numSessions < 3; numSessions++)
|
|
{
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
|
|
if (node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)))
|
|
{
|
|
VerifyOrQuit(sessionInfo.mIsCommissioner);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)) ||
|
|
node3.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disconnect from candidate side on node 1");
|
|
|
|
node1.Get<Tmf::SecureAgent>().Close();
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<Manager>().IsRunning());
|
|
|
|
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node3.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
|
|
for (uint8_t numSessions = 0; numSessions < 2; numSessions++)
|
|
{
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)) ||
|
|
node3.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Connect again from node 1 after 25 seconds");
|
|
|
|
nexus.AdvanceTime(25 * Time::kOneSecondInMsec);
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node3.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
|
|
for (uint8_t numSessions = 0; numSessions < 3; numSessions++)
|
|
{
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)) ||
|
|
node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)) ||
|
|
node3.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Wait for the first two sessions to timeout, check that only the recent node1 session is connected");
|
|
|
|
nexus.AdvanceTime(28 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(!node2.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(!node3.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Wait for the node1 session to also timeout, validate that its disconnected");
|
|
|
|
nexus.AdvanceTime(25 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(!node2.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(!node3.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
static bool sEphemeralKeyCallbackCalled = false;
|
|
|
|
void HandleEphemeralKeyChange(void *aContext)
|
|
{
|
|
Node *node;
|
|
|
|
VerifyOrQuit(aContext != nullptr);
|
|
node = reinterpret_cast<Node *>(aContext);
|
|
|
|
Log(" EphemeralKeyCallback() state:%s",
|
|
EphemeralKeyManager::StateToString(node->Get<EphemeralKeyManager>().GetState()));
|
|
sEphemeralKeyCallbackCalled = true;
|
|
}
|
|
|
|
void TestBorderAgentEphemeralKey(void)
|
|
{
|
|
static const char kEphemeralKey[] = "nexus1234";
|
|
static const char kTooShortEphemeralKey[] = "abcde";
|
|
static const char kTooLongEphemeralKey[] = "012345678901234567890123456789012";
|
|
|
|
static constexpr uint16_t kEphemeralKeySize = sizeof(kEphemeralKey) - 1;
|
|
static constexpr uint16_t kUdpPort = 49155;
|
|
|
|
Core nexus;
|
|
Node &node0 = nexus.CreateNode();
|
|
Node &node1 = nexus.CreateNode();
|
|
Node &node2 = nexus.CreateNode();
|
|
Ip6::SockAddr sockAddr;
|
|
Ip6::SockAddr baSockAddr;
|
|
Pskc pskc;
|
|
Manager::SessionIterator iter;
|
|
Manager::SessionInfo sessionInfo;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAgentEphemeralKey");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// Form the topology:
|
|
// - node0 leader acting as Border Agent,
|
|
// - node1 and node2 staying disconnected (acting as candidate)
|
|
|
|
node0.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
|
|
|
|
SuccessOrQuit(node1.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
|
|
node1.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
|
|
node1.Get<ThreadNetif>().Up();
|
|
|
|
SuccessOrQuit(node2.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
|
|
node2.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
|
|
node2.Get<ThreadNetif>().Up();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check Border Agent ephemeral key counter's initial value");
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 0);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 0);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationTimeouts == 0);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationDisconnects == 0);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationMaxAttempts == 0);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationClears == 0);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcInvalidArgsErrors == 0);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcInvalidBaStateErrors == 0);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check Border Agent ephemeral key feature enabled");
|
|
|
|
node0.Get<EphemeralKeyManager>().SetEnabled(false);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateDisabled);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort) ==
|
|
kErrorInvalidState);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcInvalidBaStateErrors == 1);
|
|
|
|
node0.Get<EphemeralKeyManager>().SetEnabled(true);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check Border Agent ephemeral key initial state");
|
|
|
|
sEphemeralKeyCallbackCalled = false;
|
|
VerifyOrQuit(!sEphemeralKeyCallbackCalled);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Set and start ephemeral key on Border Agent");
|
|
|
|
node0.Get<EphemeralKeyManager>().SetCallback(HandleEphemeralKeyChange, &node0);
|
|
|
|
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 1);
|
|
|
|
SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(kUdpPort));
|
|
|
|
nexus.AdvanceTime(0);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a secure connection with BA using the ephemeral key");
|
|
|
|
sEphemeralKeyCallbackCalled = false;
|
|
sockAddr.SetAddress(node0.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
sockAddr.SetPort(kUdpPort);
|
|
|
|
SuccessOrQuit(
|
|
node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize));
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 1);
|
|
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disconnect from candidate side, check that ephemeral key use is stopped");
|
|
|
|
sEphemeralKeyCallbackCalled = false;
|
|
|
|
node1.Get<Tmf::SecureAgent>().Close();
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationDisconnects == 1);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start using ephemeral key again with short timeout (20 seconds) and establish a connection");
|
|
|
|
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, 20 * Time::kOneSecondInMsec, kUdpPort));
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationDisconnects == 1);
|
|
|
|
Log(" Check the ephemeral key timeout behavior");
|
|
|
|
sEphemeralKeyCallbackCalled = false;
|
|
nexus.AdvanceTime(25 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationTimeouts == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationDisconnects == 1);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start using ephemeral key without any candidate connecting, check timeout");
|
|
|
|
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
|
|
|
|
Log(" Wait more than 120 seconds (default ephemeral key timeout)");
|
|
sEphemeralKeyCallbackCalled = false;
|
|
nexus.AdvanceTime(122 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 3);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationTimeouts == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationDisconnects == 1);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check that ephemeral key use is stopped after max failed connection attempts");
|
|
|
|
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
|
|
|
|
SuccessOrQuit(
|
|
node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize - 2));
|
|
|
|
for (uint8_t numAttempts = 1; numAttempts < 10; numAttempts++)
|
|
{
|
|
Log(" Attempt %u to connect with the wrong key", numAttempts);
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
}
|
|
|
|
Log(" Attempt 10 (final attempt) to connect with the wrong key, check that ephemeral key use is stopped");
|
|
|
|
sEphemeralKeyCallbackCalled = false;
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 4);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationTimeouts == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationDisconnects == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationMaxAttempts == 1);
|
|
|
|
node1.Get<Tmf::SecureAgent>().Close();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that disabling ephemeral key will stop and disconnect an active session");
|
|
|
|
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
|
|
|
|
SuccessOrQuit(
|
|
node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize));
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
sEphemeralKeyCallbackCalled = false;
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
|
|
node0.Get<EphemeralKeyManager>().SetEnabled(false);
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateDisabled);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 5);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 3);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationTimeouts == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationDisconnects == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationMaxAttempts == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationClears == 1);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check ephemeral key can be used along with PSKc");
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().IsRunning());
|
|
SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(node0.Get<Manager>().GetUdpPort()));
|
|
|
|
node0.Get<EphemeralKeyManager>().SetEnabled(true);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
|
|
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
|
|
|
|
Log(" Establish a secure session using PSKc (from node2)");
|
|
|
|
baSockAddr.SetAddress(node0.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
baSockAddr.SetPort(node0.Get<Manager>().GetUdpPort());
|
|
|
|
node0.Get<KeyManager>().GetPskc(pskc);
|
|
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().Connect(baSockAddr));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
Log(" Establish a secure session using ephemeral key (from node1)");
|
|
|
|
node1.Get<Tmf::SecureAgent>().Close();
|
|
SuccessOrQuit(
|
|
node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize));
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
Log(" Stop the secure session using ephemeral key");
|
|
|
|
node0.Get<EphemeralKeyManager>().Stop();
|
|
|
|
sEphemeralKeyCallbackCalled = false;
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
VerifyOrQuit(sEphemeralKeyCallbackCalled);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 6);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcSecureSessionSuccesses == 4);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationTimeouts == 2);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationDisconnects == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationMaxAttempts == 1);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcDeactivationClears == 2);
|
|
|
|
Log(" Check the BA session using PSKc is still connected");
|
|
|
|
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
iter.Init(node0.GetInstance());
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
|
|
iter.Init(node0.GetInstance());
|
|
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
|
VerifyOrQuit(sessionInfo.mIsConnected);
|
|
VerifyOrQuit(!sessionInfo.mIsCommissioner);
|
|
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
|
|
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check proper error is returned with invalid ephemeral key (too short or long)");
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().Start(kTooShortEphemeralKey, /* aTimeout */ 0, kUdpPort) ==
|
|
kErrorInvalidArgs);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 6);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcInvalidArgsErrors == 1);
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().Start(kTooLongEphemeralKey, /* aTimeout */ 0, kUdpPort) ==
|
|
kErrorInvalidArgs);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcActivations == 6);
|
|
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcInvalidArgsErrors == 2);
|
|
}
|
|
|
|
void TestBorderAgentEphemeralKeyTapGeneration(void)
|
|
{
|
|
static constexpr uint16_t kMaxRounds = 20;
|
|
|
|
using Tap = EphemeralKeyManager::Tap;
|
|
|
|
Core nexus;
|
|
Node &node = nexus.CreateNode();
|
|
Tap tap;
|
|
uint8_t index;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAgentEphemeralKeyTapGeneration");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
node.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node.Get<Mle::Mle>().IsLeader());
|
|
|
|
for (uint16_t round = 0; round < kMaxRounds; round++)
|
|
{
|
|
SuccessOrQuit(tap.GenerateRandom());
|
|
SuccessOrQuit(tap.Validate());
|
|
Log("Generated random TAP: %s", tap.mTap);
|
|
|
|
// Tamper with one of the digits and ensure checksum fails.
|
|
|
|
index = round % Tap::kLength;
|
|
|
|
tap.mTap[index]++;
|
|
|
|
if (tap.mTap[index] > '9')
|
|
{
|
|
tap.mTap[index] = '0';
|
|
}
|
|
|
|
VerifyOrQuit(tap.Validate() == kErrorFailed);
|
|
}
|
|
|
|
// CHeck valid TAP strings (Thread spec (1.4.1d3) - section 8.4.9.7)
|
|
|
|
SuccessOrQuit(StringCopy(tap.mTap, "903723159"));
|
|
SuccessOrQuit(tap.Validate());
|
|
|
|
SuccessOrQuit(StringCopy(tap.mTap, "746351983"));
|
|
SuccessOrQuit(tap.Validate());
|
|
|
|
// Check invalid TAP strings.
|
|
|
|
SuccessOrQuit(StringCopy(tap.mTap, ""));
|
|
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
|
|
|
SuccessOrQuit(StringCopy(tap.mTap, "1234"));
|
|
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
|
|
|
SuccessOrQuit(StringCopy(tap.mTap, "12345678"));
|
|
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
|
|
|
SuccessOrQuit(StringCopy(tap.mTap, "123456789"));
|
|
VerifyOrQuit(tap.Validate() == kErrorFailed);
|
|
|
|
SuccessOrQuit(StringCopy(tap.mTap, "a23456789"));
|
|
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
|
|
|
SuccessOrQuit(StringCopy(tap.mTap, "12345678A"));
|
|
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
|
}
|
|
|
|
EpskcEvent GetNewestEpskcEvent(Node &aNode)
|
|
{
|
|
const EpskcEvent *epskcEvent = nullptr;
|
|
Iterator iter;
|
|
uint32_t age;
|
|
iter.Init();
|
|
|
|
epskcEvent = aNode.Get<HistoryTracker::Local>().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<Mle::Mle>().IsLeader());
|
|
sockAddr.SetAddress(node0.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
sockAddr.SetPort(kUdpPort);
|
|
|
|
SuccessOrQuit(node1.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
|
|
node1.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
|
|
node1.Get<ThreadNetif>().Up();
|
|
|
|
// Enable the Ephemeral Key feature.
|
|
node0.Get<EphemeralKeyManager>().SetEnabled(true);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log(" Check Ephemeral Key Journey 1: Epskc Mode is activated, then deactivated");
|
|
|
|
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED);
|
|
|
|
node0.Get<EphemeralKeyManager>().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<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(kUdpPort));
|
|
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED);
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
|
|
SuccessOrQuit(
|
|
node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize - 2));
|
|
|
|
for (uint8_t numAttempts = 1; numAttempts < 10; numAttempts++)
|
|
{
|
|
Log(" Attempt %u to connect with the wrong key", numAttempts);
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
}
|
|
|
|
Log(" Attempt 10 (final attempt) to connect with the wrong key, check that ephemeral key use is stopped");
|
|
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().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<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED);
|
|
|
|
SuccessOrQuit(
|
|
node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED);
|
|
|
|
node0.Get<EphemeralKeyManager>().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<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED);
|
|
|
|
node1.Get<Tmf::SecureAgent>().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<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriActiveGet);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_ACTIVE_DATASET);
|
|
|
|
node1.Get<Tmf::SecureAgent>().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<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriActiveGet);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().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<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriPendingGet);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_PENDING_DATASET);
|
|
|
|
node1.Get<Tmf::SecureAgent>().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<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriActiveGet);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().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<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriPendingGet);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().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<Tmf::SecureAgent>().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<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED);
|
|
|
|
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED);
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().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<Tmf::SecureAgent>().IsConnected())
|
|
{
|
|
break;
|
|
}
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
|
|
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_KEEP_ALIVE);
|
|
}
|
|
|
|
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
|
|
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
epskcEvent = GetNewestEpskcEvent(node0);
|
|
VerifyOrQuit(epskcEvent == OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_EPSKC_TIMEOUT);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
struct TxtData
|
|
{
|
|
void Init(const uint8_t *aData, uint16_t aLength) { mData = aData, mLength = aLength; }
|
|
|
|
void ValidateFormat(void)
|
|
{
|
|
TxtEntry::Iterator iter;
|
|
TxtEntry txtEntry;
|
|
|
|
iter.Init(mData, mLength);
|
|
|
|
while (true)
|
|
{
|
|
Error error = iter.GetNextEntry(txtEntry);
|
|
|
|
if (error == kErrorNotFound)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SuccessOrQuit(error);
|
|
VerifyOrQuit(txtEntry.mKey != nullptr);
|
|
}
|
|
}
|
|
|
|
void LogAllTxtEntries(void)
|
|
{
|
|
static constexpr uint16_t kValueStringSize = 256;
|
|
|
|
char valueString[kValueStringSize];
|
|
TxtEntry::Iterator iter;
|
|
TxtEntry txtEntry;
|
|
|
|
Log("TXT data - length %u", mLength);
|
|
|
|
iter.Init(mData, mLength);
|
|
|
|
while (true)
|
|
{
|
|
Error error = iter.GetNextEntry(txtEntry);
|
|
StringWriter writer(valueString, sizeof(valueString));
|
|
|
|
if (error == kErrorNotFound)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SuccessOrQuit(error);
|
|
VerifyOrQuit(txtEntry.mKey != nullptr);
|
|
|
|
writer.AppendHexBytes(txtEntry.mValue, txtEntry.mValueLength);
|
|
Log(" %s -> [%s] (len:%u)", txtEntry.mKey, valueString, txtEntry.mValueLength);
|
|
}
|
|
}
|
|
|
|
bool ContainsKey(const char *aKey) const
|
|
{
|
|
TxtEntry txtEntry;
|
|
|
|
return FindTxtEntry(aKey, txtEntry);
|
|
}
|
|
|
|
void ValidateKey(const char *aKey, const void *aValue, uint16_t aValueLength) const
|
|
{
|
|
TxtEntry txtEntry;
|
|
|
|
VerifyOrQuit(FindTxtEntry(aKey, txtEntry));
|
|
VerifyOrQuit(txtEntry.mValueLength == aValueLength);
|
|
VerifyOrQuit(memcmp(txtEntry.mValue, aValue, aValueLength) == 0);
|
|
}
|
|
|
|
template <typename ObjectType> void ValidateKey(const char *aKey, const ObjectType &aObject) const
|
|
{
|
|
static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
|
|
|
|
ValidateKey(aKey, &aObject, sizeof(aObject));
|
|
}
|
|
|
|
void ValidateKey(const char *aKey, const char *aString) const { ValidateKey(aKey, aString, strlen(aString)); }
|
|
|
|
uint32_t ReadUint32Key(const char *aKey) const
|
|
{
|
|
TxtEntry txtEntry;
|
|
|
|
VerifyOrQuit(FindTxtEntry(aKey, txtEntry));
|
|
VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t));
|
|
return BigEndian::ReadUint32(txtEntry.mValue);
|
|
}
|
|
|
|
bool FindTxtEntry(const char *aKey, TxtEntry &aTxtEntry) const
|
|
{
|
|
bool found = false;
|
|
TxtEntry::Iterator iter;
|
|
|
|
iter.Init(mData, mLength);
|
|
|
|
while (iter.GetNextEntry(aTxtEntry) == kErrorNone)
|
|
{
|
|
VerifyOrQuit(aTxtEntry.mKey != nullptr);
|
|
|
|
if (StringMatch(aTxtEntry.mKey, aKey))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
const uint8_t *mData;
|
|
uint16_t mLength;
|
|
};
|
|
|
|
void ValidateMeshCoPTxtData(TxtData &aTxtData, Node &aNode)
|
|
{
|
|
// State bitmap masks and field values
|
|
static constexpr uint32_t kMaskConnectionMode = 7 << 0;
|
|
static constexpr uint32_t kConnectionModeDisabled = 0 << 0;
|
|
static constexpr uint32_t kConnectionModePskc = 1 << 0;
|
|
static constexpr uint32_t kMaskThreadIfStatus = 3 << 3;
|
|
static constexpr uint32_t kThreadIfStatusNotInitialized = 0 << 3;
|
|
static constexpr uint32_t kThreadIfStatusInitialized = 1 << 3;
|
|
static constexpr uint32_t kThreadIfStatusActive = 2 << 3;
|
|
static constexpr uint32_t kMaskThreadRole = 3 << 9;
|
|
static constexpr uint32_t kThreadRoleDisabledOrDetached = 0 << 9;
|
|
static constexpr uint32_t kThreadRoleChild = 1 << 9;
|
|
static constexpr uint32_t kThreadRoleRouter = 2 << 9;
|
|
static constexpr uint32_t kThreadRoleLeader = 3 << 9;
|
|
static constexpr uint32_t kFlagEpskcSupported = 1 << 11;
|
|
static constexpr uint32_t kFlagAdmitterSupported = 1 << 14;
|
|
|
|
MeshCoP::BorderAgent::Id id;
|
|
BaTxtData::Info info;
|
|
const Mac::ExtAddress &extAddress = aNode.Get<Mac::Mac>().GetExtAddress();
|
|
uint32_t stateBitmap;
|
|
uint32_t threadIfStatus;
|
|
uint32_t threadRole;
|
|
bool baIsRunning;
|
|
|
|
aTxtData.ValidateFormat();
|
|
aTxtData.LogAllTxtEntries();
|
|
|
|
SuccessOrQuit(info.ParseFrom(aTxtData.mData, aTxtData.mLength));
|
|
|
|
aNode.Get<Manager>().GetId(id);
|
|
aTxtData.ValidateKey("id", id);
|
|
VerifyOrQuit(info.mHasAgentId);
|
|
VerifyOrQuit(id == AsCoreType(&info.mAgentId));
|
|
|
|
aTxtData.ValidateKey("rv", "1");
|
|
VerifyOrQuit(info.mHasRecordVersion);
|
|
VerifyOrQuit(StringMatch(info.mRecordVersion, "1"));
|
|
|
|
aTxtData.ValidateKey("tv", kThreadVersionString);
|
|
VerifyOrQuit(info.mHasThreadVersion);
|
|
VerifyOrQuit(StringMatch(info.mThreadVersion, kThreadVersionString));
|
|
|
|
aTxtData.ValidateKey("xa", extAddress);
|
|
VerifyOrQuit(info.mHasExtAddress);
|
|
VerifyOrQuit(AsCoreType(&info.mExtAddress) == extAddress);
|
|
|
|
if (aNode.Get<MeshCoP::ActiveDatasetManager>().IsComplete())
|
|
{
|
|
const char *networkName = aNode.Get<NetworkIdentity>().GetNetworkName().GetAsCString();
|
|
const MeshCoP::ExtendedPanId &extPanId = aNode.Get<NetworkIdentity>().GetExtPanId();
|
|
|
|
aTxtData.ValidateKey("nn", networkName);
|
|
VerifyOrQuit(info.mHasNetworkName);
|
|
VerifyOrQuit(StringMatch(info.mNetworkName.m8, networkName));
|
|
|
|
aTxtData.ValidateKey("xp", extPanId);
|
|
VerifyOrQuit(info.mHasExtendedPanId);
|
|
VerifyOrQuit(AsCoreType(&info.mExtendedPanId) == extPanId);
|
|
}
|
|
|
|
if (aNode.Get<Mle::Mle>().IsAttached())
|
|
{
|
|
uint32_t partitionId = aNode.Get<Mle::Mle>().GetLeaderData().GetPartitionId();
|
|
const MeshCoP::Timestamp ×tamp = aNode.Get<ActiveDatasetManager>().GetTimestamp();
|
|
MeshCoP::Timestamp::Info timestampInfo;
|
|
|
|
aTxtData.ValidateKey("pt", BigEndian::HostSwap32(partitionId));
|
|
VerifyOrQuit(info.mHasPartitionId);
|
|
VerifyOrQuit(info.mPartitionId == partitionId);
|
|
|
|
aTxtData.ValidateKey("at", timestamp);
|
|
timestamp.ConvertTo(timestampInfo);
|
|
VerifyOrQuit(info.mHasActiveTimestamp);
|
|
VerifyOrQuit(info.mActiveTimestamp.mSeconds == timestampInfo.mSeconds);
|
|
VerifyOrQuit(info.mActiveTimestamp.mTicks == timestampInfo.mTicks);
|
|
VerifyOrQuit(info.mActiveTimestamp.mAuthoritative == timestampInfo.mAuthoritative);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(!aTxtData.ContainsKey("pt"));
|
|
VerifyOrQuit(!info.mHasPartitionId);
|
|
|
|
VerifyOrQuit(!aTxtData.ContainsKey("at"));
|
|
VerifyOrQuit(!info.mHasActiveTimestamp);
|
|
}
|
|
|
|
stateBitmap = aTxtData.ReadUint32Key("sb");
|
|
VerifyOrQuit(info.mHasStateBitmap);
|
|
|
|
baIsRunning = aNode.Get<Manager>().IsRunning();
|
|
VerifyOrQuit((stateBitmap & kMaskConnectionMode) == (baIsRunning ? kConnectionModePskc : kConnectionModeDisabled));
|
|
VerifyOrQuit(info.mStateBitmap.mConnMode ==
|
|
(baIsRunning ? BaTxtData::kConnModePskc : BaTxtData::kConnModeDisabled));
|
|
|
|
switch (aNode.Get<Mle::Mle>().GetRole())
|
|
{
|
|
case Mle::DeviceRole::kRoleDisabled:
|
|
threadIfStatus = kThreadIfStatusNotInitialized;
|
|
threadRole = kThreadRoleDisabledOrDetached;
|
|
VerifyOrQuit(info.mStateBitmap.mThreadIfState == BaTxtData::kThreadIfNotInit);
|
|
VerifyOrQuit(info.mStateBitmap.mThreadRole == BaTxtData::kRoleDisabledDetached);
|
|
break;
|
|
case Mle::DeviceRole::kRoleDetached:
|
|
threadIfStatus = kThreadIfStatusInitialized;
|
|
threadRole = kThreadRoleDisabledOrDetached;
|
|
VerifyOrQuit(info.mStateBitmap.mThreadIfState == BaTxtData::kThreadIfInit);
|
|
VerifyOrQuit(info.mStateBitmap.mThreadRole == BaTxtData::kRoleDisabledDetached);
|
|
break;
|
|
case Mle::DeviceRole::kRoleChild:
|
|
threadIfStatus = kThreadIfStatusActive;
|
|
threadRole = kThreadRoleChild;
|
|
VerifyOrQuit(info.mStateBitmap.mThreadIfState == BaTxtData::kThreadIfActive);
|
|
VerifyOrQuit(info.mStateBitmap.mThreadRole == BaTxtData::kRoleChild);
|
|
break;
|
|
case Mle::DeviceRole::kRoleRouter:
|
|
threadIfStatus = kThreadIfStatusActive;
|
|
threadRole = kThreadRoleRouter;
|
|
VerifyOrQuit(info.mStateBitmap.mThreadIfState == BaTxtData::kThreadIfActive);
|
|
VerifyOrQuit(info.mStateBitmap.mThreadRole == BaTxtData::kRoleRouter);
|
|
break;
|
|
case Mle::DeviceRole::kRoleLeader:
|
|
threadIfStatus = kThreadIfStatusActive;
|
|
threadRole = kThreadRoleLeader;
|
|
VerifyOrQuit(info.mStateBitmap.mThreadIfState == BaTxtData::kThreadIfActive);
|
|
VerifyOrQuit(info.mStateBitmap.mThreadRole == BaTxtData::kRoleLeader);
|
|
break;
|
|
}
|
|
|
|
VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == threadIfStatus);
|
|
VerifyOrQuit((stateBitmap & kMaskThreadRole) == threadRole);
|
|
|
|
if (aNode.Get<EphemeralKeyManager>().GetState() != EphemeralKeyManager::kStateDisabled)
|
|
{
|
|
VerifyOrQuit(stateBitmap & kFlagEpskcSupported);
|
|
VerifyOrQuit(info.mStateBitmap.mEpskcSupported);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(!(stateBitmap & kFlagEpskcSupported));
|
|
VerifyOrQuit(!info.mStateBitmap.mEpskcSupported);
|
|
}
|
|
|
|
if (aNode.Get<Admitter>().IsEnabled())
|
|
{
|
|
VerifyOrQuit(stateBitmap & kFlagAdmitterSupported);
|
|
VerifyOrQuit(info.mStateBitmap.mAdmitterSupported);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(!(stateBitmap & kFlagAdmitterSupported));
|
|
VerifyOrQuit(!info.mStateBitmap.mAdmitterSupported);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
void HandleServiceChanged(void *aContext) // Callback used in `TestBorderAgentTxtDataCallback().`
|
|
{
|
|
// `aContext` is a boolean `callbackInvoked`
|
|
VerifyOrQuit(aContext != nullptr);
|
|
*static_cast<bool *>(aContext) = true;
|
|
}
|
|
|
|
void ReadAndValidateMeshCoPTxtData(Node &aNode)
|
|
{
|
|
BaTxtData::ServiceTxtData serviceTxtData;
|
|
TxtData txtData;
|
|
|
|
SuccessOrQuit(aNode.Get<BaTxtData>().Prepare(serviceTxtData));
|
|
txtData.Init(serviceTxtData.mData, serviceTxtData.mLength);
|
|
|
|
ValidateMeshCoPTxtData(txtData, aNode);
|
|
}
|
|
|
|
void TestBorderAgentTxtDataCallback(void)
|
|
{
|
|
Core nexus;
|
|
Node &node0 = nexus.CreateNode();
|
|
bool callbackInvoked = false;
|
|
MeshCoP::BorderAgent::Id newId;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAgentTxtDataCallback");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Set MeshCoP service change callback. Will get initial values.
|
|
Log("Set MeshCoP service change callback and check initial values");
|
|
node0.Get<BaTxtData>().SetChangedCallback(HandleServiceChanged, &callbackInvoked);
|
|
nexus.AdvanceTime(1);
|
|
|
|
// Check the initial TXT entries
|
|
ReadAndValidateMeshCoPTxtData(node0);
|
|
|
|
// Check the Border Agent state
|
|
VerifyOrQuit(!node0.Get<Manager>().IsRunning());
|
|
VerifyOrQuit(node0.Get<Manager>().GetUdpPort() == 0);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Join Thread network and check updated values and states.
|
|
callbackInvoked = false;
|
|
Log("Join Thread network and check updated Txt data and states");
|
|
node0.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(callbackInvoked);
|
|
ReadAndValidateMeshCoPTxtData(node0);
|
|
|
|
VerifyOrQuit(node0.Get<Manager>().IsRunning());
|
|
VerifyOrQuit(node0.Get<Manager>().GetUdpPort() != 0);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
Log("Change the Border Agent ID and validate that TXT data changed and callback is invoked");
|
|
|
|
newId.GenerateRandom();
|
|
|
|
callbackInvoked = false;
|
|
node0.Get<Manager>().SetId(newId);
|
|
|
|
nexus.AdvanceTime(1);
|
|
ReadAndValidateMeshCoPTxtData(node0);
|
|
|
|
// Validate that setting the ID to the same value as before is
|
|
// correctly detected and does not trigger the callback.
|
|
|
|
callbackInvoked = false;
|
|
node0.Get<Manager>().SetId(newId);
|
|
nexus.AdvanceTime(1);
|
|
VerifyOrQuit(!callbackInvoked);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disable EphemeralKeyManager and validate that TXT data state bitmap indicates this");
|
|
|
|
callbackInvoked = false;
|
|
node0.Get<EphemeralKeyManager>().SetEnabled(false);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateDisabled);
|
|
|
|
nexus.AdvanceTime(1);
|
|
VerifyOrQuit(callbackInvoked);
|
|
ReadAndValidateMeshCoPTxtData(node0);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disable the MLE operation and validate the TXT data state bitmap");
|
|
|
|
callbackInvoked = false;
|
|
SuccessOrQuit(node0.Get<Mle::Mle>().Disable());
|
|
|
|
nexus.AdvanceTime(1);
|
|
VerifyOrQuit(callbackInvoked);
|
|
ReadAndValidateMeshCoPTxtData(node0);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
static constexpr uint32_t kInfraIfIndex = 1;
|
|
static constexpr uint16_t kMaxEntries = 5;
|
|
static constexpr uint16_t kMaxTxtDataSize = 128;
|
|
|
|
typedef Dns::Name::Buffer DnsName;
|
|
|
|
struct BrowseOutcome
|
|
{
|
|
DnsName mServiceInstance;
|
|
uint32_t mTtl;
|
|
};
|
|
|
|
struct SrvOutcome
|
|
{
|
|
DnsName mHostName;
|
|
uint16_t mPort;
|
|
uint32_t mTtl;
|
|
};
|
|
|
|
struct TxtOutcome
|
|
{
|
|
uint8_t mTxtData[kMaxTxtDataSize];
|
|
uint16_t mTxtDataLength;
|
|
uint32_t mTtl;
|
|
};
|
|
|
|
static Array<BrowseOutcome, kMaxEntries> sBrowseOutcomes;
|
|
static Array<SrvOutcome, kMaxEntries> sSrvOutcomes;
|
|
static Array<TxtOutcome, kMaxEntries> sTxtOutcomes;
|
|
|
|
void HandleBrowseCallback(otInstance *aInstance, const Dns::Multicast::Core::BrowseResult *aResult)
|
|
{
|
|
BrowseOutcome *outcome;
|
|
|
|
VerifyOrQuit(aInstance != nullptr);
|
|
VerifyOrQuit(aResult->mInfraIfIndex == kInfraIfIndex);
|
|
|
|
outcome = sBrowseOutcomes.PushBack();
|
|
VerifyOrQuit(outcome != nullptr);
|
|
|
|
SuccessOrQuit(StringCopy(outcome->mServiceInstance, aResult->mServiceInstance));
|
|
outcome->mTtl = aResult->mTtl;
|
|
}
|
|
|
|
void HandleSrvCallback(otInstance *aInstance, const Dns::Multicast::Core::SrvResult *aResult)
|
|
{
|
|
SrvOutcome *outcome;
|
|
|
|
VerifyOrQuit(aInstance != nullptr);
|
|
VerifyOrQuit(aResult->mInfraIfIndex == kInfraIfIndex);
|
|
|
|
outcome = sSrvOutcomes.PushBack();
|
|
VerifyOrQuit(outcome != nullptr);
|
|
|
|
SuccessOrQuit(StringCopy(outcome->mHostName, aResult->mHostName));
|
|
outcome->mPort = aResult->mPort;
|
|
outcome->mTtl = aResult->mTtl;
|
|
}
|
|
|
|
void HandleTxtCallback(otInstance *aInstance, const Dns::Multicast::Core::TxtResult *aResult)
|
|
{
|
|
TxtOutcome *outcome;
|
|
|
|
VerifyOrQuit(aInstance != nullptr);
|
|
VerifyOrQuit(aResult->mInfraIfIndex == kInfraIfIndex);
|
|
|
|
outcome = sTxtOutcomes.PushBack();
|
|
VerifyOrQuit(outcome != nullptr);
|
|
|
|
VerifyOrQuit(aResult->mTxtDataLength < kMaxTxtDataSize);
|
|
memcpy(outcome->mTxtData, aResult->mTxtData, aResult->mTxtDataLength);
|
|
outcome->mTxtDataLength = aResult->mTxtDataLength;
|
|
outcome->mTtl = aResult->mTtl;
|
|
}
|
|
|
|
void ValidateRegisteredServiceData(Dns::Multicast::Core::Service &aService, Node &aNode)
|
|
{
|
|
TxtData txtData;
|
|
|
|
txtData.Init(aService.mTxtData, aService.mTxtDataLength);
|
|
ValidateMeshCoPTxtData(txtData, aNode);
|
|
}
|
|
|
|
void TestBorderAgentServiceRegistration(void)
|
|
{
|
|
static const char kDefaultServiceBaseName[] = OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME;
|
|
static const char kEphemeralKey[] = "nexus1234";
|
|
static const uint8_t kVendorTxtData[] = {8, 'v', 'n', '=', 'n', 'e', 'x', 'u', 's'};
|
|
|
|
static constexpr uint32_t kUdpPort = 49155;
|
|
|
|
Core nexus;
|
|
Node &node0 = nexus.CreateNode();
|
|
Node &node1 = nexus.CreateNode();
|
|
Dns::Multicast::Core::Iterator *iterator;
|
|
Dns::Multicast::Core::Service service;
|
|
Dns::Multicast::Core::EntryState entryState;
|
|
Dns::Multicast::Core::Browser browser;
|
|
Dns::Multicast::Core::SrvResolver srvResolver;
|
|
Dns::Multicast::Core::TxtResolver txtResolver;
|
|
TxtData txtData;
|
|
uint16_t txtDataLengthWithNoVendorData;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAgentServiceRegistration");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
node0.Form();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Disable Border Agent function on node1
|
|
node1.Get<MeshCoP::BorderAgent::Manager>().SetEnabled(false);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Enable mDNS
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().IsEnabled());
|
|
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
|
VerifyOrQuit(node1.Get<Dns::Multicast::Core>().IsEnabled());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
VerifyOrQuit(node0.Get<MeshCoP::BorderAgent::Manager>().IsEnabled());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the registered mDNS MeshCop service by Border Agent");
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, kDefaultServiceBaseName));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
ValidateRegisteredServiceData(service, node0);
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the service can be resolved on other nodes");
|
|
|
|
ClearAllBytes(browser);
|
|
browser.mServiceType = "_meshcop._udp";
|
|
browser.mInfraIfIndex = kInfraIfIndex;
|
|
browser.mCallback = HandleBrowseCallback;
|
|
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().StartBrowser(browser));
|
|
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(sBrowseOutcomes.GetLength() == 1);
|
|
VerifyOrQuit(StringStartsWith(sBrowseOutcomes[0].mServiceInstance, kDefaultServiceBaseName));
|
|
VerifyOrQuit(sBrowseOutcomes[0].mTtl > 0);
|
|
|
|
ClearAllBytes(srvResolver);
|
|
srvResolver.mServiceInstance = sBrowseOutcomes[0].mServiceInstance;
|
|
srvResolver.mServiceType = browser.mServiceType;
|
|
srvResolver.mInfraIfIndex = kInfraIfIndex;
|
|
srvResolver.mCallback = HandleSrvCallback;
|
|
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().StartSrvResolver(srvResolver));
|
|
|
|
ClearAllBytes(txtResolver);
|
|
txtResolver.mServiceInstance = sBrowseOutcomes[0].mServiceInstance;
|
|
txtResolver.mServiceType = browser.mServiceType;
|
|
txtResolver.mInfraIfIndex = kInfraIfIndex;
|
|
txtResolver.mCallback = HandleTxtCallback;
|
|
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().StartTxtResolver(txtResolver));
|
|
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(sSrvOutcomes.GetLength() == 1);
|
|
VerifyOrQuit(StringStartsWith(sSrvOutcomes[0].mHostName, "ot"));
|
|
VerifyOrQuit(sSrvOutcomes[0].mTtl > 0);
|
|
VerifyOrQuit(sSrvOutcomes[0].mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
|
|
VerifyOrQuit(sTxtOutcomes.GetLength() == 1);
|
|
VerifyOrQuit(sTxtOutcomes[0].mTtl > 0);
|
|
txtData.Init(sTxtOutcomes[0].mTxtData, sTxtOutcomes[0].mTxtDataLength);
|
|
ValidateMeshCoPTxtData(txtData, node0);
|
|
|
|
sBrowseOutcomes.Clear();
|
|
sSrvOutcomes.Clear();
|
|
sTxtOutcomes.Clear();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable and start ephemeral key");
|
|
|
|
node0.Get<EphemeralKeyManager>().SetEnabled(true);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
|
node0.Get<EphemeralKeyManager>().SetCallback(HandleEphemeralKeyChange, &node0);
|
|
|
|
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
|
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check the registered services");
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
for (uint8_t num = 2; num > 0; num--)
|
|
{
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
Log("- - - - - - - - - - - - - - - - -");
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, kDefaultServiceBaseName));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
|
|
if (StringMatch(service.mServiceType, "_meshcop._udp"))
|
|
{
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
ValidateRegisteredServiceData(service, node0);
|
|
}
|
|
else if (StringMatch(service.mServiceType, "_meshcop-e._udp"))
|
|
{
|
|
VerifyOrQuit(service.mPort == kUdpPort);
|
|
VerifyOrQuit(service.mTxtDataLength == 1);
|
|
VerifyOrQuit(service.mTxtData[0] == 0);
|
|
}
|
|
else
|
|
{
|
|
// Unexpected service type
|
|
VerifyOrQuit(false);
|
|
}
|
|
}
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
VerifyOrQuit(sBrowseOutcomes.GetLength() == 0);
|
|
VerifyOrQuit(sSrvOutcomes.GetLength() == 0);
|
|
VerifyOrQuit(sTxtOutcomes.GetLength() == 0);
|
|
|
|
Log("Wait for the ephemeral key to expire and validate the registered service is removed");
|
|
|
|
nexus.AdvanceTime(5 * Time::kOneMinuteInMsec);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, kDefaultServiceBaseName));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
ValidateRegisteredServiceData(service, node0);
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Change the base service name and validate the new service");
|
|
|
|
SuccessOrQuit(node0.Get<MeshCoP::BorderAgent::Manager>().SetServiceBaseName("OpenThreadAgent"));
|
|
|
|
nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
ValidateRegisteredServiceData(service, node0);
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
// Check browser/resolver outcome on node1
|
|
|
|
VerifyOrQuit(sSrvOutcomes.GetLength() == 1);
|
|
VerifyOrQuit(sSrvOutcomes[0].mTtl == 0);
|
|
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().StopSrvResolver(srvResolver));
|
|
|
|
VerifyOrQuit(sTxtOutcomes.GetLength() == 1);
|
|
VerifyOrQuit(sTxtOutcomes[0].mTtl == 0);
|
|
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().StopTxtResolver(txtResolver));
|
|
|
|
VerifyOrQuit(sBrowseOutcomes.GetLength() == 2);
|
|
VerifyOrQuit(sBrowseOutcomes[0].mTtl == 0);
|
|
VerifyOrQuit(StringStartsWith(sBrowseOutcomes[0].mServiceInstance, kDefaultServiceBaseName));
|
|
|
|
VerifyOrQuit(sBrowseOutcomes[1].mTtl > 0);
|
|
VerifyOrQuit(StringStartsWith(sBrowseOutcomes[1].mServiceInstance, "OpenThreadAgent"));
|
|
|
|
sBrowseOutcomes.Clear();
|
|
sSrvOutcomes.Clear();
|
|
sTxtOutcomes.Clear();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Admitter function");
|
|
|
|
node0.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(node0.Get<Admitter>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check the registered service");
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
ValidateRegisteredServiceData(service, node0);
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disable Admitter function");
|
|
|
|
node0.Get<Admitter>().SetEnabled(false);
|
|
VerifyOrQuit(!node0.Get<Admitter>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check the registered service");
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
ValidateRegisteredServiceData(service, node0);
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disable Border Agent and validate that registered service is removed");
|
|
|
|
node0.Get<MeshCoP::BorderAgent::Manager>().SetEnabled(false);
|
|
VerifyOrQuit(!node0.Get<MeshCoP::BorderAgent::Manager>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
VerifyOrQuit(sBrowseOutcomes.GetLength() == 1);
|
|
VerifyOrQuit(sBrowseOutcomes[0].mTtl == 0);
|
|
VerifyOrQuit(StringStartsWith(sBrowseOutcomes[0].mServiceInstance, "OpenThreadAgent"));
|
|
|
|
sBrowseOutcomes.Clear();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Change the base service name while agent is disabled and validate no service is registered");
|
|
|
|
SuccessOrQuit(node0.Get<MeshCoP::BorderAgent::Manager>().SetServiceBaseName("NewName"));
|
|
|
|
VerifyOrQuit(!node0.Get<MeshCoP::BorderAgent::Manager>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
VerifyOrQuit(sBrowseOutcomes.IsEmpty());
|
|
|
|
SuccessOrQuit(node0.Get<MeshCoP::BorderAgent::Manager>().SetServiceBaseName("OpenThreadAgent"));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Re-enable Border Agent and validate that service is registered again");
|
|
|
|
node0.Get<MeshCoP::BorderAgent::Manager>().SetEnabled(true);
|
|
VerifyOrQuit(node0.Get<MeshCoP::BorderAgent::Manager>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
ValidateRegisteredServiceData(service, node0);
|
|
txtDataLengthWithNoVendorData = service.mTxtDataLength;
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
VerifyOrQuit(sBrowseOutcomes.GetLength() == 1);
|
|
VerifyOrQuit(sBrowseOutcomes[0].mTtl > 0);
|
|
VerifyOrQuit(StringStartsWith(sBrowseOutcomes[0].mServiceInstance, "OpenThreadAgent"));
|
|
|
|
sBrowseOutcomes.Clear();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Set vendor TXT data and validate that it is included in the registered mDNS service");
|
|
|
|
node0.Get<BaTxtData>().SetVendorData(kVendorTxtData, sizeof(kVendorTxtData));
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
ValidateRegisteredServiceData(service, node0);
|
|
|
|
// Check that vendor TXT data is included at the end of
|
|
// the registered service TXT data.
|
|
VerifyOrQuit(service.mTxtDataLength > txtDataLengthWithNoVendorData);
|
|
VerifyOrQuit(service.mTxtDataLength > sizeof(kVendorTxtData));
|
|
VerifyOrQuit(!memcmp(&service.mTxtData[service.mTxtDataLength - sizeof(kVendorTxtData)], kVendorTxtData,
|
|
sizeof(kVendorTxtData)));
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Clear vendor TXT data and validate that the registered mDNS service is updated accordingly");
|
|
|
|
node0.Get<BaTxtData>().SetVendorData(nullptr, 0);
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
|
|
Log(" HostName: %s", service.mHostName);
|
|
Log(" ServiceInstance: %s", service.mServiceInstance);
|
|
Log(" ServiceType: %s", service.mServiceType);
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
ValidateRegisteredServiceData(service, node0);
|
|
VerifyOrQuit(service.mTxtDataLength == txtDataLengthWithNoVendorData);
|
|
|
|
// Check that there is no more registered mDNS service
|
|
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
|
|
|
|
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
}
|
|
|
|
} // namespace Nexus
|
|
} // namespace ot
|
|
|
|
int main(void)
|
|
{
|
|
ot::Nexus::TestBorderAgent();
|
|
ot::Nexus::TestBorderAgentEphemeralKey();
|
|
ot::Nexus::TestBorderAgentEphemeralKeyTapGeneration();
|
|
ot::Nexus::TestHistoryTrackerBorderAgentEpskcEvent();
|
|
ot::Nexus::TestBorderAgentTxtDataCallback();
|
|
ot::Nexus::TestBorderAgentServiceRegistration();
|
|
printf("All tests passed\n");
|
|
return 0;
|
|
}
|