[border-agent] support multiple parallel sessions (#11212)

This commit updates the `BorderAgent` implementation to support
multiple parallel sessions using PSKc. It adds new public OT APIs to
iterate over all sessions, along with related CLI commands. The nexus
`test_border_agent` is updated to cover multi-session behavior and
the new session iteration APIs.
This commit is contained in:
Abtin Keshavarzian
2025-02-12 11:04:13 -08:00
committed by GitHub
parent 1b9b73f2b7
commit 15a789267d
9 changed files with 527 additions and 161 deletions
+56 -4
View File
@@ -36,6 +36,7 @@
#define OPENTHREAD_BORDER_AGENT_H_
#include <openthread/instance.h>
#include <openthread/ip6.h>
#ifdef __cplusplus
extern "C" {
@@ -89,13 +90,32 @@ typedef struct otBorderAgentCounters
} otBorderAgentCounters;
/**
* Gets the counters of the Thread Border Agent.
* Represents information about a Border Agent session.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* This structure is populated by `otBorderAgentGetNextSessionInfo()` during iteration over the list of sessions using
* an `otBorderAgentSessionIterator`.
*
* @returns A pointer to the Border Agent counters.
* To ensure consistent `mLifetime` calculations, the iterator's initialization time is stored within the iterator,
* and each session's `mLifetime` is calculated relative to this time.
*/
const otBorderAgentCounters *otBorderAgentGetCounters(otInstance *aInstance);
typedef struct otBorderAgentSessionInfo
{
otSockAddr mPeerSockAddr; ///< Socket address (IPv6 address and port number) of session peer.
bool mIsConnected; ///< Indicates whether the session is connected.
bool mIsCommissioner; ///< Indicates whether the session is accepted as full commissioner.
uint64_t mLifetime; ///< Milliseconds since the session was first established.
} otBorderAgentSessionInfo;
/**
* Represents an iterator for Border Agent sessions.
*
* The caller MUST NOT access or update the fields in this struct. It is intended for OpenThread internal use only.
*/
typedef struct otBorderAgentSessionIterator
{
void *mPtr;
uint64_t mData;
} otBorderAgentSessionIterator;
/**
* Indicates whether or not the Border Agent service is active and running.
@@ -161,6 +181,38 @@ otError otBorderAgentGetId(otInstance *aInstance, otBorderAgentId *aId);
*/
otError otBorderAgentSetId(otInstance *aInstance, const otBorderAgentId *aId);
/**
* Initializes a session iterator.
*
* An iterator MUST be initialized before being used in `otBorderAgentGetNextSessionInfo()`. A previously initialized
* iterator can be re-initialized to start from the beginning of the session list.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aIterator The iterator to initialize.
*/
void otBorderAgentInitSessionIterator(otInstance *aInstance, otBorderAgentSessionIterator *aIterator);
/**
* Retrieves the next Border Agent session information.
*
* @param[in] aIterator The iterator to use.
* @param[out] aSessionInfo A pointer to an `otBorderAgentSessionInfo` to populate.
*
* @retval OT_ERROR_NONE Successfully retrieved the next session info.
* @retval OT_ERROR_NOT_FOUND No more sessions are available. The end of the list has been reached.
*/
otError otBorderAgentGetNextSessionInfo(otBorderAgentSessionIterator *aIterator,
otBorderAgentSessionInfo *aSessionInfo);
/**
* Gets the counters of the Thread Border Agent.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns A pointer to the Border Agent counters.
*/
const otBorderAgentCounters *otBorderAgentGetCounters(otInstance *aInstance);
/*--------------------------------------------------------------------------------------------------------------------
* Border Agent Ephemeral Key feature */
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (475)
#define OPENTHREAD_API_VERSION (476)
/**
* @addtogroup api-instance
+15
View File
@@ -387,6 +387,21 @@ Active
Done
```
### ba sessions
Prints the list of Border Agent's sessions. Information per session:
- Peer socket address (IPv6 address and port).
- Whether or not the session is connected.
- Whether or not the session is accepted as full commissioner.
- Session lifetime in milliseconds (calculated from the time the session was first established).
```bash
ba sessions
[fe80:0:0:0:cc79:2a29:d311:1aea]:9202 connected:yes commissioner:no lifetime:1860
Done
```
### ba ephemeralkey
Print the Border Agent's Ephemeral Key Manager state.
+31
View File
@@ -475,6 +475,37 @@ template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
{
OutputLine("%s", otBorderAgentIsActive(GetInstancePtr()) ? "Active" : "Inactive");
}
/**
* @cli ba sessions
* @code
* ba sessions
* [fe80:0:0:0:cc79:2a29:d311:1aea]:9202 connected:yes commissioner:no lifetime:1860
* Done
* @endcode
* @par
* Prints the list of Border Agent's sessions. Information per session:
* * Peer socket address (IPv6 address and port).
* * Whether or not the session is connected.
* * Whether or not the session is accepted as full commissioner.
* * Session lifetime in milliseconds (calculated from the time the session was first established).
*/
else if (aArgs[0] == "sessions")
{
otBorderAgentSessionIterator iterator;
otBorderAgentSessionInfo info;
char sockAddrString[OT_IP6_SOCK_ADDR_STRING_SIZE];
Uint64StringBuffer lifetimeString;
otBorderAgentInitSessionIterator(GetInstancePtr(), &iterator);
while (otBorderAgentGetNextSessionInfo(&iterator, &info) == OT_ERROR_NONE)
{
otIp6SockAddrToString(&info.mPeerSockAddr, sockAddrString, sizeof(sockAddrString));
OutputLine("%s connected:%s commissioner:%s lifetime:%s", sockAddrString, info.mIsConnected ? "yes" : "no",
info.mIsCommissioner ? "yes" : "no", Uint64ToString(info.mLifetime, lifetimeString));
}
}
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
/**
* @cli ba id (get,set)
+13 -1
View File
@@ -56,7 +56,7 @@ otError otBorderAgentSetId(otInstance *aInstance, const otBorderAgentId *aId)
bool otBorderAgentIsActive(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().IsActive();
return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().IsRunning();
}
uint16_t otBorderAgentGetUdpPort(otInstance *aInstance)
@@ -64,6 +64,18 @@ uint16_t otBorderAgentGetUdpPort(otInstance *aInstance)
return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().GetUdpPort();
}
void otBorderAgentInitSessionIterator(otInstance *aInstance, otBorderAgentSessionIterator *aIterator)
{
AsCoreType(aIterator).Init(AsCoreType(aInstance));
}
otError otBorderAgentGetNextSessionInfo(otBorderAgentSessionIterator *aIterator, otBorderAgentSessionInfo *aSessionInfo)
{
AssertPointerIsNotNull(aSessionInfo);
return AsCoreType(aIterator).GetNextSessionInfo(*aSessionInfo);
}
const otBorderAgentCounters *otBorderAgentGetCounters(otInstance *aInstance)
{
return &AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().GetCounters();
+73 -45
View File
@@ -47,9 +47,8 @@ RegisterLogModule("BorderAgent");
BorderAgent::BorderAgent(Instance &aInstance)
: InstanceLocator(aInstance)
, mState(kStateStopped)
, mIsRunning(false)
, mDtlsTransport(aInstance, kNoLinkSecurity)
, mCoapDtlsSession(nullptr)
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
, mIdInitialized(false)
#endif
@@ -97,48 +96,43 @@ exit:
}
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
Error BorderAgent::Start(uint16_t aUdpPort)
{
Error error;
Pskc pskc;
Get<KeyManager>().GetPskc(pskc);
error = Start(aUdpPort, pskc.m8, Pskc::kSize);
pskc.Clear();
return error;
}
Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength)
void BorderAgent::Start(void)
{
Error error = kErrorNone;
Pskc pskc;
VerifyOrExit(mState == kStateStopped);
VerifyOrExit(!mIsRunning);
mDtlsTransport.SetAcceptCallback(BorderAgent::HandleAcceptSession, this);
mDtlsTransport.SetRemoveSessionCallback(BorderAgent::HandleRemoveSession, this);
SuccessOrExit(error = mDtlsTransport.Open());
SuccessOrExit(error = mDtlsTransport.Bind(aUdpPort));
SuccessOrExit(error = mDtlsTransport.Bind(kUdpPort));
SuccessOrExit(error = mDtlsTransport.SetPsk(aPsk, aPskLength));
Get<KeyManager>().GetPskc(pskc);
SuccessOrExit(error = mDtlsTransport.SetPsk(pskc.m8, Pskc::kSize));
pskc.Clear();
mState = kStateStarted;
mIsRunning = true;
LogInfo("Border Agent start listening on port %u", GetUdpPort());
exit:
if (!mIsRunning)
{
mDtlsTransport.Close();
}
LogWarnOnError(error, "start agent");
return error;
}
void BorderAgent::Stop(void)
{
VerifyOrExit(mState != kStateStopped);
VerifyOrExit(mIsRunning);
mDtlsTransport.Close();
mIsRunning = false;
mState = kStateStopped;
LogInfo("Border Agent stopped");
exit:
@@ -165,7 +159,7 @@ void BorderAgent::HandleNotifierEvents(Events aEvents)
{
Pskc pskc;
VerifyOrExit(mState != kStateStopped);
VerifyOrExit(mIsRunning);
Get<KeyManager>().GetPskc(pskc);
@@ -188,17 +182,7 @@ SecureSession *BorderAgent::HandleAcceptSession(void *aContext, const Ip6::Messa
BorderAgent::CoapDtlsSession *BorderAgent::HandleAcceptSession(void)
{
CoapDtlsSession *session = nullptr;
VerifyOrExit(mCoapDtlsSession == nullptr);
session = CoapDtlsSession::Allocate(GetInstance(), mDtlsTransport);
VerifyOrExit(session != nullptr);
mCoapDtlsSession = session;
exit:
return session;
return CoapDtlsSession::Allocate(GetInstance(), mDtlsTransport);
}
void BorderAgent::HandleRemoveSession(void *aContext, SecureSession &aSession)
@@ -212,7 +196,6 @@ void BorderAgent::HandleRemoveSession(SecureSession &aSession)
coapSession.Cleanup();
coapSession.Free();
mCoapDtlsSession = nullptr;
}
void BorderAgent::HandleSessionConnected(CoapDtlsSession &aSession)
@@ -227,7 +210,6 @@ void BorderAgent::HandleSessionConnected(CoapDtlsSession &aSession)
else
#endif
{
mState = kStateConnected;
mCounters.mPskcSecureSessionSuccesses++;
}
}
@@ -244,8 +226,6 @@ void BorderAgent::HandleSessionDisconnected(CoapDtlsSession &aSession, CoapDtlsS
else
#endif
{
mState = kStateStarted;
if (aEvent == CoapDtlsSession::kDisconnectedError)
{
mCounters.mPskcSecureSessionFailures++;
@@ -265,11 +245,28 @@ void BorderAgent::HandleCommissionerPetitionAccepted(CoapDtlsSession &aSession)
else
#endif
{
mState = kStateAccepted;
mCounters.mPskcCommissionerPetitions++;
}
}
BorderAgent::CoapDtlsSession *BorderAgent::FindActiveCommissionerSession(void)
{
CoapDtlsSession *commissionerSession = nullptr;
for (SecureSession &session : mDtlsTransport.GetSessions())
{
CoapDtlsSession &coapSession = static_cast<CoapDtlsSession &>(session);
if (coapSession.IsActiveCommissioner())
{
commissionerSession = &coapSession;
break;
}
}
return commissionerSession;
}
Coap::Message::Code BorderAgent::CoapCodeFromError(Error aError)
{
Coap::Message::Code code;
@@ -298,25 +295,55 @@ template <> void BorderAgent::HandleTmf<kUriRelayRx>(Coap::Message &aMessage, co
OT_UNUSED_VARIABLE(aMessageInfo);
Coap::Message *message = nullptr;
Error error = kErrorNone;
Coap::Message *message = nullptr;
Error error = kErrorNone;
CoapDtlsSession *session;
VerifyOrExit(mState != kStateStopped);
VerifyOrExit(mIsRunning);
VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop);
VerifyOrExit(mCoapDtlsSession != nullptr);
session = FindActiveCommissionerSession();
VerifyOrExit(session != nullptr);
message = mCoapDtlsSession->NewPriorityNonConfirmablePostMessage(kUriRelayRx);
message = session->NewPriorityNonConfirmablePostMessage(kUriRelayRx);
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = mCoapDtlsSession->ForwardToCommissioner(*message, aMessage));
SuccessOrExit(error = session->ForwardToCommissioner(*message, aMessage));
LogInfo("Sent to commissioner on RelayRx (c/rx)");
exit:
FreeMessageOnError(message, error);
}
//----------------------------------------------------------------------------------------------------------------------
// BorderAgent::SessionIterator
void BorderAgent::SessionIterator::Init(Instance &aInstance)
{
SetSession(static_cast<CoapDtlsSession *>(aInstance.Get<BorderAgent>().mDtlsTransport.GetSessions().GetHead()));
SetInitTime(aInstance.Get<Uptime>().GetUptime());
}
Error BorderAgent::SessionIterator::GetNextSessionInfo(SessionInfo &aSessionInfo)
{
Error error = kErrorNone;
CoapDtlsSession *session = GetSession();
VerifyOrExit(session != nullptr, error = kErrorNotFound);
SetSession(static_cast<CoapDtlsSession *>(session->GetNext()));
aSessionInfo.mPeerSockAddr.mAddress = session->GetMessageInfo().GetPeerAddr();
aSessionInfo.mPeerSockAddr.mPort = session->GetMessageInfo().GetPeerPort();
aSessionInfo.mIsConnected = session->IsConnected();
aSessionInfo.mIsCommissioner = session->IsActiveCommissioner();
aSessionInfo.mLifetime = GetInitTime() - session->GetAllocationTime();
exit:
return error;
}
//----------------------------------------------------------------------------------------------------------------------
// BorderAgent::EphemeralKeyManager
@@ -614,6 +641,7 @@ BorderAgent::CoapDtlsSession::CoapDtlsSession(Instance &aInstance, Dtls::Transpo
, mIsActiveCommissioner(false)
, mTimer(aInstance, HandleTimer, this)
, mUdpReceiver(HandleUdpReceive, this)
, mAllocationTime(aInstance.Get<Uptime>().GetUptime())
{
mCommissionerAloc.InitAsThreadOriginMeshLocal();
+67 -30
View File
@@ -50,6 +50,7 @@
#include "common/tasklet.hpp"
#include "meshcop/dataset.hpp"
#include "meshcop/secure_transport.hpp"
#include "net/socket.hpp"
#include "net/udp6.hpp"
#include "thread/tmf.hpp"
#include "thread/uri_paths.hpp"
@@ -62,6 +63,10 @@ namespace MeshCoP {
#error "Border Agent feature requires `OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE`"
#endif
#if !OPENTHREAD_CONFIG_UPTIME_ENABLE
#error "Border Agent feature requires `OPENTHREAD_CONFIG_UPTIME_ENABLE`"
#endif
class BorderAgent : public InstanceLocator, private NonCopyable
{
friend class ot::Notifier;
@@ -70,16 +75,38 @@ class BorderAgent : public InstanceLocator, private NonCopyable
class CoapDtlsSession;
public:
typedef otBorderAgentId Id; ///< Border Agent ID.
typedef otBorderAgentCounters Counters; ///< Border Agent Counters.
typedef otBorderAgentId Id; ///< Border Agent ID.
typedef otBorderAgentCounters Counters; ///< Border Agent Counters.
typedef otBorderAgentSessionInfo SessionInfo; ///< A session info.
enum State : uint8_t
/**
* Represents an iterator for secure sessions.
*/
class SessionIterator : public otBorderAgentSessionIterator
{
kStateDisabled = OT_BORDER_AGENT_STATE_DISABLED, ///< Ephemeral key feature is disabled.
kStateStopped = OT_BORDER_AGENT_STATE_STOPPED, ///< Enabled, but the ephemeral key is not set and started.
kStateStarted = OT_BORDER_AGENT_STATE_STARTED, ///< Ephemeral key is set and listening to accept connection
kStateConnected = OT_BORDER_AGENT_STATE_CONNECTED, ///< Session connected with candidate, not full commissioner
kStateAccepted = OT_BORDER_AGENT_STATE_ACCEPTED, ///< Session connected and accepted as full commissioner.
public:
/**
* Initializes the `SessionIterator`.
*
* @param[in] aInstance The OpenThread instance.
*/
void Init(Instance &aInstance);
/**
* Retrieves the next session information.
*
* @param[out] aSessionInfo A `SessionInfo` to populate.
*
* @retval kErrorNone Successfully retrieved the next session. @p aSessionInfo is updated.
* @retval kErrorNotFound No more sessions are available. The end of the list has been reached.
*/
Error GetNextSessionInfo(SessionInfo &aSessionInfo);
private:
CoapDtlsSession *GetSession(void) const { return static_cast<CoapDtlsSession *>(mPtr); }
void SetSession(CoapDtlsSession *aSession) { mPtr = aSession; }
uint64_t GetInitTime(void) const { return mData; }
void SetInitTime(uint64_t aInitTime) { mData = aInitTime; }
};
/**
@@ -127,19 +154,12 @@ public:
uint16_t GetUdpPort(void) const;
/**
* Gets the state of the Border Agent service.
* Indicates whether the Border Agent service is running.
*
* @returns The state of the Border Agent service.
* @retval TRUE Border Agent service is running.
* @retval FALSE Border Agent service is not running.
*/
State GetState(void) const { return mState; }
/**
* Indicates whether the Border Agent service is active.
*
* @retval TRUE Border Agent service is active.
* @retval FALSE Border Agent service is not active.
*/
bool IsActive(void) const { return (mState != kStateDisabled) && (mState != kStateStopped); }
bool IsRunning(void) const { return mIsRunning; }
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
/**
@@ -157,6 +177,18 @@ public:
typedef otBorderAgentEphemeralKeyCallback CallbackHandler; ///< Callback function pointer.
/**
* Represents the state of the `EphemeralKeyManager`.
*/
enum State : uint8_t
{
kStateDisabled = OT_BORDER_AGENT_STATE_DISABLED, ///< Ephemeral key feature is disabled.
kStateStopped = OT_BORDER_AGENT_STATE_STOPPED, ///< Enabled, but the key is not set and started.
kStateStarted = OT_BORDER_AGENT_STATE_STARTED, ///< Key is set and listening to accept connection.
kStateConnected = OT_BORDER_AGENT_STATE_CONNECTED, ///< Session connected, not full commissioner.
kStateAccepted = OT_BORDER_AGENT_STATE_ACCEPTED, ///< Session connected and accepted as full commissioner.
};
/**
* Enables/disables Ephemeral Key Manager.
*
@@ -308,8 +340,10 @@ private:
friend Heap::Allocatable<CoapDtlsSession>;
public:
Error ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage);
void Cleanup(void);
Error ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage);
void Cleanup(void);
bool IsActiveCommissioner(void) const { return mIsActiveCommissioner; }
uint64_t GetAllocationTime(void) const { return mAllocationTime; }
private:
class ForwardContext : public ot::LinkedListEntry<ForwardContext>,
@@ -366,13 +400,12 @@ private:
TimerMilliContext mTimer;
Ip6::Udp::Receiver mUdpReceiver;
Ip6::Netif::UnicastAddress mCommissionerAloc;
uint64_t mAllocationTime;
};
void Start(void) { IgnoreError(Start(kUdpPort)); }
Error Start(uint16_t aUdpPort);
Error Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength);
void Stop(void);
void HandleNotifierEvents(Events aEvents);
void Start(void);
void Stop(void);
void HandleNotifierEvents(Events aEvents);
template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
@@ -380,6 +413,7 @@ private:
CoapDtlsSession *HandleAcceptSession(void);
static void HandleRemoveSession(void *aContext, SecureSession &aSession);
void HandleRemoveSession(SecureSession &aSession);
CoapDtlsSession *FindActiveCommissionerSession(void);
void HandleSessionConnected(CoapDtlsSession &aSession);
void HandleSessionDisconnected(CoapDtlsSession &aSession, CoapDtlsSession::ConnectEvent aEvent);
@@ -387,9 +421,8 @@ private:
static Coap::Message::Code CoapCodeFromError(Error aError);
State mState;
Dtls::Transport mDtlsTransport;
CoapDtlsSession *mCoapDtlsSession;
bool mIsRunning;
Dtls::Transport mDtlsTransport;
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
Id mId;
bool mIdInitialized;
@@ -405,7 +438,11 @@ DeclareTmfHandler(BorderAgent, kUriRelayRx);
} // namespace MeshCoP
DefineCoreType(otBorderAgentId, MeshCoP::BorderAgent::Id);
DefineMapEnum(otBorderAgentEphemeralKeyState, MeshCoP::BorderAgent::State);
DefineCoreType(otBorderAgentSessionIterator, MeshCoP::BorderAgent::SessionIterator);
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
DefineMapEnum(otBorderAgentEphemeralKeyState, MeshCoP::BorderAgent::EphemeralKeyManager::State);
#endif
} // namespace ot
+1 -1
View File
@@ -100,7 +100,7 @@ class Tls;
/**
* Represents a secure session.
*/
class SecureSession : private LinkedListEntry<SecureSession>, private NonCopyable
class SecureSession : public LinkedListEntry<SecureSession>, private NonCopyable
{
friend class LinkedListEntry<SecureSession>;
friend class LinkedList<SecureSession>;
+270 -79
View File
@@ -36,16 +36,21 @@
namespace ot {
namespace Nexus {
using BorderAgent = MeshCoP::BorderAgent;
using BorderAgent = MeshCoP::BorderAgent;
using EphemeralKeyManager = ot::MeshCoP::BorderAgent::EphemeralKeyManager;
void TestBorderAgent(void)
{
Core nexus;
Node &node0 = nexus.CreateNode();
Node &node1 = nexus.CreateNode();
Ip6::SockAddr sockAddr;
Pskc pskc;
Coap::Message *message;
Core nexus;
Node &node0 = nexus.CreateNode();
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
Node &node3 = nexus.CreateNode();
Ip6::SockAddr sockAddr;
Pskc pskc;
Coap::Message *message;
BorderAgent::SessionIterator iter;
BorderAgent::SessionInfo sessionInfo;
Log("------------------------------------------------------------------------------------------------------");
Log("TestBorderAgent");
@@ -54,7 +59,7 @@ void TestBorderAgent(void)
// Form the topology:
// - node0 leader acting as Border Agent,
// - node1 staying disconnected (acting as candidate)
// - node1-3 stay disconnected (acting as candidate)
node0.Form();
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
@@ -64,10 +69,18 @@ void TestBorderAgent(void)
node1.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
node1.Get<ThreadNetif>().Up();
SuccessOrQuit(node2.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
node2.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
node2.Get<ThreadNetif>().Up();
SuccessOrQuit(node3.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
node3.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
node3.Get<ThreadNetif>().Up();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check Border Agent initial state");
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent>().IsRunning());
SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(node0.Get<BorderAgent>().GetUdpPort()));
@@ -87,9 +100,15 @@ void TestBorderAgent(void)
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateConnected);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mPskcSecureSessionSuccesses == 1);
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(!sessionInfo.mIsCommissioner);
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disconnect from candidate side");
@@ -98,7 +117,10 @@ void TestBorderAgent(void)
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent>().IsRunning());
iter.Init(node0.GetInstance());
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Establish a secure connection again");
@@ -110,9 +132,15 @@ void TestBorderAgent(void)
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateConnected);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mPskcSecureSessionSuccesses == 2);
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(!sessionInfo.mIsCommissioner);
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Send `Commissioner Petition` TMF command to become full commissioner");
@@ -123,19 +151,29 @@ void TestBorderAgent(void)
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateAccepted);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mPskcSecureSessionSuccesses == 2);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mPskcCommissionerPetitions == 1);
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(sessionInfo.mIsCommissioner);
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Send `Commissioner Keep Alive` and check timeout behavior");
nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateAccepted);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(sessionInfo.mIsCommissioner);
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerKeepAlive);
VerifyOrQuit(message != nullptr);
SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
@@ -146,16 +184,18 @@ void TestBorderAgent(void)
nexus.AdvanceTime(49 * Time::kOneSecondInMsec);
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateAccepted);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
Log(" Wait for additional 5 seconds (ensuring TIMEOUT_LEAD_PET and session disconnect guard time expires)");
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent>().IsRunning());
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
iter.Init(node0.GetInstance());
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Establish a secure session again and petition to become commissioner");
@@ -163,7 +203,6 @@ void TestBorderAgent(void)
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateConnected);
message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
VerifyOrQuit(message != nullptr);
@@ -172,12 +211,145 @@ void TestBorderAgent(void)
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateAccepted);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mPskcSecureSessionSuccesses == 3);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mPskcCommissionerPetitions == 2);
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(sessionInfo.mIsCommissioner);
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Establish two more secure sessions while the first session is still active");
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().Open());
SuccessOrQuit(node2.Get<Tmf::SecureAgent>().Connect(sockAddr));
SuccessOrQuit(node3.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
SuccessOrQuit(node3.Get<Tmf::SecureAgent>().Open());
SuccessOrQuit(node3.Get<Tmf::SecureAgent>().Connect(sockAddr));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node3.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mPskcSecureSessionSuccesses == 5);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mPskcCommissionerPetitions == 2);
iter.Init(node0.GetInstance());
for (uint8_t numSessions = 0; numSessions < 3; numSessions++)
{
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
if (node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)))
{
VerifyOrQuit(sessionInfo.mIsCommissioner);
}
else
{
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)) ||
node3.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(!sessionInfo.mIsCommissioner);
}
}
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disconnect from candidate side on node 1");
node1.Get<Tmf::SecureAgent>().Close();
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent>().IsRunning());
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node3.Get<Tmf::SecureAgent>().IsConnected());
iter.Init(node0.GetInstance());
for (uint8_t numSessions = 0; numSessions < 2; numSessions++)
{
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(!sessionInfo.mIsCommissioner);
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)) ||
node3.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
}
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Connect again from node 1 after 25 seconds");
nexus.AdvanceTime(25 * Time::kOneSecondInMsec);
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node3.Get<Tmf::SecureAgent>().IsConnected());
iter.Init(node0.GetInstance());
for (uint8_t numSessions = 0; numSessions < 3; numSessions++)
{
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(!sessionInfo.mIsCommissioner);
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)) ||
node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)) ||
node3.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
}
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Wait for the first two sessions to timeout, check that only the recent node1 session is connected");
nexus.AdvanceTime(28 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(!node2.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(!node3.Get<Tmf::SecureAgent>().IsConnected());
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(!sessionInfo.mIsCommissioner);
VerifyOrQuit(node1.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Wait for the node1 session to also timeout, validate that its disconnected");
nexus.AdvanceTime(25 * Time::kOneSecondInMsec);
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(!node2.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(!node3.Get<Tmf::SecureAgent>().IsConnected());
iter.Init(node0.GetInstance());
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
}
//----------------------------------------------------------------------------------------------------------------------
static bool sEphemeralKeyCallbackCalled = false;
void HandleEphemeralKeyChange(void *aContext)
@@ -188,7 +360,7 @@ void HandleEphemeralKeyChange(void *aContext)
node = reinterpret_cast<Node *>(aContext);
Log(" EphemeralKeyCallback() state:%s",
BorderAgent::EphemeralKeyManager::StateToString(node->Get<BorderAgent::EphemeralKeyManager>().GetState()));
EphemeralKeyManager::StateToString(node->Get<EphemeralKeyManager>().GetState()));
sEphemeralKeyCallbackCalled = true;
}
@@ -201,13 +373,15 @@ void TestBorderAgentEphemeralKey(void)
static constexpr uint16_t kEphemeralKeySize = sizeof(kEphemeralKey) - 1;
static constexpr uint16_t kUdpPort = 49155;
Core nexus;
Node &node0 = nexus.CreateNode();
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
Ip6::SockAddr sockAddr;
Ip6::SockAddr baSockAddr;
Pskc pskc;
Core nexus;
Node &node0 = nexus.CreateNode();
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
Ip6::SockAddr sockAddr;
Ip6::SockAddr baSockAddr;
Pskc pskc;
BorderAgent::SessionIterator iter;
BorderAgent::SessionInfo sessionInfo;
Log("------------------------------------------------------------------------------------------------------");
Log("TestBorderAgentEphemeralKey");
@@ -245,15 +419,15 @@ void TestBorderAgentEphemeralKey(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check Border Agent ephemeral key feature enabled");
node0.Get<MeshCoP::BorderAgent::EphemeralKeyManager>().SetEnabled(false);
VerifyOrQuit(node0.Get<MeshCoP::BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateDisabled);
VerifyOrQuit(node0.Get<MeshCoP::BorderAgent::EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0,
kUdpPort) == kErrorInvalidState);
node0.Get<EphemeralKeyManager>().SetEnabled(false);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateDisabled);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort) ==
kErrorInvalidState);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcInvalidBaStateErrors == 1);
node0.Get<MeshCoP::BorderAgent::EphemeralKeyManager>().SetEnabled(true);
VerifyOrQuit(node0.Get<MeshCoP::BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStopped);
node0.Get<EphemeralKeyManager>().SetEnabled(true);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check Border Agent ephemeral key initial state");
@@ -264,12 +438,12 @@ void TestBorderAgentEphemeralKey(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Set and start ephemeral key on Border Agent");
node0.Get<BorderAgent::EphemeralKeyManager>().SetCallback(HandleEphemeralKeyChange, &node0);
node0.Get<EphemeralKeyManager>().SetCallback(HandleEphemeralKeyChange, &node0);
SuccessOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetUdpPort() == kUdpPort);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 1);
SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(kUdpPort));
@@ -293,7 +467,7 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateConnected);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 1);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcSecureSessionSuccesses == 1);
@@ -310,7 +484,7 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStopped);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
VerifyOrQuit(sEphemeralKeyCallbackCalled);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 1);
@@ -322,11 +496,10 @@ void TestBorderAgentEphemeralKey(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Start using ephemeral key again with short timeout (20 seconds) and establish a connection");
SuccessOrQuit(
node0.Get<BorderAgent::EphemeralKeyManager>().Start(kEphemeralKey, 20 * Time::kOneSecondInMsec, kUdpPort));
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, 20 * Time::kOneSecondInMsec, kUdpPort));
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetUdpPort() == kUdpPort);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
@@ -334,7 +507,7 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateConnected);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 2);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcSecureSessionSuccesses == 2);
@@ -346,7 +519,7 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(25 * Time::kOneSecondInMsec);
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStopped);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
VerifyOrQuit(sEphemeralKeyCallbackCalled);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 2);
@@ -357,16 +530,16 @@ void TestBorderAgentEphemeralKey(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Start using ephemeral key without any candidate connecting, check timeout");
SuccessOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetUdpPort() == kUdpPort);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
Log(" Wait more than 120 seconds (default ephemeral key timeout)");
sEphemeralKeyCallbackCalled = false;
nexus.AdvanceTime(122 * Time::kOneSecondInMsec);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStopped);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
VerifyOrQuit(sEphemeralKeyCallbackCalled);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 3);
@@ -377,10 +550,10 @@ void TestBorderAgentEphemeralKey(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check that ephemeral key use is stopped after max failed connection attempts");
SuccessOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetUdpPort() == kUdpPort);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
SuccessOrQuit(
node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize - 2));
@@ -394,7 +567,7 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
}
Log(" Attempt 10 (final attempt) to connect with the wrong key, check that ephemeral key use is stopped");
@@ -404,7 +577,7 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStopped);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
VerifyOrQuit(sEphemeralKeyCallbackCalled);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 4);
@@ -418,10 +591,10 @@ void TestBorderAgentEphemeralKey(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that disabling ephemeral key will stop and disconnect an active session");
SuccessOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetUdpPort() == kUdpPort);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
SuccessOrQuit(
node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize));
@@ -432,18 +605,18 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateConnected);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
VerifyOrQuit(sEphemeralKeyCallbackCalled);
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
sEphemeralKeyCallbackCalled = false;
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateConnected);
node0.Get<BorderAgent::EphemeralKeyManager>().SetEnabled(false);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
node0.Get<EphemeralKeyManager>().SetEnabled(false);
nexus.AdvanceTime(0);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateDisabled);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateDisabled);
VerifyOrQuit(sEphemeralKeyCallbackCalled);
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
@@ -459,16 +632,16 @@ void TestBorderAgentEphemeralKey(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check ephemeral key can be used along with PSKc");
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent>().IsRunning());
SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(node0.Get<BorderAgent>().GetUdpPort()));
node0.Get<BorderAgent::EphemeralKeyManager>().SetEnabled(true);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStopped);
node0.Get<EphemeralKeyManager>().SetEnabled(true);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
SuccessOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
SuccessOrQuit(node0.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStarted);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetUdpPort() == kUdpPort);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
Log(" Establish a secure session using PSKc (from node2)");
@@ -483,7 +656,13 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateConnected);
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(!sessionInfo.mIsCommissioner);
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
Log(" Establish a secure session using ephemeral key (from node1)");
@@ -497,18 +676,18 @@ void TestBorderAgentEphemeralKey(void)
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateConnected);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateConnected);
VerifyOrQuit(sEphemeralKeyCallbackCalled);
Log(" Stop the secure session using ephemeral key");
node0.Get<BorderAgent::EphemeralKeyManager>().Stop();
node0.Get<EphemeralKeyManager>().Stop();
sEphemeralKeyCallbackCalled = false;
nexus.AdvanceTime(0);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStopped);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
VerifyOrQuit(sEphemeralKeyCallbackCalled);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 6);
@@ -521,27 +700,39 @@ void TestBorderAgentEphemeralKey(void)
Log(" Check the BA session using PSKc is still connected");
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateConnected);
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(!sessionInfo.mIsCommissioner);
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node2.Get<Tmf::SecureAgent>().IsConnected());
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().GetState() == BorderAgent::kStateStopped);
VerifyOrQuit(node0.Get<BorderAgent>().GetState() == BorderAgent::kStateConnected);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
iter.Init(node0.GetInstance());
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
VerifyOrQuit(sessionInfo.mIsConnected);
VerifyOrQuit(!sessionInfo.mIsCommissioner);
VerifyOrQuit(node2.Get<ThreadNetif>().HasUnicastAddress(AsCoreType(&sessionInfo.mPeerSockAddr.mAddress)));
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check proper error is returned with invalid ephemeral key (too short or long)");
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().Start(kTooShortEphemeralKey, /* aTimeout */ 0,
kUdpPort) == kErrorInvalidArgs);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().Start(kTooShortEphemeralKey, /* aTimeout */ 0, kUdpPort) ==
kErrorInvalidArgs);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 6);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcInvalidArgsErrors == 1);
VerifyOrQuit(node0.Get<BorderAgent::EphemeralKeyManager>().Start(kTooLongEphemeralKey, /* aTimeout */ 0,
kUdpPort) == kErrorInvalidArgs);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().Start(kTooLongEphemeralKey, /* aTimeout */ 0, kUdpPort) ==
kErrorInvalidArgs);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcActivations == 6);
VerifyOrQuit(node0.Get<BorderAgent>().GetCounters().mEpskcInvalidArgsErrors == 2);
}