Files
openthread/tests/nexus/test_border_agent.cpp
T
Abtin Keshavarzian 1e79496c57 [border-agent] handle mDNS service name conflict by renaming (#12790)
This commit adds support for handling mDNS service name conflicts by
automatically renaming the service when a collision is detected during
registration.

The new naming scheme appends a suffix based on the last two bytes of
the device's Extended Address (e.g., " #AB1E"). If this name also
conflicts, an additional index is appended (e.g., " #AB1E (1)").

Changes:
- Added `mServiceRenameIndex` to `Manager` and `EphemeralKeyManager`
  to track re-naming attempts.
- Updated `otBorderAgentSetMeshCoPServiceBaseName()` and CLI documentation
  to reflect the new naming and conflict resolution logic.
- Updated `OT_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME_MAX_LENGTH` to
  ensure the full name fits within the 63-character DNS label limit.
- Added Nexus tests to verify the renaming logic under conflict.
2026-04-06 23:47:39 -05:00

2584 lines
110 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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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>().AllocateAndInitPriorityConfirmablePostMessage(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, bool aExpectVendorInfo, const char *aVendorName = nullptr)
{
// 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 &timestamp = 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);
}
if (aExpectVendorInfo)
{
const char *expectedVendorName = (aVendorName != nullptr) ? aVendorName : aNode.Get<VendorInfo>().GetName();
const char *expectedModelName = aNode.Get<VendorInfo>().GetModel();
aTxtData.ValidateKey("vn", expectedVendorName);
VerifyOrQuit(info.mHasVendorName);
VerifyOrQuit(StringMatch(info.mVendorName, expectedVendorName));
aTxtData.ValidateKey("mn", expectedModelName);
VerifyOrQuit(info.mHasModelName);
VerifyOrQuit(StringMatch(info.mModelName, expectedModelName));
}
else
{
VerifyOrQuit(!aTxtData.ContainsKey("vn"));
VerifyOrQuit(!info.mHasVendorName);
VerifyOrQuit(!aTxtData.ContainsKey("mn"));
VerifyOrQuit(!info.mHasModelName);
}
}
//----------------------------------------------------------------------------------------------------------------------
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, /* aExpectVendorInfo */ false);
}
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 = 400;
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,
const char *aVendorName = nullptr)
{
TxtData txtData;
txtData.Init(aService.mTxtData, aService.mTxtDataLength);
ValidateMeshCoPTxtData(txtData, aNode, /* aExpectVendorInfo */ true, aVendorName);
}
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;
bool foundService;
bool foundEpskService;
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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
Log("- - - - - - - - - - - - - - - - -");
Log(" HostName: %s", service.mHostName);
Log(" ServiceInstance: %s", service.mServiceInstance);
Log(" ServiceType: %s", service.mServiceType);
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
Log(" Port: %u", service.mPort);
Log(" TTL: %lu", ToUlong(service.mTtl));
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);
foundService = true;
}
}
VerifyOrQuit(foundService);
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, /* aExptecVendorInfo */ true);
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);
foundService = false;
foundEpskService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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);
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
ValidateRegisteredServiceData(service, node0);
}
else if (StringMatch(service.mServiceType, "_meshcop-e._udp"))
{
VerifyOrQuit(!foundEpskService);
foundEpskService = true;
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);
VerifyOrQuit(service.mPort == kUdpPort);
VerifyOrQuit(service.mTxtDataLength == 1);
VerifyOrQuit(service.mTxtData[0] == 0);
}
}
VerifyOrQuit(foundService);
VerifyOrQuit(foundEpskService);
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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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.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);
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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);
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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);
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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);
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
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);
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop._udp"));
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
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);
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop._udp"));
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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;
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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, /* aExpectedVendorName */ "nexus");
// Check that vendor TXT data is included at the end of
// the registered service TXT data.
VerifyOrQuit(service.mTxtDataLength > sizeof(kVendorTxtData));
VerifyOrQuit(!memcmp(&service.mTxtData[service.mTxtDataLength - sizeof(kVendorTxtData)], kVendorTxtData,
sizeof(kVendorTxtData)));
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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);
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Change vendor name and validate that the registered mDNS service is updated accordingly");
SuccessOrQuit(node0.Get<VendorInfo>().SetName("RD:v"));
nexus.AdvanceTime(Time::kOneSecondInMsec);
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
VerifyOrQuit(iterator != nullptr);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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);
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Change vendor model and validate that the registered mDNS service is updated accordingly");
SuccessOrQuit(node0.Get<VendorInfo>().SetModel("model"));
nexus.AdvanceTime(Time::kOneSecondInMsec);
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
VerifyOrQuit(iterator != nullptr);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(!foundService);
foundService = true;
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);
}
else
{
VerifyOrQuit(!StringMatch(service.mServiceType, "_meshcop-e._udp"));
}
}
VerifyOrQuit(foundService);
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
}
void TestBorderAgentServiceRegistrationRename(void)
{
static const char kServiceBaseName[] = "VeryLongServiceBaseNameForTestingPurposeOfLimitsMax";
static const char kEphemeralKey[] = "nexus1234";
static constexpr uint32_t kUdpPort = 49155;
Core nexus;
Node &node0 = nexus.CreateNode();
Node &node1 = nexus.CreateNode();
Mac::ExtAddress extAddress;
Dns::Multicast::Core::Iterator *iterator;
Dns::Multicast::Core::Service service;
Dns::Multicast::Core::EntryState entryState;
String<Dns::Name::kMaxLabelSize> expectedName;
bool foundService;
bool foundEpskService;
Log("------------------------------------------------------------------------------------------------------");
Log("TestBorderAgentServiceRegistrationRename");
nexus.AdvanceTime(0);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Set the same Ext address on both node0 and node1
extAddress.GenerateRandom();
extAddress.m8[6] = 0xbe;
extAddress.m8[7] = 0xef;
node0.Get<Mac::Mac>().SetExtAddress(extAddress);
node1.Get<Mac::Mac>().SetExtAddress(extAddress);
VerifyOrQuit(node1.Get<Mac::Mac>().GetExtAddress() == node0.Get<Mac::Mac>().GetExtAddress());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Set the Service Base Name on both node0 and node1
SuccessOrQuit(node0.Get<MeshCoP::BorderAgent::Manager>().SetServiceBaseName(kServiceBaseName));
SuccessOrQuit(node1.Get<MeshCoP::BorderAgent::Manager>().SetServiceBaseName(kServiceBaseName));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Disable Border Agent function on node1
node1.Get<MeshCoP::BorderAgent::Manager>().SetEnabled(false);
node0.Form();
node1.Form();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Construct the expected service instance name label.
expectedName.Append("%s #%02X%02X", kServiceBaseName, extAddress.m8[6], extAddress.m8[7]);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 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(node1.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);
foundService = false;
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
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));
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(StringMatch(service.mServiceInstance, expectedName.AsCString()));
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);
foundService = true;
}
}
VerifyOrQuit(foundService);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Enable BorderAgent on node1");
node1.Get<MeshCoP::BorderAgent::Manager>().SetEnabled(true);
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate the registered mDNS MeshCop service by Border Agent on node1");
iterator = node1.Get<Dns::Multicast::Core>().AllocateIterator();
VerifyOrQuit(iterator != nullptr);
foundService = false;
while (node1.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
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));
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
Log("Validate that node1 properly renamed the conflicted service name");
VerifyOrQuit(StringStartsWith(service.mServiceInstance, expectedName.AsCString()));
expectedName.Append(" (1)");
VerifyOrQuit(StringMatch(service.mServiceInstance, expectedName.AsCString()));
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
ValidateRegisteredServiceData(service, node1);
foundService = true;
}
}
VerifyOrQuit(foundService);
node1.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Enable and start ephemeral key on node 1");
node1.Get<EphemeralKeyManager>().SetEnabled(true);
VerifyOrQuit(node1.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
node1.Get<EphemeralKeyManager>().SetCallback(HandleEphemeralKeyChange, &node1);
SuccessOrQuit(node1.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
VerifyOrQuit(node1.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the registered services - validate the same service name is used for `_meshcop-e` service");
iterator = node1.Get<Dns::Multicast::Core>().AllocateIterator();
VerifyOrQuit(iterator != nullptr);
foundService = false;
foundEpskService = false;
while (node1.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
{
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));
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(StringMatch(service.mServiceInstance, expectedName.AsCString()));
VerifyOrQuit(service.mPort == node1.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
ValidateRegisteredServiceData(service, node1);
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
foundService = true;
}
else if (StringMatch(service.mServiceType, "_meshcop-e._udp"))
{
VerifyOrQuit(StringMatch(service.mServiceInstance, expectedName.AsCString()));
VerifyOrQuit(service.mPort == kUdpPort);
VerifyOrQuit(service.mTxtDataLength == 1);
VerifyOrQuit(service.mTxtData[0] == 0);
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
foundEpskService = true;
}
}
VerifyOrQuit(foundService);
VerifyOrQuit(foundEpskService);
node1.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();
ot::Nexus::TestBorderAgentServiceRegistrationRename();
printf("All tests passed\n");
return 0;
}