mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
3ce616d835
This commit renames the `NetworkDiagnostic` namespace in `src/core/thread/` and its related types to `NetDiag` for brevity. It updates the corresponding filenames and header guards as well.
3580 lines
141 KiB
C++
3580 lines
141 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 Admitter = MeshCoP::BorderAgent::Admitter;
|
|
using Manager = MeshCoP::BorderAgent::Manager;
|
|
using Commissioner = MeshCoP::Commissioner;
|
|
using Joiner = MeshCoP::Joiner;
|
|
|
|
static LogLevel kLogLevel = kLogLevelCrit;
|
|
|
|
void TestBorderAdmitterPrimeSelection(void)
|
|
{
|
|
Core nexus;
|
|
Node &leader = nexus.CreateNode();
|
|
Node &node1 = nexus.CreateNode();
|
|
Node &node2 = nexus.CreateNode();
|
|
Node &node3 = nexus.CreateNode();
|
|
Node *nodes[] = {&node1, &node2, &node3};
|
|
NetworkData::Service::Iterator netDataIter(leader.GetInstance());
|
|
uint16_t rloc16;
|
|
bool found;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAdmitterPrimeSelection");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevel));
|
|
|
|
// Form the topology.
|
|
|
|
leader.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
node1.Join(leader);
|
|
node2.Join(leader);
|
|
node3.Join(leader);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneMinuteInMsec);
|
|
|
|
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
|
|
VerifyOrQuit(node1.Get<Mle::Mle>().IsRouter());
|
|
VerifyOrQuit(node2.Get<Mle::Mle>().IsRouter());
|
|
VerifyOrQuit(node3.Get<Mle::Mle>().IsRouter());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check Border Admitter initial state");
|
|
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!node2.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!node3.Get<Admitter>().IsEnabled());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Admitter role on `node1` and validate that it becomes the Prime Admitter");
|
|
|
|
node1.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(node1.Get<Admitter>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the NetworkData contains a single Admitter Service from `node1`");
|
|
|
|
netDataIter.Reset();
|
|
|
|
SuccessOrQuit(netDataIter.GetNextBorderAdmitterInfo(rloc16));
|
|
VerifyOrQuit(rloc16 == node1.Get<Mle::Mle>().GetRloc16());
|
|
|
|
VerifyOrQuit(netDataIter.GetNextBorderAdmitterInfo(rloc16) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Admitter role on `node2` & `node3` and validate that `node1` remains the Prime Admitter");
|
|
|
|
node2.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(node2.Get<Admitter>().IsEnabled());
|
|
|
|
node3.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(node3.Get<Admitter>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
VerifyOrQuit(!node2.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!node3.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the NetworkData contains the Admitter Service from `node1`");
|
|
|
|
netDataIter.Reset();
|
|
|
|
SuccessOrQuit(netDataIter.GetNextBorderAdmitterInfo(rloc16));
|
|
VerifyOrQuit(rloc16 == node1.Get<Mle::Mle>().GetRloc16());
|
|
|
|
VerifyOrQuit(netDataIter.GetNextBorderAdmitterInfo(rloc16) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disable Admitter role on `node1` and check that another Prime Admitter is elected");
|
|
|
|
node1.Get<Admitter>().SetEnabled(false);
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
nexus.AdvanceTime(75 * Time::kOneSecondInMsec);
|
|
|
|
// We use `!=` to do an "exclusive or" logic check (either node2 or node3 is prime and not both)
|
|
VerifyOrQuit(node2.Get<Admitter>().IsPrimeAdmitter() != node3.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
VerifyOrQuit(!node2.Get<Admitter>().IsActiveCommissioner());
|
|
VerifyOrQuit(!node3.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the NetworkData contains a single Admitter Service entry");
|
|
|
|
netDataIter.Reset();
|
|
|
|
SuccessOrQuit(netDataIter.GetNextBorderAdmitterInfo(rloc16));
|
|
|
|
VerifyOrQuit(rloc16 == (node2.Get<Admitter>().IsPrimeAdmitter() ? node2.Get<Mle::Mle>().GetRloc16()
|
|
: node3.Get<Mle::Mle>().GetRloc16()));
|
|
|
|
VerifyOrQuit(netDataIter.GetNextBorderAdmitterInfo(rloc16) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disable Admitter role on all nodes (`node2` and `node3`)");
|
|
|
|
node2.Get<Admitter>().SetEnabled(false);
|
|
VerifyOrQuit(!node2.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!node2.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
node3.Get<Admitter>().SetEnabled(false);
|
|
VerifyOrQuit(!node3.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!node3.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the NetworkData contains no Admitter Service entry");
|
|
|
|
netDataIter.Reset();
|
|
VerifyOrQuit(netDataIter.GetNextBorderAdmitterInfo(rloc16) == kErrorNotFound);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Admitter role on all 3 nodes at the same time");
|
|
|
|
node1.Get<Admitter>().SetEnabled(true);
|
|
node2.Get<Admitter>().SetEnabled(true);
|
|
node3.Get<Admitter>().SetEnabled(true);
|
|
|
|
VerifyOrQuit(node1.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(node2.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(node3.Get<Admitter>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(75 * Time::kOneSecondInMsec);
|
|
|
|
Log("Validate that we end up with a single Prime Admitter");
|
|
|
|
found = false;
|
|
|
|
for (Node *node : nodes)
|
|
{
|
|
if (node->Get<Admitter>().IsPrimeAdmitter())
|
|
{
|
|
VerifyOrQuit(!found);
|
|
found = true;
|
|
rloc16 = node->Get<Mle::Mle>().GetRloc16();
|
|
}
|
|
|
|
VerifyOrQuit(!node->Get<Admitter>().IsActiveCommissioner());
|
|
}
|
|
|
|
VerifyOrQuit(found);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
enum AdmitterState : uint8_t
|
|
{
|
|
kAdmitterUnavailable = 0,
|
|
kAdmitterReady = 1,
|
|
kAdmitterActive = 2,
|
|
kAdmitterConflictError = 3,
|
|
};
|
|
|
|
const char *AdmitterStateToString(uint8_t aState)
|
|
{
|
|
const char *str = "Unknown";
|
|
|
|
switch (aState)
|
|
{
|
|
case kAdmitterUnavailable:
|
|
str = "AdmitterUnavailable";
|
|
break;
|
|
case kAdmitterReady:
|
|
str = "AdmitterReady";
|
|
break;
|
|
case kAdmitterActive:
|
|
str = "AdmitterActive";
|
|
break;
|
|
case kAdmitterConflictError:
|
|
str = "AdmitterConflictError";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
struct AdmitterInfo
|
|
{
|
|
// Tracks information about Admitter from received
|
|
// messages on an Enroller.
|
|
|
|
void ParseAdmitterInfo(const Coap::Message &aResponse)
|
|
{
|
|
Error error;
|
|
|
|
error = Tlv::Find<MeshCoP::AdmitterStateTlv>(aResponse, mAdmitterState);
|
|
VerifyOrQuit(error == kErrorNone || error == kErrorNotFound);
|
|
mHasAdmitterState = (error == kErrorNone);
|
|
|
|
error = Tlv::Find<MeshCoP::CommissionerSessionIdTlv>(aResponse, mCommrSessionId);
|
|
VerifyOrQuit(error == kErrorNone || error == kErrorNotFound);
|
|
mHasCommrSessionId = (error == kErrorNone);
|
|
|
|
error = Tlv::Find<MeshCoP::JoinerUdpPortTlv>(aResponse, mJoinerUdp);
|
|
VerifyOrQuit(error == kErrorNone || error == kErrorNotFound);
|
|
mHasJoinerUdp = (error == kErrorNone);
|
|
}
|
|
|
|
bool mHasAdmitterState;
|
|
bool mHasCommrSessionId;
|
|
bool mHasJoinerUdp;
|
|
uint8_t mResponseState;
|
|
uint8_t mAdmitterState;
|
|
uint16_t mCommrSessionId;
|
|
uint16_t mJoinerUdp;
|
|
};
|
|
|
|
struct ResponseContext : public AdmitterInfo, public Clearable<ResponseContext>
|
|
{
|
|
// Tracks information in a TMF response received by an enroller.
|
|
// (populated by `HandleResponse()`).
|
|
|
|
bool mReceived;
|
|
};
|
|
|
|
void HandleResponse(void *aContext, Coap::Msg *aMsg, Error aResult)
|
|
{
|
|
OT_UNUSED_VARIABLE(aResult);
|
|
|
|
ResponseContext *responseContext;
|
|
|
|
VerifyOrQuit(aContext != nullptr);
|
|
VerifyOrQuit(aMsg != nullptr);
|
|
|
|
responseContext = static_cast<ResponseContext *>(aContext);
|
|
|
|
VerifyOrQuit(!responseContext->mReceived); // Duplicate response
|
|
responseContext->mReceived = true;
|
|
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::StateTlv>(aMsg->mMessage, responseContext->mResponseState));
|
|
|
|
responseContext->ParseAdmitterInfo(aMsg->mMessage);
|
|
|
|
Log(" Received response - %s",
|
|
MeshCoP::StateTlv::StateToString(static_cast<MeshCoP::StateTlv::State>(responseContext->mResponseState)));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
struct ReceiveContext
|
|
{
|
|
// Tracks information from received TMF on an Enroller send by
|
|
// Admitter.
|
|
|
|
static constexpr uint8_t kMaxEntries = 16;
|
|
|
|
void Clear(void)
|
|
{
|
|
mStateReports.Clear();
|
|
mRelayRxMsgs.DequeueAndFreeAll();
|
|
mProxyRxMsgs.DequeueAndFreeAll();
|
|
}
|
|
|
|
bool HasReceivedReportState(void) const { return !mStateReports.IsEmpty(); }
|
|
uint8_t GetLastReportedAdmitterState(void) { return mStateReports.Back()->mAdmitterState; }
|
|
|
|
Array<AdmitterInfo, kMaxEntries> mStateReports;
|
|
MessageQueue mRelayRxMsgs;
|
|
MessageQueue mProxyRxMsgs;
|
|
};
|
|
|
|
bool HandleResource(void *aContext, Uri aUri, Coap::Msg &aMsg)
|
|
{
|
|
bool didHandle = false;
|
|
ReceiveContext *recvContext;
|
|
AdmitterInfo *info;
|
|
Message *msgClone;
|
|
uint16_t joinerPort;
|
|
Ip6::InterfaceIdentifier joinerIid;
|
|
|
|
VerifyOrQuit(aContext != nullptr);
|
|
recvContext = static_cast<ReceiveContext *>(aContext);
|
|
|
|
switch (aUri)
|
|
{
|
|
case kUriEnrollerReportState:
|
|
didHandle = true;
|
|
info = recvContext->mStateReports.PushBack();
|
|
VerifyOrQuit(info != nullptr);
|
|
info->ParseAdmitterInfo(aMsg.mMessage);
|
|
VerifyOrQuit(info->mHasAdmitterState);
|
|
Log(" Received `EnrollerReportState` with state %s", AdmitterStateToString(info->mAdmitterState));
|
|
break;
|
|
|
|
case kUriRelayRx:
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerUdpPortTlv>(aMsg.mMessage, joinerPort));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(aMsg.mMessage, joinerIid));
|
|
Log(" Received `RelayRx` from joiner - port:%u iid:%s", joinerPort, joinerIid.ToString().AsCString());
|
|
|
|
msgClone = aMsg.mMessage.Clone<kNoReservedHeader>();
|
|
VerifyOrQuit(msgClone != nullptr);
|
|
recvContext->mRelayRxMsgs.Enqueue(*msgClone);
|
|
break;
|
|
|
|
case kUriProxyRx:
|
|
Log(" Received `ProxyRx`");
|
|
msgClone = aMsg.mMessage.Clone<kNoReservedHeader>();
|
|
VerifyOrQuit(msgClone != nullptr);
|
|
recvContext->mProxyRxMsgs.Enqueue(*msgClone);
|
|
break;
|
|
default:
|
|
Log(" Received unexpected URI %u", aUri);
|
|
break;
|
|
}
|
|
|
|
return didHandle;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TestBorderAdmitterEnrollerInteraction(void)
|
|
{
|
|
static const char kEnrollerId[] = "en00";
|
|
static const char kEnrollerIdAlt[] = "en01";
|
|
|
|
static const uint8_t kEnrollerTimeoutInSec = 50;
|
|
|
|
Core nexus;
|
|
Node &admitter = nexus.CreateNode();
|
|
Node &enroller = nexus.CreateNode();
|
|
Ip6::SockAddr sockAddr;
|
|
Pskc pskc;
|
|
Admitter::Iterator iter;
|
|
Admitter::EnrollerInfo enrollerInfo;
|
|
Admitter::JoinerInfo joinerInfo;
|
|
Coap::Message *message;
|
|
uint8_t mode;
|
|
MeshCoP::SteeringData steeringData;
|
|
MeshCoP::SteeringData leaderSteeringData;
|
|
ResponseContext responseContext;
|
|
ReceiveContext recvContext;
|
|
Manager::SessionInfo *sessionInfo;
|
|
uint16_t rloc16;
|
|
uint16_t sessionId;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAdmitterEnrollerInteraction");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// Form the topology:
|
|
// - `admitter` forms its own network (acting as leader)
|
|
// - `enroller` stays disconnected.
|
|
|
|
admitter.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
VerifyOrQuit(admitter.Get<Mle::Mle>().IsLeader());
|
|
|
|
SuccessOrQuit(enroller.Get<Mac::Mac>().SetPanChannel(admitter.Get<Mac::Mac>().GetPanChannel()));
|
|
enroller.Get<Mac::Mac>().SetPanId(admitter.Get<Mac::Mac>().GetPanId());
|
|
enroller.Get<ThreadNetif>().Up();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Border Admitter on `admitter`");
|
|
|
|
admitter.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<Ip6::Filter>().AddUnsecurePort(admitter.Get<Manager>().GetUdpPort()));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a DTLS connection from `enroller` to `admitter`");
|
|
|
|
sockAddr.SetAddress(admitter.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
sockAddr.SetPort(admitter.Get<Manager>().GetUdpPort());
|
|
|
|
admitter.Get<KeyManager>().GetPskc(pskc);
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
|
|
enroller.Get<Tmf::SecureAgent>().RegisterResourceHandler(HandleResource, &recvContext);
|
|
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().Open(0));
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(enroller.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check that Enroller session list on `admitter` is empty");
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerRegister` message from `enroller` to `admitter`");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
mode = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx | MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
|
|
steeringData.SetToPermitAllJoiners();
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerId));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContext.mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the enroller list on `admitter`");
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
|
|
sessionInfo = &enrollerInfo.mSessionInfo;
|
|
VerifyOrQuit(sessionInfo->mIsConnected);
|
|
VerifyOrQuit(!sessionInfo->mIsCommissioner);
|
|
VerifyOrQuit(enroller.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo->mPeerSockAddr.mAddress)));
|
|
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `admitter` becomes active commissioner");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `EnrollerReportState` is received with the updated Admitter state");
|
|
|
|
VerifyOrQuit(recvContext.HasReceivedReportState());
|
|
VerifyOrQuit(recvContext.GetLastReportedAdmitterState() == kAdmitterActive);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that commissioner steering data and session ID are properly set");
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == steeringData);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Ensure no changes before enroller timeout");
|
|
|
|
recvContext.Clear();
|
|
|
|
nexus.AdvanceTime((kEnrollerTimeoutInSec - 3) * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!recvContext.HasReceivedReportState());
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(enrollerInfo.mRegisterDuration >= 48 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerKeepAlive` message");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContext.mHasAdmitterState);
|
|
VerifyOrQuit(responseContext.mAdmitterState == kAdmitterActive);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
|
|
VerifyOrQuit(!recvContext.HasReceivedReportState());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Wait until just before the timeout and validate that `admitter` remains active commissioner");
|
|
|
|
nexus.AdvanceTime((kEnrollerTimeoutInSec - 2) * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!recvContext.HasReceivedReportState());
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerKeepAlive` message with an Enroller Mode TLV changing the mode");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
mode = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx;
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContext.mHasAdmitterState);
|
|
VerifyOrQuit(responseContext.mAdmitterState == kAdmitterActive);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
VerifyOrQuit(!recvContext.HasReceivedReportState());
|
|
|
|
nexus.AdvanceTime((kEnrollerTimeoutInSec - 2) * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!recvContext.HasReceivedReportState());
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerKeepAlive` message with Steering Data TLV");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(steeringData.Init(MeshCoP::SteeringData::kMaxLength));
|
|
SuccessOrQuit(steeringData.UpdateBloomFilter(admitter.Get<Mac::Mac>().GetExtAddress()));
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContext.mHasAdmitterState);
|
|
VerifyOrQuit(responseContext.mAdmitterState == kAdmitterActive);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate Network Data is updated with the new Steering Data");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!recvContext.HasReceivedReportState());
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == steeringData);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the enroller timeout");
|
|
|
|
// Already 2 seconds has passed since sending last `EnrollerKeepAlive`
|
|
nexus.AdvanceTime((kEnrollerTimeoutInSec - 2) * Time::kOneSecondInMsec - 50);
|
|
|
|
VerifyOrQuit(!recvContext.HasReceivedReportState());
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
nexus.AdvanceTime(75);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the `admitter` resigns from being active commissioner");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
VerifyOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16) == kErrorNotFound);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!enroller.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Re-establish DTLS session");
|
|
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
VerifyOrQuit(enroller.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Register as enroller again");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerId));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContext.mHasAdmitterState);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `admitter` becomes active commissioner again");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == steeringData);
|
|
|
|
nexus.AdvanceTime((kEnrollerTimeoutInSec / 2) * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerKeepAlive` message from `enroller` with `kReject` status, resigning enroller role");
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kReject));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kReject);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
VerifyOrQuit(enroller.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Register as enroller again");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerId));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContext.mHasAdmitterState);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
nexus.AdvanceTime((kEnrollerTimeoutInSec / 2) * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerRegister` message while already registered, with different parameters");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
mode = 0;
|
|
SuccessOrQuit(steeringData.Init(8));
|
|
SuccessOrQuit(steeringData.UpdateBloomFilter(admitter.Get<Mac::Mac>().GetExtAddress()));
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerIdAlt));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kAccept);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
Log("Validate that enroller info is updated accordingly");
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerIdAlt));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the Network Data (Commissioner Data) is also updated");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == steeringData);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the `EnrollerRegister` extended the keep-alive timeout");
|
|
|
|
recvContext.Clear();
|
|
|
|
nexus.AdvanceTime((kEnrollerTimeoutInSec - 3) * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!recvContext.HasReceivedReportState());
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerIdAlt));
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an invalid `EnrollerKeepAlive` message without State TLV and validate that it is rejected");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kReject);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check that enroller is removed on `admitter`, and it stops being active commissioner");
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(!enroller.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
VerifyOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Re-establish DTLS session");
|
|
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
VerifyOrQuit(enroller.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send `EnrollerRegister` with missing TLVs, validate that it is rejected");
|
|
|
|
for (uint16_t testIter = 0; testIter < 3; testIter++)
|
|
{
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
// Skip one of the required TLVs for each `testIter`.
|
|
|
|
if (testIter != 0)
|
|
{
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerId));
|
|
}
|
|
|
|
if (testIter != 1)
|
|
{
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
}
|
|
|
|
if (testIter != 2)
|
|
{
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
}
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(250);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kReject);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
Log("Register as enroller with invalid Steering Data, validate that Admitter rejects");
|
|
|
|
for (uint8_t length = 1; length <= 16; length++)
|
|
{
|
|
// Steering Data Length 1 can only be used with
|
|
// `SetPermitAllJoiners()` or empty. Lengths of 8
|
|
// and 16 are valid.
|
|
|
|
if ((length == 8) || (length == 16))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Log("Send `EnrollerRegister` with invalid Steering Data length %u, validate that it is rejected", length);
|
|
|
|
SuccessOrQuit(steeringData.Init(length));
|
|
SuccessOrQuit(steeringData.UpdateBloomFilter(admitter.Get<Mac::Mac>().GetExtAddress()));
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerId));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(250);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kReject);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
Log("Register as enroller with empty Steering Data, validate that Admitter accepts");
|
|
|
|
SuccessOrQuit(steeringData.Init(1));
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerId));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContext.Clear();
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContext));
|
|
|
|
nexus.AdvanceTime(250);
|
|
|
|
VerifyOrQuit(responseContext.mReceived);
|
|
VerifyOrQuit(responseContext.mResponseState == MeshCoP::StateTlv::kAccept);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TestBorderAdmitterCommissionerConflictAndPetitionerRetry(void)
|
|
{
|
|
static const char kEnrollerId[] = "TestEnroller1234";
|
|
|
|
Core nexus;
|
|
Node &admitter = nexus.CreateNode();
|
|
Node &enroller = nexus.CreateNode();
|
|
Node &otherCommr = nexus.CreateNode();
|
|
Ip6::SockAddr sockAddr;
|
|
Pskc pskc;
|
|
Admitter::Iterator iter;
|
|
Admitter::EnrollerInfo enrollerInfo;
|
|
Coap::Message *message;
|
|
uint8_t mode;
|
|
MeshCoP::SteeringData steeringData;
|
|
MeshCoP::SteeringData leaderSteeringData;
|
|
ReceiveContext recvContext;
|
|
uint16_t rloc16;
|
|
uint16_t sessionId;
|
|
uint16_t numStateChanges;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAdmitterCommissionerConflictAndPetitionerRetry");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// Form the topology:
|
|
// - `admitter` forms the network (as leader)
|
|
// - `otherCommr` joins the same network.
|
|
// - `enroller` stays disconnected.
|
|
|
|
admitter.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
|
|
otherCommr.Join(admitter);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneMinuteInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Mle::Mle>().IsLeader());
|
|
VerifyOrQuit(otherCommr.Get<Mle::Mle>().IsRouter());
|
|
|
|
SuccessOrQuit(enroller.Get<Mac::Mac>().SetPanChannel(admitter.Get<Mac::Mac>().GetPanChannel()));
|
|
enroller.Get<Mac::Mac>().SetPanId(admitter.Get<Mac::Mac>().GetPanId());
|
|
enroller.Get<ThreadNetif>().Up();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Border Admitter on `admitter`");
|
|
|
|
admitter.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<Ip6::Filter>().AddUnsecurePort(admitter.Get<Manager>().GetUdpPort()));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Make `otherCommr` the active commissioner");
|
|
|
|
SuccessOrQuit(otherCommr.Get<Commissioner>().Start(nullptr, nullptr, nullptr));
|
|
|
|
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(otherCommr.Get<Commissioner>().GetState() == Commissioner::kStateActive);
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == otherCommr.Get<Mle::Mle>().GetRloc16());
|
|
|
|
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a DTLS connection from `enroller` to `admitter`");
|
|
|
|
sockAddr.SetAddress(admitter.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
sockAddr.SetPort(admitter.Get<Manager>().GetUdpPort());
|
|
|
|
admitter.Get<KeyManager>().GetPskc(pskc);
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
|
|
enroller.Get<Tmf::SecureAgent>().RegisterResourceHandler(HandleResource, &recvContext);
|
|
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().Open(0));
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(enroller.Get<Tmf::SecureAgent>().IsConnected());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerRegister` message from `enroller` to `admitter`");
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
mode = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx | MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
|
|
steeringData.SetToPermitAllJoiners();
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerId));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the enroller list on `admitter`");
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Since there is another commissioner active, validate that `admitter` fails to become active");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPetitionRejected());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `EnrollerReportState` is received with `ConflictError` state");
|
|
|
|
VerifyOrQuit(recvContext.HasReceivedReportState());
|
|
VerifyOrQuit(recvContext.GetLastReportedAdmitterState() == kAdmitterConflictError);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPetitionRejected());
|
|
|
|
VerifyOrQuit(otherCommr.Get<Commissioner>().GetState() == Commissioner::kStateActive);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Stop `otherCommr` from acting as active commissioner");
|
|
|
|
recvContext.Clear();
|
|
|
|
SuccessOrQuit(otherCommr.Get<Commissioner>().Stop());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the `admitter` will detect this, petitions, and becomes active commissioner");
|
|
|
|
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsPetitionRejected());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `EnrollerReportState` is received now with `Active` state");
|
|
|
|
VerifyOrQuit(recvContext.HasReceivedReportState());
|
|
VerifyOrQuit(recvContext.GetLastReportedAdmitterState() == kAdmitterActive);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check the Network Data (Commissioner Data) to be properly set");
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == steeringData);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("From `otherCommr` forcefully evict the current active commissioner (`admitter`)");
|
|
|
|
recvContext.Clear();
|
|
|
|
SuccessOrQuit(otherCommr.Get<Manager>().EvictActiveCommissioner());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send `EnrollerKeepAlive` three times, 20 seconds apart to maintain the enroller connection");
|
|
|
|
for (uint8_t i = 0; i < 3; i++)
|
|
{
|
|
nexus.AdvanceTime(20 * Time::kOneSecondInMsec);
|
|
|
|
message = enroller.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
|
|
SuccessOrQuit(enroller.Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
SuccessOrQuit(iter.GetNextEnrollerInfo(enrollerInfo));
|
|
|
|
VerifyOrQuit(StringMatch(enrollerInfo.mId, kEnrollerId));
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the eviction was properly detected, and petitioner retry mechanism did restore it");
|
|
|
|
VerifyOrQuit(recvContext.HasReceivedReportState());
|
|
VerifyOrQuit(recvContext.GetLastReportedAdmitterState() == kAdmitterActive);
|
|
|
|
numStateChanges = recvContext.mStateReports.GetLength();
|
|
VerifyOrQuit(numStateChanges >= 2);
|
|
VerifyOrQuit(recvContext.mStateReports[numStateChanges - 2].mAdmitterState == kAdmitterReady);
|
|
VerifyOrQuit(recvContext.mStateReports[numStateChanges - 1].mAdmitterState == kAdmitterActive);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
template <uint8_t kNumEnrollers>
|
|
uint8_t FindMatchingEnroller(const Admitter::EnrollerInfo &aInfo,
|
|
const char *(&aEnrollerIds)[kNumEnrollers],
|
|
BitSet<kNumEnrollers> &aFoundIndexes)
|
|
{
|
|
// Finds a matching enroller by comparing `aInfo.mId` against
|
|
// `aEnrollerIds[]` and returns the matched index. This function
|
|
// ensures each enroller is found only once by checking that the
|
|
// index is not already in `aFoundIndexes`, then updates
|
|
// `aFoundIndexes`.
|
|
|
|
uint8_t matchedIndex = kNumEnrollers;
|
|
|
|
for (uint8_t index = 0; index < kNumEnrollers; index++)
|
|
{
|
|
if (StringMatch(aInfo.mId, aEnrollerIds[index]))
|
|
{
|
|
matchedIndex = index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(matchedIndex < kNumEnrollers);
|
|
|
|
VerifyOrQuit(!aFoundIndexes.Has(matchedIndex));
|
|
aFoundIndexes.Add(matchedIndex);
|
|
|
|
return matchedIndex;
|
|
}
|
|
|
|
template <uint8_t kNumEnrollers> bool DidFindAllEnrollers(const BitSet<kNumEnrollers> &aFoundIndexes)
|
|
{
|
|
bool didFindAll = true;
|
|
|
|
for (uint8_t index = 0; index < kNumEnrollers; index++)
|
|
{
|
|
if (!aFoundIndexes.Has(index))
|
|
{
|
|
didFindAll = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return didFindAll;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
void LogEnroller(const Admitter::EnrollerInfo &aInfo)
|
|
{
|
|
Log(" Enroller - id:%s steeringData:%s mode:0x%02x[%c%c]", aInfo.mId,
|
|
AsCoreType(&aInfo.mSteeringData).ToString().AsCString(), aInfo.mMode,
|
|
aInfo.mMode & MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx ? 'J' : '-',
|
|
aInfo.mMode & MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx ? 'U' : '-');
|
|
}
|
|
|
|
void LogJoiner(const Admitter::JoinerInfo &aInfo)
|
|
{
|
|
Log(" Joiner - iid:%s, msec-till-expire:%lu", AsCoreType(&aInfo.mIid).ToString().AsCString(),
|
|
ToUlong(aInfo.mMsecTillExpiration));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TestBorderAdmitterMultipleEnrollers(void)
|
|
{
|
|
static constexpr uint8_t kNumEnrollers = 4;
|
|
|
|
static const char *kEnrollerIds[kNumEnrollers] = {"earth", "water", "wind", "fire"};
|
|
|
|
Core nexus;
|
|
Node &admitter = nexus.CreateNode();
|
|
Node *enrollers[kNumEnrollers];
|
|
Ip6::SockAddr sockAddr;
|
|
Pskc pskc;
|
|
Admitter::Iterator iter;
|
|
Admitter::EnrollerInfo enrollerInfo;
|
|
Admitter::JoinerInfo joinerInfo;
|
|
Coap::Message *message;
|
|
uint8_t mode;
|
|
MeshCoP::SteeringData steeringData[kNumEnrollers];
|
|
ReceiveContext recvContext[kNumEnrollers];
|
|
ResponseContext responseContexts[kNumEnrollers];
|
|
BitSet<kNumEnrollers> foundEnrollers;
|
|
MeshCoP::SteeringData leaderSteeringData;
|
|
MeshCoP::SteeringData combinedSteeringData;
|
|
uint16_t rloc16;
|
|
uint16_t sessionId;
|
|
Mac::ExtAddress joinerIid;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAdmitterMultipleEnrollers");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
enrollers[i] = &nexus.CreateNode();
|
|
}
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// Form the topology:
|
|
// - `admitter` forms the network (as leader)
|
|
// - All enrollers stay disconnected.
|
|
|
|
admitter.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Mle::Mle>().IsLeader());
|
|
|
|
for (Node *enroller : enrollers)
|
|
{
|
|
SuccessOrQuit(enroller->Get<Mac::Mac>().SetPanChannel(admitter.Get<Mac::Mac>().GetPanChannel()));
|
|
enroller->Get<Mac::Mac>().SetPanId(admitter.Get<Mac::Mac>().GetPanId());
|
|
enroller->Get<ThreadNetif>().Up();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Border Admitter on `admitter`");
|
|
|
|
admitter.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<Ip6::Filter>().AddUnsecurePort(admitter.Get<Manager>().GetUdpPort()));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a DTLS connection from all `enrollers` to `admitter`");
|
|
|
|
sockAddr.SetAddress(admitter.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
sockAddr.SetPort(admitter.Get<Manager>().GetUdpPort());
|
|
|
|
admitter.Get<KeyManager>().GetPskc(pskc);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Node *enroller = enrollers[i];
|
|
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
|
|
recvContext[i].Clear();
|
|
enroller->Get<Tmf::SecureAgent>().RegisterResourceHandler(HandleResource, &recvContext[i]);
|
|
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().Open(0));
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(enroller->Get<Tmf::SecureAgent>().IsConnected());
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Prepare Steering Data for each enroller");
|
|
|
|
mode = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx | MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
|
|
steeringData[0].SetToPermitAllJoiners();
|
|
|
|
SuccessOrQuit(steeringData[1].Init(16));
|
|
|
|
for (uint8_t numJoiners = 0; numJoiners < 3; numJoiners++)
|
|
{
|
|
joinerIid.GenerateRandom();
|
|
SuccessOrQuit(steeringData[1].UpdateBloomFilter(joinerIid));
|
|
}
|
|
|
|
SuccessOrQuit(steeringData[2].Init(8));
|
|
joinerIid.GenerateRandom();
|
|
SuccessOrQuit(steeringData[2].UpdateBloomFilter(joinerIid));
|
|
|
|
SuccessOrQuit(steeringData[3].Init(8));
|
|
joinerIid.GenerateRandom();
|
|
SuccessOrQuit(steeringData[3].UpdateBloomFilter(joinerIid));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerRegister` message from all `enrollers`");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
message =
|
|
enrollers[i]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerIds[i]));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, mode));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData[i]));
|
|
|
|
responseContexts[i].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[i]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[i]));
|
|
}
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check that all registrations were accepted");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
VerifyOrQuit(responseContexts[i].mReceived);
|
|
VerifyOrQuit(responseContexts[i].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[i].mHasAdmitterState);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `admitter` becomes active commissioner");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData.GetLength() == 1);
|
|
VerifyOrQuit(leaderSteeringData.PermitsAllJoiners());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `EnrollerReportState` is received with the updated Admitter state");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
// Some enrollers may already get the updated state in the Register response
|
|
if (recvContext[i].HasReceivedReportState())
|
|
{
|
|
VerifyOrQuit(recvContext[i].GetLastReportedAdmitterState() == kAdmitterActive);
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - -. - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the enroller list on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData[matchedIndex]);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send a keep alive from first enroller with reject status (to unregister the enroller)");
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kReject));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kReject);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check enroller info and that enroller 0 is no longer present");
|
|
|
|
foundEnrollers.Clear();
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData[matchedIndex]);
|
|
VerifyOrQuit(enrollerInfo.mMode == mode);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(!foundEnrollers.Has(0));
|
|
VerifyOrQuit(foundEnrollers.Has(1));
|
|
VerifyOrQuit(foundEnrollers.Has(2));
|
|
VerifyOrQuit(foundEnrollers.Has(3));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `admitter` is still active commissioner and the steering data is updated");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
combinedSteeringData = steeringData[1];
|
|
SuccessOrQuit(combinedSteeringData.MergeBloomFilterWith(steeringData[2]));
|
|
SuccessOrQuit(combinedSteeringData.MergeBloomFilterWith(steeringData[3]));
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == combinedSteeringData);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Wait 20 seconds, send keep-alive from enrollers[2,3] but not from [1]");
|
|
|
|
nexus.AdvanceTime(20 * Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 2; i < kNumEnrollers; i++)
|
|
{
|
|
message =
|
|
enrollers[i]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
|
|
responseContexts[i].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[i]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[i]));
|
|
}
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 2; i < kNumEnrollers; i++)
|
|
{
|
|
VerifyOrQuit(responseContexts[i].mReceived);
|
|
VerifyOrQuit(responseContexts[i].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Wait 35 more seconds and check that enroller 1 is now removed");
|
|
|
|
nexus.AdvanceTime(35 * Time::kOneSecondInMsec);
|
|
|
|
foundEnrollers.Clear();
|
|
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
LogEnroller(enrollerInfo);
|
|
|
|
FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(!foundEnrollers.Has(0));
|
|
VerifyOrQuit(!foundEnrollers.Has(1));
|
|
VerifyOrQuit(foundEnrollers.Has(2));
|
|
VerifyOrQuit(foundEnrollers.Has(3));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `admitter` updates the steering data");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
combinedSteeringData = steeringData[2];
|
|
SuccessOrQuit(combinedSteeringData.MergeBloomFilterWith(steeringData[3]));
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == combinedSteeringData);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TestBorderAdmitterJoinerEnrollerInteraction(void)
|
|
{
|
|
static constexpr uint8_t kNumEnrollers = 4;
|
|
static constexpr uint8_t kNumJoiners = 2;
|
|
|
|
static const char *kEnrollerIds[kNumEnrollers] = {"diamond", "ruby", "sapphire", "emerald"};
|
|
|
|
static const char kPskd[] = "J01NME1234";
|
|
|
|
Core nexus;
|
|
Node &admitter = nexus.CreateNode();
|
|
Node *enrollers[kNumEnrollers];
|
|
Node *joiners[kNumJoiners];
|
|
Ip6::SockAddr sockAddr;
|
|
Pskc pskc;
|
|
Coap::Message *message;
|
|
uint8_t modes[kNumEnrollers];
|
|
ResponseContext responseContexts[kNumEnrollers];
|
|
ReceiveContext recvContext[kNumEnrollers];
|
|
MeshCoP::SteeringData steeringData;
|
|
MeshCoP::SteeringData leaderSteeringData;
|
|
Ip6::InterfaceIdentifier joinerIids[kNumJoiners];
|
|
Ip6::InterfaceIdentifier wildcardJoinerIid;
|
|
Admitter::Iterator iter;
|
|
Admitter::EnrollerInfo enrollerInfo;
|
|
Admitter::JoinerInfo joinerInfo;
|
|
BitSet<kNumEnrollers> foundEnrollers;
|
|
BitSet<kNumJoiners> foundJoiners;
|
|
uint16_t sessionId;
|
|
uint16_t rloc16;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAdmitterJoinerEnrollerInteraction");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
enrollers[i] = &nexus.CreateNode();
|
|
}
|
|
|
|
for (uint8_t j = 0; j < kNumJoiners; j++)
|
|
{
|
|
joiners[j] = &nexus.CreateNode();
|
|
}
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// Form the topology:
|
|
// - `admitter` forms the network (as leader)
|
|
// - All enrollers stay disconnected.
|
|
|
|
admitter.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Mle::Mle>().IsLeader());
|
|
|
|
for (Node *enroller : enrollers)
|
|
{
|
|
SuccessOrQuit(enroller->Get<Mac::Mac>().SetPanChannel(admitter.Get<Mac::Mac>().GetPanChannel()));
|
|
enroller->Get<Mac::Mac>().SetPanId(admitter.Get<Mac::Mac>().GetPanId());
|
|
enroller->Get<ThreadNetif>().Up();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Border Admitter on `admitter`");
|
|
|
|
admitter.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<Ip6::Filter>().AddUnsecurePort(admitter.Get<Manager>().GetUdpPort()));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a DTLS connection from all `enrollers` to `admitter`");
|
|
|
|
sockAddr.SetAddress(admitter.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
sockAddr.SetPort(admitter.Get<Manager>().GetUdpPort());
|
|
|
|
admitter.Get<KeyManager>().GetPskc(pskc);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Node *enroller = enrollers[i];
|
|
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
|
|
recvContext[i].Clear();
|
|
enroller->Get<Tmf::SecureAgent>().RegisterResourceHandler(HandleResource, &recvContext[i]);
|
|
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().Open(0));
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(enroller->Get<Tmf::SecureAgent>().IsConnected());
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Prepare mode for each enroller");
|
|
|
|
modes[0] = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx | MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
modes[1] = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx | MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
modes[2] = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx;
|
|
modes[3] = MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
|
|
steeringData.SetToPermitAllJoiners();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerRegister` message from all `enrollers`");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
message =
|
|
enrollers[i]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerIds[i]));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, modes[i]));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContexts[i].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[i]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[i]));
|
|
}
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check that all registrations were accepted");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
VerifyOrQuit(responseContexts[i].mReceived);
|
|
VerifyOrQuit(responseContexts[i].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[i].mHasAdmitterState);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `admitter` becomes active commissioner");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == steeringData);
|
|
VerifyOrQuit(leaderSteeringData.PermitsAllJoiners());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the Enroller info on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start `joiners[0]`");
|
|
|
|
joiners[0]->Get<ThreadNetif>().Up();
|
|
SuccessOrQuit(joiners[0]->Get<Joiner>().Start(kPskd,
|
|
/* aProvisioningUrl */ nullptr,
|
|
/* aVendorName */ nullptr,
|
|
/* aVendorModel */ nullptr,
|
|
/* aVendorSwVersion */ nullptr,
|
|
/* aVendorData */ nullptr,
|
|
/* aCallback */ nullptr,
|
|
/* aContext */ nullptr));
|
|
|
|
joinerIids[0].SetFromExtAddress(joiners[0]->Get<Joiner>().GetId());
|
|
|
|
nexus.AdvanceTime(8 * Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `joiner` `RelayRx` are forwarded to all `enrollers` with `kForwardJoinerRelayRx` mode flag");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::InterfaceIdentifier readIid;
|
|
uint16_t joinerRouterRloc;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mRelayRxMsgs.GetHead());
|
|
|
|
if ((modes[i] & MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx) == 0)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(*message, readIid));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerRouterLocatorTlv>(*message, joinerRouterRloc));
|
|
|
|
VerifyOrQuit(readIid == joinerIids[0]);
|
|
VerifyOrQuit(joinerRouterRloc == admitter.Get<Mle::Mle>().GetRloc16());
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Stop `joiners[0]`");
|
|
|
|
joiners[0]->Get<Joiner>().Stop();
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerJoinerAccept` message from `enrollers[0]` to `admitter` accepting `joiners[0]`");
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerAccept);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[0]));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[0].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the accepted `joiners[0]` is tracked by `enrollers[0]` entry on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 0)
|
|
{
|
|
SuccessOrQuit(iter.GetNextJoinerInfo(joinerInfo));
|
|
VerifyOrQuit(AsCoreType(&joinerInfo.mIid) == joinerIids[0]);
|
|
LogJoiner(joinerInfo);
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start `joiners[0]` again and validate that its `RelayRx` are only forwarded to `enrollers[0]`");
|
|
|
|
joiners[0]->Get<ThreadNetif>().Up();
|
|
SuccessOrQuit(joiners[0]->Get<Joiner>().Start(kPskd,
|
|
/* aProvisioningUrl */ nullptr,
|
|
/* aVendorName */ nullptr,
|
|
/* aVendorModel */ nullptr,
|
|
/* aVendorSwVersion */ nullptr,
|
|
/* aVendorData */ nullptr,
|
|
/* aCallback */ nullptr,
|
|
/* aContext */ nullptr));
|
|
|
|
nexus.AdvanceTime(8 * Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::InterfaceIdentifier readIid;
|
|
uint16_t joinerRouterRloc;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mRelayRxMsgs.GetHead());
|
|
|
|
if (i != 0)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(*message, readIid));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerRouterLocatorTlv>(*message, joinerRouterRloc));
|
|
|
|
VerifyOrQuit(readIid == joinerIids[0]);
|
|
VerifyOrQuit(joinerRouterRloc == admitter.Get<Mle::Mle>().GetRloc16());
|
|
}
|
|
|
|
joiners[0]->Get<Joiner>().Stop();
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start `joiners[1]` and validate that its `RelayRx` are forwarded to all `enrollers`");
|
|
|
|
joiners[1]->Get<ThreadNetif>().Up();
|
|
SuccessOrQuit(joiners[1]->Get<Joiner>().Start(kPskd,
|
|
/* aProvisioningUrl */ nullptr,
|
|
/* aVendorName */ nullptr,
|
|
/* aVendorModel */ nullptr,
|
|
/* aVendorSwVersion */ nullptr,
|
|
/* aVendorData */ nullptr,
|
|
/* aCallback */ nullptr,
|
|
/* aContext */ nullptr));
|
|
|
|
joinerIids[1].SetFromExtAddress(joiners[1]->Get<Joiner>().GetId());
|
|
|
|
nexus.AdvanceTime(8 * Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::InterfaceIdentifier readIid;
|
|
uint16_t joinerRouterRloc;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mRelayRxMsgs.GetHead());
|
|
|
|
if ((modes[i] & MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx) == 0)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(*message, readIid));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerRouterLocatorTlv>(*message, joinerRouterRloc));
|
|
|
|
VerifyOrQuit(readIid == joinerIids[1]);
|
|
VerifyOrQuit(joinerRouterRloc == admitter.Get<Mle::Mle>().GetRloc16());
|
|
}
|
|
|
|
joiners[1]->Get<Joiner>().Stop();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send `EnrollerKeepAlive` message from `enrollers[0]` change its mode to disallow `kForwardJoinerRelayRx`");
|
|
|
|
modes[0] = MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, modes[0]));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[0].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the `enrollers[0]` mode got changed on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 0)
|
|
{
|
|
SuccessOrQuit(iter.GetNextJoinerInfo(joinerInfo));
|
|
VerifyOrQuit(AsCoreType(&joinerInfo.mIid) == joinerIids[0]);
|
|
LogJoiner(joinerInfo);
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start `joiners[0]` again and validate that its `RelayRx` is still only forwarded to `enrollers[0]`");
|
|
|
|
joiners[0]->Get<ThreadNetif>().Up();
|
|
SuccessOrQuit(joiners[0]->Get<Joiner>().Start(kPskd,
|
|
/* aProvisioningUrl */ nullptr,
|
|
/* aVendorName */ nullptr,
|
|
/* aVendorModel */ nullptr,
|
|
/* aVendorSwVersion */ nullptr,
|
|
/* aVendorData */ nullptr,
|
|
/* aCallback */ nullptr,
|
|
/* aContext */ nullptr));
|
|
|
|
nexus.AdvanceTime(8 * Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::InterfaceIdentifier readIid;
|
|
uint16_t joinerRouterRloc;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mRelayRxMsgs.GetHead());
|
|
|
|
if (i != 0)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(*message, readIid));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerRouterLocatorTlv>(*message, joinerRouterRloc));
|
|
|
|
VerifyOrQuit(readIid == joinerIids[0]);
|
|
VerifyOrQuit(joinerRouterRloc == admitter.Get<Mle::Mle>().GetRloc16());
|
|
}
|
|
|
|
joiners[0]->Get<Joiner>().Stop();
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start `joiners[1]` and validate that its `RelayRx` is not longer forwarded to `enrollers[0]`");
|
|
|
|
joiners[1]->Get<ThreadNetif>().Up();
|
|
SuccessOrQuit(joiners[1]->Get<Joiner>().Start(kPskd,
|
|
/* aProvisioningUrl */ nullptr,
|
|
/* aVendorName */ nullptr,
|
|
/* aVendorModel */ nullptr,
|
|
/* aVendorSwVersion */ nullptr,
|
|
/* aVendorData */ nullptr,
|
|
/* aCallback */ nullptr,
|
|
/* aContext */ nullptr));
|
|
|
|
joinerIids[1].SetFromExtAddress(joiners[1]->Get<Joiner>().GetId());
|
|
|
|
nexus.AdvanceTime(8 * Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::InterfaceIdentifier readIid;
|
|
uint16_t joinerRouterRloc;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mRelayRxMsgs.GetHead());
|
|
|
|
if ((modes[i] & MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx) == 0)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(*message, readIid));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerRouterLocatorTlv>(*message, joinerRouterRloc));
|
|
|
|
VerifyOrQuit(readIid == joinerIids[1]);
|
|
VerifyOrQuit(joinerRouterRloc == admitter.Get<Mle::Mle>().GetRloc16());
|
|
}
|
|
|
|
joiners[1]->Get<Joiner>().Stop();
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send `EnrollerKeepAlive` message from `enrollers[0]` to revert its mode back to allowing both Joiner and UDP");
|
|
|
|
modes[0] = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx | MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, modes[0]));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[0].mHasAdmitterState);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the `enrollers[0]` mode got changed on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 0)
|
|
{
|
|
SuccessOrQuit(iter.GetNextJoinerInfo(joinerInfo));
|
|
VerifyOrQuit(AsCoreType(&joinerInfo.mIid) == joinerIids[0]);
|
|
LogJoiner(joinerInfo);
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send `EnrollerKeepAlive` message from all `enrollers` to maintain the connection");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
message =
|
|
enrollers[i]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
|
|
responseContexts[i].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[i]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[i]));
|
|
}
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
VerifyOrQuit(responseContexts[i].mReceived);
|
|
VerifyOrQuit(responseContexts[i].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[i].mHasAdmitterState);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerJoinerAccept` message from `enrollers[0]` to `admitter` accepting `joiners[1]`");
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerAccept);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[1]));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[0].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that both accepted `joiners` are tracked by `enrollers[0]` on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 0)
|
|
{
|
|
uint16_t numJoiners = 0;
|
|
|
|
foundJoiners.Clear();
|
|
|
|
while (iter.GetNextJoinerInfo(joinerInfo) == kErrorNone)
|
|
{
|
|
LogJoiner(joinerInfo);
|
|
|
|
numJoiners++;
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
if (joinerIids[j] == AsCoreType(&joinerInfo.mIid))
|
|
{
|
|
VerifyOrQuit(!foundJoiners.Has(j));
|
|
foundJoiners.Add(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(numJoiners == 2);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start `joiners[1]` again and validate that its `RelayRx` are only forwarded to `enrollers[0]`");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
joiners[1]->Get<ThreadNetif>().Up();
|
|
SuccessOrQuit(joiners[1]->Get<Joiner>().Start(kPskd,
|
|
/* aProvisioningUrl */ nullptr,
|
|
/* aVendorName */ nullptr,
|
|
/* aVendorModel */ nullptr,
|
|
/* aVendorSwVersion */ nullptr,
|
|
/* aVendorData */ nullptr,
|
|
/* aCallback */ nullptr,
|
|
/* aContext */ nullptr));
|
|
|
|
nexus.AdvanceTime(8 * Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::InterfaceIdentifier readIid;
|
|
uint16_t joinerRouterRloc;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mRelayRxMsgs.GetHead());
|
|
|
|
if (i != 0)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(*message, readIid));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerRouterLocatorTlv>(*message, joinerRouterRloc));
|
|
|
|
VerifyOrQuit(readIid == joinerIids[1]);
|
|
VerifyOrQuit(joinerRouterRloc == admitter.Get<Mle::Mle>().GetRloc16());
|
|
}
|
|
|
|
joiners[1]->Get<Joiner>().Stop();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("From `enrollers[1]` send `EnrollerJoinerAccept` for `joiners[1]`");
|
|
|
|
message =
|
|
enrollers[1]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerAccept);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[1]));
|
|
|
|
responseContexts[1].Clear();
|
|
SuccessOrQuit(enrollers[1]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[1]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
Log("Validate that the request is rejected since `joiners[1]` is already accepted by `enrollers[0]`");
|
|
|
|
VerifyOrQuit(responseContexts[1].mReceived);
|
|
VerifyOrQuit(responseContexts[1].mResponseState == MeshCoP::StateTlv::kReject);
|
|
VerifyOrQuit(responseContexts[1].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate `joiners[1]` is still accepted by `enrollers[0]`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 0)
|
|
{
|
|
uint16_t numJoiners = 0;
|
|
|
|
foundJoiners.Clear();
|
|
|
|
while (iter.GetNextJoinerInfo(joinerInfo) == kErrorNone)
|
|
{
|
|
LogJoiner(joinerInfo);
|
|
|
|
numJoiners++;
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
if (joinerIids[j] == AsCoreType(&joinerInfo.mIid))
|
|
{
|
|
VerifyOrQuit(!foundJoiners.Has(j));
|
|
foundJoiners.Add(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(numJoiners == 2);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start `joiners[1]` again and validate that its `RelayRx` are only forwarded to `enrollers[0]`");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
joiners[1]->Get<ThreadNetif>().Up();
|
|
SuccessOrQuit(joiners[1]->Get<Joiner>().Start(kPskd,
|
|
/* aProvisioningUrl */ nullptr,
|
|
/* aVendorName */ nullptr,
|
|
/* aVendorModel */ nullptr,
|
|
/* aVendorSwVersion */ nullptr,
|
|
/* aVendorData */ nullptr,
|
|
/* aCallback */ nullptr,
|
|
/* aContext */ nullptr));
|
|
|
|
nexus.AdvanceTime(8 * Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::InterfaceIdentifier readIid;
|
|
uint16_t joinerRouterRloc;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mRelayRxMsgs.GetHead());
|
|
|
|
if (i != 0)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(*message, readIid));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerRouterLocatorTlv>(*message, joinerRouterRloc));
|
|
|
|
VerifyOrQuit(readIid == joinerIids[1]);
|
|
VerifyOrQuit(joinerRouterRloc == admitter.Get<Mle::Mle>().GetRloc16());
|
|
}
|
|
|
|
joiners[1]->Get<Joiner>().Stop();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerJoinerAccept` message again accepting `joiners[1]` from `enrollers[0]`");
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerAccept);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[1]));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[0].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that there is no change in the `enrollers` list and the tracked `joiners` on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 0)
|
|
{
|
|
uint16_t numJoiners = 0;
|
|
|
|
foundJoiners.Clear();
|
|
|
|
while (iter.GetNextJoinerInfo(joinerInfo) == kErrorNone)
|
|
{
|
|
LogJoiner(joinerInfo);
|
|
|
|
numJoiners++;
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
if (joinerIids[j] == AsCoreType(&joinerInfo.mIid))
|
|
{
|
|
VerifyOrQuit(!foundJoiners.Has(j));
|
|
foundJoiners.Add(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(numJoiners == 2);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerJoinerRelease` message from `enrollers[0]` to `admitter` releasing `joiners[0]`");
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerRelease);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[0]));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[0].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that the released `joiners[0]` is removed on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 0)
|
|
{
|
|
SuccessOrQuit(iter.GetNextJoinerInfo(joinerInfo));
|
|
VerifyOrQuit(AsCoreType(&joinerInfo.mIid) == joinerIids[1]);
|
|
LogJoiner(joinerInfo);
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerJoinerRelease` message again releasing `joiners[0]` from `enrollers[0]`");
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerRelease);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[0]));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
Log("Validate that `EnrollerJoinerRelease` is accepted, even though the given IID is already removed");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[0].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerJoinerRelease` message releasing `joiners[1]` from `enrollers[0]`");
|
|
|
|
message =
|
|
enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerRelease);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[1]));
|
|
|
|
responseContexts[0].Clear();
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[0]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[0].mReceived);
|
|
VerifyOrQuit(responseContexts[0].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[0].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send two `EnrollerJoinerAccept` messages from `enrollers[2]` accepting both `joiners`");
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
message = enrollers[2]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(
|
|
kUriEnrollerJoinerAccept);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[j]));
|
|
|
|
responseContexts[2].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[2]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[2]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[2].mReceived);
|
|
VerifyOrQuit(responseContexts[2].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[2].mHasAdmitterState);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that both accepted joiners are tracked by `enrollers[2]` on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 2)
|
|
{
|
|
uint16_t numJoiners = 0;
|
|
|
|
foundJoiners.Clear();
|
|
|
|
while (iter.GetNextJoinerInfo(joinerInfo) == kErrorNone)
|
|
{
|
|
LogJoiner(joinerInfo);
|
|
|
|
numJoiners++;
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
if (joinerIids[j] == AsCoreType(&joinerInfo.mIid))
|
|
{
|
|
VerifyOrQuit(!foundJoiners.Has(j));
|
|
foundJoiners.Add(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(numJoiners == 2);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerJoinerRelease` message from `enrollers[2]` with wildcard IID releasing all joiners");
|
|
|
|
message =
|
|
enrollers[2]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerRelease);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
wildcardJoinerIid.Clear();
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, wildcardJoinerIid));
|
|
|
|
responseContexts[2].Clear();
|
|
SuccessOrQuit(enrollers[2]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[2]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[2].mReceived);
|
|
VerifyOrQuit(responseContexts[2].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[2].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that all previously accepted joiners by `enrollers[2]` on `admitter` are now removed");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an invalid `EnrollerJoinerAccept` message from enrollers[2] with wildcard IID");
|
|
|
|
message =
|
|
enrollers[2]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerJoinerAccept);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
wildcardJoinerIid.Clear();
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, wildcardJoinerIid));
|
|
|
|
responseContexts[2].Clear();
|
|
SuccessOrQuit(enrollers[2]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[2]));
|
|
|
|
Log("Validate that the invalid `EnrollerJoinerAccept` is correctly rejected");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[2].mReceived);
|
|
VerifyOrQuit(responseContexts[2].mResponseState == MeshCoP::StateTlv::kReject);
|
|
VerifyOrQuit(responseContexts[2].mHasAdmitterState);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send two `EnrollerJoinerAccept` messages from `enrollers[2]` accepting both `joiners`");
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
message = enrollers[2]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(
|
|
kUriEnrollerJoinerAccept);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::JoinerIidTlv>(*message, joinerIids[j]));
|
|
|
|
responseContexts[2].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[2]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[2]));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(responseContexts[2].mReceived);
|
|
VerifyOrQuit(responseContexts[2].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[2].mHasAdmitterState);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that both accepted Joiners are tracked by `enrollers[2]` on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 2)
|
|
{
|
|
uint16_t numJoiners = 0;
|
|
|
|
foundJoiners.Clear();
|
|
|
|
while (iter.GetNextJoinerInfo(joinerInfo) == kErrorNone)
|
|
{
|
|
LogJoiner(joinerInfo);
|
|
|
|
numJoiners++;
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
if (joinerIids[j] == AsCoreType(&joinerInfo.mIid))
|
|
{
|
|
VerifyOrQuit(!foundJoiners.Has(j));
|
|
foundJoiners.Add(j);
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(joinerInfo.mMsecTillExpiration >= 6 * Time::kOneMinuteInMsec);
|
|
}
|
|
|
|
VerifyOrQuit(numJoiners == 2);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Wait for 4 minutes, sending `EnrollerKeepAlive` every 30 seconds to maintain enroller connections");
|
|
|
|
for (uint8_t interval = 0; interval < 4 * 2; interval++)
|
|
{
|
|
Log("Send `EnrollerKeepAlive` message from all `enrollers` to maintain the connection");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
message = enrollers[i]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(
|
|
kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
|
|
responseContexts[i].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[i]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[i]));
|
|
}
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
VerifyOrQuit(responseContexts[i].mReceived);
|
|
VerifyOrQuit(responseContexts[i].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[i].mHasAdmitterState);
|
|
}
|
|
|
|
nexus.AdvanceTime(29 * Time::kOneSecondInMsec);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the enroller list on `admitter` and that both joiners are still accepted by `enrollers[2]`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 2)
|
|
{
|
|
uint16_t numJoiners = 0;
|
|
|
|
foundJoiners.Clear();
|
|
|
|
while (iter.GetNextJoinerInfo(joinerInfo) == kErrorNone)
|
|
{
|
|
LogJoiner(joinerInfo);
|
|
|
|
numJoiners++;
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
if (joinerIids[j] == AsCoreType(&joinerInfo.mIid))
|
|
{
|
|
VerifyOrQuit(!foundJoiners.Has(j));
|
|
foundJoiners.Add(j);
|
|
}
|
|
}
|
|
|
|
// Since we waited for 4 minutes, the joiner expiration time should be closer
|
|
|
|
VerifyOrQuit(joinerInfo.mMsecTillExpiration < 6 * Time::kOneMinuteInMsec);
|
|
}
|
|
|
|
VerifyOrQuit(numJoiners == 2);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Start `joiners[1]` and validate that its RelayRx are only forwarded to `enrollers[2]`");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
recvContext[i].Clear();
|
|
}
|
|
|
|
joiners[1]->Get<ThreadNetif>().Up();
|
|
SuccessOrQuit(joiners[1]->Get<Joiner>().Start(kPskd,
|
|
/* aProvisioningUrl */ nullptr,
|
|
/* aVendorName */ nullptr,
|
|
/* aVendorModel */ nullptr,
|
|
/* aVendorSwVersion */ nullptr,
|
|
/* aVendorData */ nullptr,
|
|
/* aCallback */ nullptr,
|
|
/* aContext */ nullptr));
|
|
|
|
nexus.AdvanceTime(8 * Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::InterfaceIdentifier readIid;
|
|
uint16_t joinerRouterRloc;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mRelayRxMsgs.GetHead());
|
|
|
|
if (i != 2)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(*message, readIid));
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerRouterLocatorTlv>(*message, joinerRouterRloc));
|
|
|
|
VerifyOrQuit(readIid == joinerIids[1]);
|
|
VerifyOrQuit(joinerRouterRloc == admitter.Get<Mle::Mle>().GetRloc16());
|
|
}
|
|
|
|
joiners[1]->Get<Joiner>().Stop();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate expiration is extended for joiners[1] after it transmitted");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 2)
|
|
{
|
|
uint16_t numJoiners = 0;
|
|
|
|
foundJoiners.Clear();
|
|
|
|
while (iter.GetNextJoinerInfo(joinerInfo) == kErrorNone)
|
|
{
|
|
LogJoiner(joinerInfo);
|
|
|
|
numJoiners++;
|
|
|
|
for (uint8_t j = 0; j < 2; j++)
|
|
{
|
|
if (joinerIids[j] == AsCoreType(&joinerInfo.mIid))
|
|
{
|
|
VerifyOrQuit(!foundJoiners.Has(j));
|
|
foundJoiners.Add(j);
|
|
|
|
// `joiners[0]` expiration should still tick down, while `joiners[1]`'s should be extended
|
|
|
|
if (j == 0)
|
|
{
|
|
VerifyOrQuit(joinerInfo.mMsecTillExpiration < 6 * Time::kOneMinuteInMsec);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(joinerInfo.mMsecTillExpiration >= 6 * Time::kOneMinuteInMsec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(numJoiners == 2);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Wait for another 4 minutes, sending `EnrollerKeepAlive` every 30 seconds");
|
|
|
|
for (uint8_t interval = 0; interval < 4 * 2; interval++)
|
|
{
|
|
Log("Send `EnrollerKeepAlive` message from all `enrollers` to maintain the connection");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
message = enrollers[i]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(
|
|
kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
|
|
responseContexts[i].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[i]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[i]));
|
|
}
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
VerifyOrQuit(responseContexts[i].mReceived);
|
|
VerifyOrQuit(responseContexts[i].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[i].mHasAdmitterState);
|
|
}
|
|
|
|
nexus.AdvanceTime(29 * Time::kOneSecondInMsec);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `joiners[0]` is timed out and removed on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
if (matchedIndex == 2)
|
|
{
|
|
SuccessOrQuit(iter.GetNextJoinerInfo(joinerInfo));
|
|
|
|
LogJoiner(joinerInfo);
|
|
|
|
VerifyOrQuit(joinerIids[1] == AsCoreType(&joinerInfo.mIid));
|
|
VerifyOrQuit(joinerInfo.mMsecTillExpiration > 0);
|
|
}
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Wait for another 4 minutes, sending `EnrollerKeepAlive` every 30 seconds");
|
|
|
|
for (uint8_t interval = 0; interval < 4 * 2; interval++)
|
|
{
|
|
Log("Send `EnrollerKeepAlive` message from all `enrollers` to maintain the connection");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
message = enrollers[i]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(
|
|
kUriEnrollerKeepAlive);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
|
|
|
|
responseContexts[i].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[i]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[i]));
|
|
}
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
VerifyOrQuit(responseContexts[i].mReceived);
|
|
VerifyOrQuit(responseContexts[i].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[i].mHasAdmitterState);
|
|
}
|
|
|
|
nexus.AdvanceTime(29 * Time::kOneSecondInMsec);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `joiners[1]` is also timed out and removed on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
}
|
|
|
|
void TestBorderAdmitterForwardingUdpProxy(void)
|
|
{
|
|
static constexpr uint8_t kNumEnrollers = 4;
|
|
|
|
static const char *kEnrollerIds[kNumEnrollers] = {"1", "2", "3", "4"};
|
|
|
|
static const uint8_t kDiagTlvs[] = {NetDiag::Tlv::kExtMacAddress, NetDiag::Tlv::kVersion};
|
|
|
|
Core nexus;
|
|
Node &admitter = nexus.CreateNode();
|
|
Node *enrollers[kNumEnrollers];
|
|
Ip6::SockAddr sockAddr;
|
|
Pskc pskc;
|
|
Coap::Message *message;
|
|
Coap::Message *diagMessage;
|
|
uint8_t modes[kNumEnrollers];
|
|
ResponseContext responseContexts[kNumEnrollers];
|
|
ReceiveContext recvContext[kNumEnrollers];
|
|
MeshCoP::SteeringData steeringData;
|
|
MeshCoP::SteeringData leaderSteeringData;
|
|
Admitter::Iterator iter;
|
|
Admitter::EnrollerInfo enrollerInfo;
|
|
Admitter::JoinerInfo joinerInfo;
|
|
BitSet<kNumEnrollers> foundEnrollers;
|
|
uint16_t sessionId;
|
|
uint16_t rloc16;
|
|
MeshCoP::UdpEncapsulationTlvHeader udpEncapHeader;
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAdmitterForwardingUdpProxy");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
enrollers[i] = &nexus.CreateNode();
|
|
}
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// Form the topology:
|
|
// - `admitter` forms the network (as leader)
|
|
// - All enrollers stay disconnected.
|
|
|
|
admitter.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Mle::Mle>().IsLeader());
|
|
|
|
for (Node *enroller : enrollers)
|
|
{
|
|
SuccessOrQuit(enroller->Get<Mac::Mac>().SetPanChannel(admitter.Get<Mac::Mac>().GetPanChannel()));
|
|
enroller->Get<Mac::Mac>().SetPanId(admitter.Get<Mac::Mac>().GetPanId());
|
|
enroller->Get<ThreadNetif>().Up();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Border Admitter on `admitter`");
|
|
|
|
admitter.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<Ip6::Filter>().AddUnsecurePort(admitter.Get<Manager>().GetUdpPort()));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Establish a DTLS connection from all `enrollers` to `admitter`");
|
|
|
|
sockAddr.SetAddress(admitter.Get<Mle::Mle>().GetLinkLocalAddress());
|
|
sockAddr.SetPort(admitter.Get<Manager>().GetUdpPort());
|
|
|
|
admitter.Get<KeyManager>().GetPskc(pskc);
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Node *enroller = enrollers[i];
|
|
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
|
|
|
recvContext[i].Clear();
|
|
enroller->Get<Tmf::SecureAgent>().RegisterResourceHandler(HandleResource, &recvContext[i]);
|
|
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().Open(0));
|
|
SuccessOrQuit(enroller->Get<Tmf::SecureAgent>().Connect(sockAddr));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(enroller->Get<Tmf::SecureAgent>().IsConnected());
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Prepare mode for each enroller");
|
|
|
|
modes[0] = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx | MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
modes[1] = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx | MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
modes[2] = MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx;
|
|
modes[3] = MeshCoP::EnrollerModeTlv::kForwardJoinerRelayRx;
|
|
|
|
steeringData.SetToPermitAllJoiners();
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Send an `EnrollerRegister` message from all `enrollers`");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
message =
|
|
enrollers[i]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriEnrollerRegister);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerIdTlv>(*message, kEnrollerIds[i]));
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::EnrollerModeTlv>(*message, modes[i]));
|
|
SuccessOrQuit(MeshCoP::SteeringDataTlv::AppendTo(*message, steeringData));
|
|
|
|
responseContexts[i].Clear();
|
|
SuccessOrQuit(
|
|
enrollers[i]->Get<Tmf::SecureAgent>().SendMessage(*message, HandleResponse, &responseContexts[i]));
|
|
}
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check that all registrations were accepted");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
VerifyOrQuit(responseContexts[i].mReceived);
|
|
VerifyOrQuit(responseContexts[i].mResponseState == MeshCoP::StateTlv::kAccept);
|
|
VerifyOrQuit(responseContexts[i].mHasAdmitterState);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `admitter` becomes active commissioner");
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(admitter.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16));
|
|
VerifyOrQuit(rloc16 == admitter.Get<Mle::Mle>().GetRloc16());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
|
|
VerifyOrQuit(sessionId == admitter.Get<Admitter>().GetCommissionerSessionId());
|
|
|
|
SuccessOrQuit(admitter.Get<NetworkData::Leader>().FindSteeringData(leaderSteeringData));
|
|
VerifyOrQuit(leaderSteeringData == steeringData);
|
|
VerifyOrQuit(leaderSteeringData.PermitsAllJoiners());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the Enroller info on `admitter`");
|
|
|
|
foundEnrollers.Clear();
|
|
iter.Init(admitter.GetInstance());
|
|
|
|
while (iter.GetNextEnrollerInfo(enrollerInfo) == kErrorNone)
|
|
{
|
|
uint8_t matchedIndex;
|
|
|
|
LogEnroller(enrollerInfo);
|
|
|
|
matchedIndex = FindMatchingEnroller<kNumEnrollers>(enrollerInfo, kEnrollerIds, foundEnrollers);
|
|
|
|
VerifyOrQuit(AsCoreType(&enrollerInfo.mSteeringData) == steeringData);
|
|
VerifyOrQuit(enrollerInfo.mMode == modes[matchedIndex]);
|
|
|
|
VerifyOrQuit(iter.GetNextJoinerInfo(joinerInfo) == kErrorNotFound);
|
|
}
|
|
|
|
VerifyOrQuit(DidFindAllEnrollers<kNumEnrollers>(foundEnrollers));
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Prepare a `DiagnosticGetQuery` message");
|
|
|
|
diagMessage = enrollers[0]->Get<Tmf::Agent>().AllocateAndInitNonConfirmablePostMessage(kUriDiagnosticGetQuery);
|
|
VerifyOrQuit(diagMessage != nullptr);
|
|
SuccessOrQuit(Tlv::Append<NetDiag::TypeListTlv>(*diagMessage, kDiagTlvs, sizeof(kDiagTlvs)));
|
|
diagMessage->WriteMessageId(0);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Embed the `DiagnosticGetQuery` into `ProxyTx` message and send it from `enrollers[0]`");
|
|
|
|
message = enrollers[0]->Get<Tmf::SecureAgent>().AllocateAndInitPriorityNonConfirmablePostMessage(kUriProxyTx);
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
udpEncapHeader.SetSourcePort(Tmf::kUdpPort);
|
|
udpEncapHeader.SetDestinationPort(Tmf::kUdpPort);
|
|
|
|
SuccessOrQuit(Tlv::AppendTlvHeader(*message, MeshCoP::Tlv::kUdpEncapsulation,
|
|
sizeof(udpEncapHeader) + diagMessage->GetLength()));
|
|
SuccessOrQuit(message->Append(udpEncapHeader));
|
|
SuccessOrQuit(message->AppendBytesFromMessage(*diagMessage, 0, diagMessage->GetLength()));
|
|
diagMessage->Free();
|
|
|
|
SuccessOrQuit(Tlv::Append<MeshCoP::Ip6AddressTlv>(*message, admitter.Get<Mle::Mle>().GetMeshLocalRloc()));
|
|
|
|
SuccessOrQuit(enrollers[0]->Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate that `admitter` receives the `DiagnosticGetQuery` response");
|
|
Log("And that it forwards it as `ProxyRx` messages to all `enrollers` with `kForwardUdpProxyRx` mode flag");
|
|
|
|
for (uint8_t i = 0; i < kNumEnrollers; i++)
|
|
{
|
|
Ip6::Address senderAddr;
|
|
OffsetRange offsetRange;
|
|
|
|
message = AsCoapMessagePtr(recvContext[i].mProxyRxMsgs.GetHead());
|
|
|
|
if ((modes[i] & MeshCoP::EnrollerModeTlv::kForwardUdpProxyRx) == 0)
|
|
{
|
|
VerifyOrQuit(message == nullptr);
|
|
Log(" Enroller %s does not set `kForwardUdpProxyRx` mode - so did not get `ProxyRx`", kEnrollerIds[i]);
|
|
continue;
|
|
}
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
VerifyOrQuit(message->ReadType() == Coap::kTypeNonConfirmable);
|
|
VerifyOrQuit(message->ReadCode() == Coap::kCodePost);
|
|
|
|
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, MeshCoP::Tlv::kUdpEncapsulation, offsetRange));
|
|
|
|
SuccessOrQuit(Tlv::Find<MeshCoP::Ip6AddressTlv>(*message, senderAddr));
|
|
VerifyOrQuit(senderAddr == admitter.Get<Mle::Mle>().GetMeshLocalRloc());
|
|
|
|
Log(" Enroller %s received `ProxyRx` from %s", kEnrollerIds[i], senderAddr.ToString().AsCString());
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
static constexpr uint32_t kInfraIfIndex = 1;
|
|
|
|
void ValidateAdmitterMdnsService(Node &aNode)
|
|
{
|
|
static const char kDefaultServiceBaseName[] = OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME;
|
|
|
|
Dns::Multicast::Core::Iterator *iterator;
|
|
Dns::Multicast::Core::Service service;
|
|
Dns::Multicast::Core::EntryState entryState;
|
|
bool found = false;
|
|
|
|
iterator = aNode.Get<Dns::Multicast::Core>().AllocateIterator();
|
|
VerifyOrQuit(iterator != nullptr);
|
|
|
|
while (aNode.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"))
|
|
{
|
|
for (uint16_t i = 0; i < service.mSubTypeLabelsLength; i++)
|
|
{
|
|
Log(" SubType: %s", service.mSubTypeLabels[i]);
|
|
}
|
|
|
|
Log(" Port: %u", service.mPort);
|
|
Log(" TTL: %lu", ToUlong(service.mTtl));
|
|
|
|
VerifyOrQuit(StringStartsWith(service.mServiceInstance, kDefaultServiceBaseName));
|
|
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
|
VerifyOrQuit(service.mPort == aNode.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
|
VerifyOrQuit(service.mTtl > 0);
|
|
VerifyOrQuit(service.mInfraIfIndex == 1);
|
|
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
|
|
|
if (aNode.Get<Admitter>().IsPrimeAdmitter())
|
|
{
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 1);
|
|
VerifyOrQuit(StringMatch(service.mSubTypeLabels[0], "_admitter"));
|
|
}
|
|
else
|
|
{
|
|
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
|
}
|
|
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
VerifyOrQuit(found);
|
|
|
|
aNode.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
|
|
void TestBorderAdmitterDnssdService(void)
|
|
{
|
|
Core nexus;
|
|
Node &node1 = nexus.CreateNode();
|
|
Node &node2 = nexus.CreateNode();
|
|
|
|
Log("------------------------------------------------------------------------------------------------------");
|
|
Log("TestBorderAdmitterDnssdService");
|
|
|
|
nexus.AdvanceTime(0);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Enable mDNS
|
|
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
|
VerifyOrQuit(node1.Get<Dns::Multicast::Core>().IsEnabled());
|
|
SuccessOrQuit(node2.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
|
VerifyOrQuit(node2.Get<Dns::Multicast::Core>().IsEnabled());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Form the topology.
|
|
|
|
node1.Form();
|
|
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
|
node2.Join(node1);
|
|
|
|
nexus.AdvanceTime(10 * Time::kOneMinuteInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Mle::Mle>().IsLeader());
|
|
VerifyOrQuit(node2.Get<Mle::Mle>().IsRouter());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Check Border Admitter initial state");
|
|
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!node2.Get<Admitter>().IsEnabled());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Admitter role on `node1` and validate that it becomes the Prime Admitter");
|
|
|
|
node1.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(node1.Get<Admitter>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the registered mDNS MeshCop service by `node1` including `_admitter` sub-type");
|
|
|
|
ValidateAdmitterMdnsService(node1);
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the registered mDNS MeshCop service by `node2` (should not have `_admitter` sub-type)");
|
|
|
|
ValidateAdmitterMdnsService(node2);
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Enable Admitter role on `node2` and validate that `node1` remains the Prime Admitter");
|
|
|
|
node2.Get<Admitter>().SetEnabled(true);
|
|
VerifyOrQuit(node2.Get<Admitter>().IsEnabled());
|
|
|
|
nexus.AdvanceTime(45 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node1.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsActiveCommissioner());
|
|
VerifyOrQuit(!node2.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the registered mDNS MeshCop service by `node1` including `_admitter` sub-type");
|
|
|
|
ValidateAdmitterMdnsService(node1);
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the registered mDNS MeshCop service by `node2` (should not have `_admitter` sub-type)");
|
|
ValidateAdmitterMdnsService(node2);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Disable Admitter role on `node1` and check that `node2` becomes the Prime Admitter");
|
|
|
|
node1.Get<Admitter>().SetEnabled(false);
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsEnabled());
|
|
VerifyOrQuit(!node1.Get<Admitter>().IsPrimeAdmitter());
|
|
|
|
nexus.AdvanceTime(75 * Time::kOneSecondInMsec);
|
|
|
|
VerifyOrQuit(node2.Get<Admitter>().IsPrimeAdmitter());
|
|
VerifyOrQuit(!node2.Get<Admitter>().IsActiveCommissioner());
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the registered mDNS MeshCop service by `node1` (no longer publishing `_admitter` sub-type)");
|
|
|
|
ValidateAdmitterMdnsService(node1);
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Log("Validate the registered mDNS MeshCop service by `node2` (now should include `_admitter` sub-type)");
|
|
ValidateAdmitterMdnsService(node2);
|
|
}
|
|
|
|
} // namespace Nexus
|
|
} // namespace ot
|
|
|
|
int main(void)
|
|
{
|
|
ot::Nexus::TestBorderAdmitterPrimeSelection();
|
|
ot::Nexus::TestBorderAdmitterEnrollerInteraction();
|
|
ot::Nexus::TestBorderAdmitterCommissionerConflictAndPetitionerRetry();
|
|
ot::Nexus::TestBorderAdmitterMultipleEnrollers();
|
|
ot::Nexus::TestBorderAdmitterJoinerEnrollerInteraction();
|
|
ot::Nexus::TestBorderAdmitterForwardingUdpProxy();
|
|
ot::Nexus::TestBorderAdmitterDnssdService();
|
|
|
|
printf("\nAll tests passed\n");
|
|
return 0;
|
|
}
|