[secure-transport] support multiple sessions on the same transport (#11092)

This commit updates `SecureTransport` to support multiple
`SecureSession`s on the same transport. The transport tracks a list
of sessions that it owns and manages. Two new callbacks are
introduced:

- `AcceptCallback`: Used to accept a new connection request, providing
  the `SecureSession` instance to use (passing ownership of the
  session to the transport).
- `RemoveSessionCallback`: Signals that a session is removed,
  releasing ownership of the session.

`BorderAgent`, `Tmf::SecureAgent`, and `ApplicationCoapSecure` are
updated to adopt the new model.

This commit also updates the Nexus `test_dtls`, adding
`TestDtlsMultiSession` to validate multiple session support
behavior.
This commit is contained in:
Abtin Keshavarzian
2025-01-08 13:43:05 -08:00
committed by GitHub
parent 70d315af23
commit 2ae1ce5293
11 changed files with 672 additions and 129 deletions
+8 -3
View File
@@ -53,10 +53,11 @@ CoapBase::CoapBase(Instance &aInstance, Sender aSender)
{
}
void CoapBase::ClearRequestsAndResponses(void)
void CoapBase::ClearAllRequestsAndResponses(void)
{
ClearRequests(nullptr); // Clear requests matching any address.
mResponsesQueue.DequeueAllResponses();
mRetransmissionTimer.Stop();
}
void CoapBase::ClearRequests(const Ip6::Address &aAddress) { ClearRequests(&aAddress); }
@@ -1553,7 +1554,11 @@ void ResponsesQueue::UpdateQueue(void)
void ResponsesQueue::DequeueResponse(Message &aMessage) { mQueue.DequeueAndFree(aMessage); }
void ResponsesQueue::DequeueAllResponses(void) { mQueue.DequeueAndFreeAll(); }
void ResponsesQueue::DequeueAllResponses(void)
{
mQueue.DequeueAndFreeAll();
mTimer.Stop();
}
void ResponsesQueue::HandleTimer(Timer &aTimer)
{
@@ -1693,7 +1698,7 @@ Error Coap::Stop(void)
VerifyOrExit(mSocket.IsBound());
SuccessOrExit(error = mSocket.Close());
ClearRequestsAndResponses();
ClearAllRequestsAndResponses();
exit:
return error;
+2 -2
View File
@@ -358,9 +358,9 @@ public:
typedef Error (*Interceptor)(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext);
/**
* Clears requests and responses used by this CoAP agent.
* Clears all requests and responses used by this CoAP agent and stops all timers.
*/
void ClearRequestsAndResponses(void);
void ClearAllRequestsAndResponses(void);
/**
* Clears requests with specified source address used by this CoAP agent.
+24 -1
View File
@@ -51,6 +51,13 @@ SecureSession::SecureSession(Instance &aInstance, Dtls::Transport &aDtlsTranspor
Dtls::Session::SetReceiveCallback(HandleDtlsReceive, this);
}
void SecureSession::Cleanup(void)
{
ClearAllRequestsAndResponses();
mTransmitQueue.DequeueAndFreeAll();
mTransmitTask.Unpost();
}
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
Error SecureSession::SendMessage(Message &aMessage,
@@ -103,7 +110,7 @@ void SecureSession::HandleDtlsConnectEvent(ConnectEvent aEvent)
if (aEvent != kConnected)
{
mTransmitQueue.DequeueAndFreeAll();
ClearRequestsAndResponses();
ClearAllRequestsAndResponses();
}
mConnectCallback.InvokeIfSet(aEvent);
@@ -151,6 +158,22 @@ exit:
FreeMessageOnError(message, error);
}
#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
MeshCoP::SecureSession *ApplicationCoapSecure::HandleDtlsAccept(void *aContext, const Ip6::MessageInfo &aMessageInfo)
{
OT_UNUSED_VARIABLE(aMessageInfo);
return static_cast<ApplicationCoapSecure *>(aContext)->HandleDtlsAccept();
}
SecureSession *ApplicationCoapSecure::HandleDtlsAccept(void)
{
return IsSessionInUse() ? nullptr : static_cast<SecureSession *>(this);
}
#endif
} // namespace Coap
} // namespace ot
+10
View File
@@ -60,6 +60,11 @@ typedef MeshCoP::Dtls Dtls;
class SecureSession : public CoapBase, public Dtls::Session
{
public:
/**
* Dequeues and frees all queued messages (requests and responses) and stops all timers and tasklets.
*/
void Cleanup(void);
/**
* Sets the connection event callback.
*
@@ -148,8 +153,13 @@ public:
, Dtls::Transport::Extension(static_cast<Dtls::Transport &>(*this))
, SecureSession(aInstance, static_cast<Dtls::Transport &>(*this))
{
Dtls::Transport::SetAcceptCallback(HandleDtlsAccept, this);
Dtls::Transport::SetExtension(static_cast<Dtls::Transport::Extension &>(*this));
}
private:
static MeshCoP::SecureSession *HandleDtlsAccept(void *aContext, const Ip6::MessageInfo &aMessageInfo);
SecureSession *HandleDtlsAccept(void);
};
#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
+63 -11
View File
@@ -51,6 +51,7 @@ BorderAgent::BorderAgent(Instance &aInstance)
, mUdpReceiver(BorderAgent::HandleUdpReceive, this)
, mTimer(aInstance)
, mDtlsTransport(aInstance, kNoLinkSecurity)
, mCoapDtlsSession(nullptr)
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
, mIdInitialized(false)
#endif
@@ -129,16 +130,14 @@ Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLen
}
#endif
mCoapDtlsSession.Reset(CoapDtlsSession::Allocate(GetInstance(), mDtlsTransport));
VerifyOrExit(mCoapDtlsSession != nullptr, error = kErrorNoBufs);
mDtlsTransport.SetAcceptCallback(HandleAcceptSession, this);
mDtlsTransport.SetRemoveSessionCallback(HandleRemoveSession, this);
SuccessOrExit(error = mDtlsTransport.Open());
SuccessOrExit(error = mDtlsTransport.Bind(aUdpPort));
SuccessOrExit(error = mDtlsTransport.SetPsk(aPsk, aPskLength));
mCoapDtlsSession->SetConnectCallback(HandleConnected, this);
mState = kStateStarted;
LogInfo("Border Agent start listening on port %u", GetUdpPort());
@@ -163,7 +162,6 @@ void BorderAgent::Stop(void)
mTimer.Stop();
mDtlsTransport.Close();
mCoapDtlsSession.Free();
mState = kStateStopped;
LogInfo("Border Agent stopped");
@@ -175,6 +173,7 @@ exit:
void BorderAgent::Disconnect(void)
{
VerifyOrExit(mState == kStateConnected || mState == kStateAccepted);
VerifyOrExit(mCoapDtlsSession != nullptr);
mCoapDtlsSession->Disconnect();
@@ -225,11 +224,51 @@ exit:
void BorderAgent::HandleTimeout(void)
{
if (mCoapDtlsSession->IsConnected())
{
mCoapDtlsSession->Disconnect();
LogWarn("Reset secure session");
}
VerifyOrExit(mCoapDtlsSession != nullptr);
VerifyOrExit(mCoapDtlsSession->IsConnected());
mCoapDtlsSession->Disconnect();
LogWarn("Reset secure session");
exit:
return;
}
SecureSession *BorderAgent::HandleAcceptSession(void *aContext, const Ip6::MessageInfo &aMessageInfo)
{
OT_UNUSED_VARIABLE(aMessageInfo);
return static_cast<BorderAgent *>(aContext)->HandleAcceptSession();
}
BorderAgent::CoapDtlsSession *BorderAgent::HandleAcceptSession(void)
{
CoapDtlsSession *session = nullptr;
VerifyOrExit(mCoapDtlsSession == nullptr);
session = CoapDtlsSession::Allocate(GetInstance(), mDtlsTransport);
VerifyOrExit(session != nullptr);
session->SetConnectCallback(HandleConnected, this);
mCoapDtlsSession = session;
exit:
return session;
}
void BorderAgent::HandleRemoveSession(void *aContext, SecureSession &aSesssion)
{
static_cast<BorderAgent *>(aContext)->HandleRemoveSession(aSesssion);
}
void BorderAgent::HandleRemoveSession(SecureSession &aSesssion)
{
CoapDtlsSession &coapSession = static_cast<CoapDtlsSession &>(aSesssion);
coapSession.Cleanup();
coapSession.Free();
mCoapDtlsSession = nullptr;
}
void BorderAgent::HandleConnected(Dtls::Session::ConnectEvent aEvent, void *aContext)
@@ -315,6 +354,7 @@ Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::Mes
OffsetRange offsetRange;
VerifyOrExit(mState != kStateStopped);
VerifyOrExit(mCoapDtlsSession != nullptr);
switch (aUri)
{
@@ -389,6 +429,7 @@ void BorderAgent::HandleCoapResponse(const ForwardContext &aForwardContext,
Error error;
SuccessOrExit(error = aResult);
VerifyOrExit(mCoapDtlsSession != nullptr);
VerifyOrExit((message = mCoapDtlsSession->NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
if (aForwardContext.IsPetition() && aResponse->GetCode() == Coap::kCodeChanged)
@@ -464,6 +505,7 @@ bool BorderAgent::HandleUdpReceive(const Message &aMessage, const Ip6::MessageIn
OffsetRange offsetRange;
VerifyOrExit(aMessageInfo.GetSockAddr() == mCommissionerAloc.GetAddress());
VerifyOrExit(mCoapDtlsSession != nullptr);
didHandle = true;
@@ -499,9 +541,11 @@ exit:
Error BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage)
{
Error error;
Error error = kErrorNone;
OffsetRange offsetRange;
VerifyOrExit(mCoapDtlsSession != nullptr);
offsetRange.InitFromMessageOffsetToEnd(aMessage);
SuccessOrExit(error = aForwardMessage.AppendBytesFromMessage(aMessage, offsetRange));
@@ -519,6 +563,8 @@ void BorderAgent::SendErrorMessage(const ForwardContext &aForwardContext, Error
Error error = kErrorNone;
Coap::Message *message = nullptr;
VerifyOrExit(mCoapDtlsSession != nullptr);
VerifyOrExit((message = mCoapDtlsSession->NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = aForwardContext.ToHeader(*message, CoapCodeFromError(aError)));
SuccessOrExit(error = mCoapDtlsSession->SendMessage(*message));
@@ -533,6 +579,8 @@ void BorderAgent::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate
Error error = kErrorNone;
Coap::Message *message = nullptr;
VerifyOrExit(mCoapDtlsSession != nullptr);
VerifyOrExit((message = mCoapDtlsSession->NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
if (aRequest.IsNonConfirmable() || aSeparate)
@@ -593,6 +641,8 @@ template <> void BorderAgent::HandleTmf<kUriRelayRx>(Coap::Message &aMessage, co
VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop);
VerifyOrExit(mCoapDtlsSession != nullptr);
message = mCoapDtlsSession->NewPriorityNonConfirmablePostMessage(kUriRelayRx);
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
@@ -681,6 +731,8 @@ void BorderAgent::HandleTmfDatasetGet(Coap::Message &aMessage, const Ip6::Messag
Error error = kErrorNone;
Coap::Message *response = nullptr;
VerifyOrExit(mCoapDtlsSession != nullptr);
// When processing `MGMT_GET` request directly on Border Agent,
// the Security Policy flags (O-bit) should be ignored to allow
// the commissioner candidate to get the full Operational Dataset.
+6 -1
View File
@@ -300,6 +300,11 @@ private:
template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
static SecureSession *HandleAcceptSession(void *aContext, const Ip6::MessageInfo &aMessageInfo);
CoapDtlsSession *HandleAcceptSession(void);
static void HandleRemoveSession(void *aContext, SecureSession &aSesssion);
void HandleRemoveSession(SecureSession &aSesssion);
static void HandleConnected(Dtls::Session::ConnectEvent aEvent, void *aContext);
void HandleConnected(Dtls::Session::ConnectEvent aEvent);
static void HandleCoapResponse(void *aContext,
@@ -331,7 +336,7 @@ private:
Ip6::Netif::UnicastAddress mCommissionerAloc;
TimeoutTimer mTimer;
Dtls::Transport mDtlsTransport;
OwnedPtr<CoapDtlsSession> mCoapDtlsSession;
CoapDtlsSession *mCoapDtlsSession;
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
Id mId;
bool mIdInitialized;
+169 -75
View File
@@ -51,13 +51,22 @@ RegisterLogModule("SecTransport");
// SecureSession
SecureSession::SecureSession(SecureTransport &aTransport)
: mTimerSet(false)
, mState(kStateDisconnected)
, mMessageSubType(Message::kSubTypeNone)
, mConnectEvent(kDisconnectedError)
, mTransport(aTransport)
, mReceiveMessage(nullptr)
: mTransport(aTransport)
{
Init();
}
void SecureSession::Init(void)
{
mTimerSet = false;
mIsServer = false;
mState = kStateDisconnected;
mMessageSubType = Message::kSubTypeNone;
mConnectEvent = kDisconnectedError;
mReceiveMessage = nullptr;
mMessageInfo.Clear();
MarkAsNotUsed();
ClearAllBytes(mSsl);
ClearAllBytes(mConf);
#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C)
@@ -99,35 +108,39 @@ Error SecureSession::Connect(const Ip6::SockAddr &aSockAddr)
Error error;
VerifyOrExit(mTransport.mIsOpen, error = kErrorInvalidState);
VerifyOrExit(IsDisconnected(), error = kErrorInvalidState);
VerifyOrExit(!IsSessionInUse(), error = kErrorInvalidState);
mTransport.DecremenetRemainingConnectionAttempts();
Init();
mMessageInfo.SetPeerAddr(aSockAddr.GetAddress());
mMessageInfo.SetPeerPort(aSockAddr.mPort);
mTransport.mIsServer = false;
SuccessOrExit(error = Setup());
error = Setup();
mTransport.mSessions.Push(*this);
exit:
return error;
}
void SecureSession::HandleTransportReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
void SecureSession::Accept(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
if (IsDisconnected())
mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
mMessageInfo.SetPeerPort(aMessageInfo.GetPeerPort());
mMessageInfo.SetIsHostInterface(aMessageInfo.IsHostInterface());
mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr());
mMessageInfo.SetSockPort(aMessageInfo.GetSockPort());
mIsServer = true;
if (Setup() == kErrorNone)
{
mTransport.DecremenetRemainingConnectionAttempts();
mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
mMessageInfo.SetPeerPort(aMessageInfo.GetPeerPort());
mMessageInfo.SetIsHostInterface(aMessageInfo.IsHostInterface());
mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr());
mMessageInfo.SetSockPort(aMessageInfo.GetSockPort());
SuccessOrExit(Setup());
HandleTransportReceive(aMessage);
}
}
void SecureSession::HandleTransportReceive(Message &aMessage)
{
VerifyOrExit(!IsDisconnected());
#ifdef MBEDTLS_SSL_SRV_C
if (IsConnecting())
@@ -151,17 +164,23 @@ Error SecureSession::Setup(void)
OT_ASSERT(mTransport.mCipherSuite != SecureTransport::kUnspecifiedCipherSuite);
VerifyOrExit(mTransport.mIsOpen, error = kErrorInvalidState);
VerifyOrExit(IsDisconnected(), error = kErrorBusy);
SetState(kStateInitializing);
if (mTransport.HasNoRemainingConnectionAttempts())
{
mConnectEvent = kDisconnectedMaxAttempts;
error = kErrorNoBufs;
ExitNow();
}
mTransport.DecremenetRemainingConnectionAttempts();
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Setup the mbedtls_ssl_config `mConf`.
mbedtls_ssl_config_init(&mConf);
rval = mbedtls_ssl_config_defaults(&mConf, mTransport.mIsServer ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
rval = mbedtls_ssl_config_defaults(&mConf, mIsServer ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
mTransport.mDatagramTransport ? MBEDTLS_SSL_TRANSPORT_DATAGRAM
: MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
@@ -257,7 +276,7 @@ Error SecureSession::Setup(void)
{
mbedtls_ssl_cookie_init(&mCookieCtx);
if (mTransport.mIsServer)
if (mIsServer)
{
rval = mbedtls_ssl_cookie_setup(&mCookieCtx, Crypto::MbedTls::CryptoSecurePrng, nullptr);
VerifyOrExit(rval == 0);
@@ -302,18 +321,11 @@ Error SecureSession::Setup(void)
exit:
if (IsInitializing())
{
error = Crypto::MbedTls::MapError(rval);
error = (error == kErrorNone) ? Crypto::MbedTls::MapError(rval) : error;
if (mTransport.HasNoRemainingConnectionAttempts())
{
mTransport.Close();
mTransport.mAutoCloseCallback.InvokeIfSet();
}
else
{
SetState(kStateDisconnected);
FreeMbedtls();
}
SetState(kStateDisconnected);
FreeMbedtls();
mTransport.mUpdateTask.Post();
}
return error;
@@ -332,8 +344,6 @@ void SecureSession::Disconnect(ConnectEvent aEvent)
mTimerFinish = TimerMilli::GetNow() + kGuardTimeNewConnectionMilli;
mTransport.mTimer.FireAtIfEarlier(mTimerFinish);
mMessageInfo.Clear();
FreeMbedtls();
exit:
@@ -488,18 +498,8 @@ void SecureSession::HandleTimer(TimeMilli aNow)
ExitNow();
}
if (mTransport.HasNoRemainingConnectionAttempts())
{
mTransport.Close();
mConnectEvent = kDisconnectedMaxAttempts;
mTransport.mAutoCloseCallback.InvokeIfSet();
}
else
{
SetState(kStateDisconnected);
}
mConnectedCallback.InvokeIfSet(mConnectEvent);
SetState(kStateDisconnected);
mTransport.mUpdateTask.Post();
}
exit:
@@ -659,15 +659,15 @@ SecureTransport::SecureTransport(Instance &aInstance, LinkSecurityMode aLayerTwo
: mLayerTwoSecurity(aLayerTwoSecurity)
, mDatagramTransport(aDatagramTransport)
, mIsOpen(false)
, mIsServer(true)
, mIsClosing(false)
, mVerifyPeerCertificate(true)
, mCipherSuite(kUnspecifiedCipherSuite)
, mPskLength(0)
, mMaxConnectionAttempts(0)
, mRemainingConnectionAttempts(0)
, mSession(nullptr)
, mSocket(aInstance, *this)
, mTimer(aInstance, SecureTransport::HandleTimer, this)
, mTimer(aInstance, HandleTimer, this)
, mUpdateTask(aInstance, HandleUpdateTask, this)
#if OPENTHREAD_CONFIG_TLS_API_ENABLE
, mExtension(nullptr)
#endif
@@ -705,14 +705,29 @@ exit:
void SecureTransport::HandleReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
SecureSession *session;
VerifyOrExit(mIsOpen);
if (!mSession->IsDisconnected())
session = mSessions.FindMatching(aMessageInfo);
if (session != nullptr)
{
VerifyOrExit(mSession->Matches(aMessageInfo));
session->HandleTransportReceive(aMessage);
ExitNow();
}
mSession->HandleTransportReceive(aMessage, aMessageInfo);
// A new connection request
VerifyOrExit(mAcceptCallback.IsSet());
session = mAcceptCallback.Invoke(aMessageInfo);
VerifyOrExit(session != nullptr);
session->Init();
mSessions.Push(*session);
session->Accept(aMessage, aMessageInfo);
exit:
return;
@@ -725,10 +740,9 @@ Error SecureTransport::Bind(uint16_t aPort)
VerifyOrExit(mIsOpen, error = kErrorInvalidState);
VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready);
VerifyOrExit(mSession->IsDisconnected(), error = kErrorInvalidState);
VerifyOrExit(mSessions.IsEmpty(), error = kErrorInvalidState);
SuccessOrExit(error = mSocket.Bind(aPort));
mIsServer = true;
error = mSocket.Bind(aPort);
exit:
return error;
@@ -742,10 +756,9 @@ Error SecureTransport::Bind(TransportCallback aCallback, void *aContext)
VerifyOrExit(!mSocket.IsBound(), error = kErrorAlready);
VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready);
VerifyOrExit(mSession->IsDisconnected(), error = kErrorInvalidState);
VerifyOrExit(mSessions.IsEmpty(), error = kErrorInvalidState);
mTransportCallback.Set(aCallback, aContext);
mIsServer = true;
exit:
return error;
@@ -754,11 +767,26 @@ exit:
void SecureTransport::Close(void)
{
VerifyOrExit(mIsOpen);
VerifyOrExit(!mIsClosing);
mSession->Disconnect(SecureSession::kDisconnectedLocalClosed);
mSession->SetState(SecureSession::kStateDisconnected);
// `mIsClosing` is used to protect against multiple
// calls to `Close()` and re-entry. As the transport is closed,
// all existing sessions are disconnected, which can trigger
// connect and remove callbacks to be invoked. These callbacks
// may call `Close()` again.
mIsOpen = false;
mIsClosing = true;
for (SecureSession &session : mSessions)
{
session.Disconnect(SecureSession::kDisconnectedLocalClosed);
session.SetState(SecureSession::kStateDisconnected);
}
RemoveDisconnectedSessions();
mIsOpen = false;
mIsClosing = false;
mTransportCallback.Clear();
IgnoreError(mSocket.Close());
mTimer.Stop();
@@ -767,6 +795,22 @@ exit:
return;
}
void SecureTransport::RemoveDisconnectedSessions(void)
{
LinkedList<SecureSession> disconnectedSessions;
SecureSession *session;
mSessions.RemoveAllMatching(disconnectedSessions, SecureSession::kStateDisconnected);
while ((session = disconnectedSessions.Pop()) != nullptr)
{
session->mConnectedCallback.InvokeIfSet(session->mConnectEvent);
session->MarkAsNotUsed();
session->mMessageInfo.Clear();
mRemoveSessionCallback.InvokeIfSet(*session);
}
}
void SecureTransport::DecremenetRemainingConnectionAttempts(void)
{
if (mRemainingConnectionAttempts > 0)
@@ -931,6 +975,22 @@ exit:
#endif // (MBEDTLS_VERSION_NUMBER >= 0x03000000)
void SecureTransport::HandleUpdateTask(Tasklet &aTasklet)
{
static_cast<SecureTransport *>(static_cast<TaskletContext &>(aTasklet).GetContext())->HandleUpdateTask();
}
void SecureTransport::HandleUpdateTask(void)
{
RemoveDisconnectedSessions();
if (mSessions.IsEmpty() && HasNoRemainingConnectionAttempts())
{
Close();
mAutoCloseCallback.InvokeIfSet();
}
}
void SecureTransport::HandleTimer(Timer &aTimer)
{
static_cast<SecureTransport *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
@@ -938,12 +998,17 @@ void SecureTransport::HandleTimer(Timer &aTimer)
void SecureTransport::HandleTimer(void)
{
if (mIsOpen)
{
TimeMilli now = TimerMilli::GetNow();
TimeMilli now = TimerMilli::GetNow();
mSession->HandleTimer(now);
VerifyOrExit(mIsOpen);
for (SecureSession &session : mSessions)
{
session.HandleTimer(now);
}
exit:
return;
}
void SecureTransport::HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr)
@@ -1138,8 +1203,9 @@ Error SecureTransport::Extension::GetPeerCertificateBase64(unsigned char *aPeerC
size_t aCertBufferSize)
{
Error error = kErrorNone;
SecureSession *session = mSecureTransport.mSession;
SecureSession *session = mSecureTransport.mSessions.GetHead();
VerifyOrExit(session != nullptr, error = kErrorInvalidState);
VerifyOrExit(session->IsConnected(), error = kErrorInvalidState);
#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
@@ -1174,8 +1240,13 @@ Error SecureTransport::Extension::GetPeerSubjectAttributeByOid(const char *aOid,
const mbedtls_asn1_named_data *data;
size_t length;
size_t attributeBufferSize;
SecureSession *session = mSecureTransport.mSession;
mbedtls_x509_crt *peerCert = const_cast<mbedtls_x509_crt *>(mbedtls_ssl_get_peer_cert(&session->mSsl));
SecureSession *session;
mbedtls_x509_crt *peerCert;
session = mSecureTransport.mSessions.GetHead();
VerifyOrExit(session != nullptr, error = kErrorInvalidState);
peerCert = const_cast<mbedtls_x509_crt *>(mbedtls_ssl_get_peer_cert(&session->mSsl));
VerifyOrExit(aAttributeLength != nullptr, error = kErrorInvalidArgs);
attributeBufferSize = *aAttributeLength;
@@ -1206,9 +1277,16 @@ Error SecureTransport::Extension::GetThreadAttributeFromPeerCertificate(int
uint8_t *aAttributeBuffer,
size_t *aAttributeLength)
{
const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&mSecureTransport.mSession->mSsl);
Error error;
SecureSession *session = mSecureTransport.mSessions.GetHead();
const mbedtls_x509_crt *cert;
return GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
VerifyOrExit(session != nullptr, error = kErrorInvalidState);
cert = mbedtls_ssl_get_peer_cert(&session->mSsl);
error = GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
exit:
return error;
}
#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
@@ -1300,6 +1378,22 @@ exit:
#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
//---------------------------------------------------------------------------------------------------------------------
// Tls
SecureSession *Tls::HandleAccept(void *aContext, const Ip6::MessageInfo &aMessageInfo)
{
OT_UNUSED_VARIABLE(aMessageInfo);
return static_cast<Tls *>(aContext)->HandleAccept();
}
SecureSession *Tls::HandleAccept(void) { return IsSessionInUse() ? nullptr : static_cast<SecureSession *>(this); }
#endif
} // namespace MeshCoP
} // namespace ot
+96 -25
View File
@@ -74,6 +74,7 @@
#include <openthread/coap_secure.h>
#include "common/callback.hpp"
#include "common/linked_list.hpp"
#include "common/locator.hpp"
#include "common/log.hpp"
#include "common/message.hpp"
@@ -99,8 +100,10 @@ class Tls;
/**
* Represents a secure session.
*/
class SecureSession : private NonCopyable
class SecureSession : private LinkedListEntry<SecureSession>, private NonCopyable
{
friend class LinkedListEntry<SecureSession>;
friend class LinkedList<SecureSession>;
friend class SecureTransport;
friend class Dtls;
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
@@ -155,10 +158,16 @@ public:
/**
* Establishes a secure session (as client).
*
* On success, ownership of the session is passed to the associated secure transport (`GetTransport()`).
* The transport will then manage the session. Once the session is disconnected and removed from the transport, the
* secure transport signals this using the `RemoveSessionCallback` callback, where ownership is
* released.
*
* @param[in] aSockAddr The server address to connect to.
*
* @retval kErrorNone Successfully started session establishment
* @retval kErrorInvalidState Transport is not ready.
* @retval kErrorNoBufs Has reached max number of allowed connection attempts.
*/
Error Connect(const Ip6::SockAddr &aSockAddr);
@@ -213,6 +222,8 @@ public:
protected:
explicit SecureSession(SecureTransport &aTransport);
bool IsSessionInUse(void) const { return (mNext != this); }
private:
static constexpr uint32_t kGuardTimeNewConnectionMilli = 2000;
static constexpr uint16_t kMaxContentLen = OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN;
@@ -232,14 +243,18 @@ private:
kStateDisconnecting,
};
void Init(void);
bool IsDisconnected(void) const { return mState == kStateDisconnected; }
bool IsInitializing(void) const { return mState == kStateInitializing; }
bool IsConnecting(void) const { return mState == kStateConnecting; }
bool IsDisconnecting(void) const { return mState == kStateDisconnecting; }
bool IsConnectingOrConnected(void) const { return mState == kStateConnecting || mState == kStateConnected; }
void MarkAsNotUsed(void) { mNext = this; }
void SetState(State aState);
bool Matches(const Ip6::MessageInfo &aInfo) { return mMessageInfo.HasSamePeerAddrAndPort(aInfo); }
void HandleTransportReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
bool Matches(const Ip6::MessageInfo &aInfo) const { return mMessageInfo.HasSamePeerAddrAndPort(aInfo); }
bool Matches(State aState) const { return (mState == aState); }
void Accept(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void HandleTransportReceive(Message &aMessage);
Error Setup(void);
void Disconnect(ConnectEvent aEvent);
void HandleTimer(TimeMilli aNow);
@@ -262,11 +277,13 @@ private:
#endif
bool mTimerSet : 1;
bool mIsServer : 1;
State mState;
Message::SubType mMessageSubType;
ConnectEvent mConnectEvent;
TimeMilli mTimerIntermediate;
TimeMilli mTimerFinish;
SecureSession *mNext;
SecureTransport &mTransport;
Message *mReceiveMessage;
Ip6::MessageInfo mMessageInfo;
@@ -306,6 +323,30 @@ public:
*/
typedef void (*AutoCloseCallback)(void *aContext);
/**
* Callback to accept a new session connection request, providing the secure session to use.
*
* This method returns a pointer to a new `SecureSession` to use for the new session. The `SecureTransport` takes
* over the ownership of the given `SecureSession`. Once the session is disconnected and removed from the transport,
* the secure transport signals this using the `RemoveSessionCallback` callback, where ownership is released.
*
* `nullptr` can be returned to reject the new session connection request.
*
* @param[in] aContex A pointer to arbitrary context information.
* @param[in] aMessageInfo The message info from the new session connection request message.
*
* @returns A pointer to `SecureSession` to use for new session or `nullptr` if new connection is rejected.
*/
typedef SecureSession *(*AcceptCallback)(void *aContext, const Ip6::MessageInfo &aMessageInfo);
/**
* Callback to signal a session is removed, releasing the ownership of the session (by `SecureTransport`).
*
* @param[in] aContex A pointer to arbitrary context information.
* @param[in] aSesssion The session being removed.
*/
typedef void (*RemoveSessionCallback)(void *aContext, SecureSession &aSesssion);
#if OPENTHREAD_CONFIG_TLS_API_ENABLE
/**
* Represents an API extension for a `SecureTransport` (DTLS or TLS).
@@ -560,6 +601,25 @@ public:
*/
Error SetMaxConnectionAttempts(uint16_t aMaxAttempts, AutoCloseCallback aCallback, void *aContext);
/**
* Sets the `AcceptCallback` used to accept new session connection requests.
*
* @param[in] aCallback The `AcceptCallback`.
* @param[in] aConext A pointer to arbitrary context to use with `AcceptCallback`.
*/
void SetAcceptCallback(AcceptCallback aCallback, void *aContext) { mAcceptCallback.Set(aCallback, aContext); }
/**
* Sets the `RemoveSessionCallback` used to signal when a session is removed.
*
* @param[in] aCallback The `RemoveSessionCallback`.
* @param[in] aConext A pointer to arbitrary context to use with `RemoveSessionCallback`.
*/
void SetRemoveSessionCallback(RemoveSessionCallback aCallback, void *aContext)
{
mRemoveSessionCallback.Set(aCallback, aContext);
}
/**
* Binds this DTLS to a UDP port.
*
@@ -629,11 +689,16 @@ public:
*/
void HandleReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
/**
* Get the list of sessions associated with the `SecureTransport`.
*
* @returns The list of associated sessions.
*/
LinkedList<SecureSession> &GetSessions(void) { return mSessions; }
protected:
SecureTransport(Instance &aInstance, LinkSecurityMode aLayerTwoSecurity, bool aDatagramTransport);
void SetSession(SecureSession &aSesssion) { mSession = &aSesssion; }
#if OPENTHREAD_CONFIG_TLS_API_ENABLE
void SetExtension(Extension &aExtension) { mExtension = &aExtension; }
#endif
@@ -655,6 +720,7 @@ private:
kUnspecifiedCipherSuite,
};
void RemoveDisconnectedSessions(void);
void DecremenetRemainingConnectionAttempts(void);
bool HasNoRemainingConnectionAttempts(void) const;
int Transmit(const unsigned char *aBuf,
@@ -700,6 +766,8 @@ private:
#endif // (MBEDTLS_VERSION_NUMBER >= 0x03000000)
#endif // MBEDTLS_SSL_EXPORT_KEYS
static void HandleUpdateTask(Tasklet &aTasklet);
void HandleUpdateTask(void);
static void HandleTimer(Timer &aTimer);
void HandleTimer(void);
@@ -721,21 +789,24 @@ private:
static const int kCipherSuites[][2];
bool mLayerTwoSecurity : 1;
bool mDatagramTransport : 1;
bool mIsOpen : 1;
bool mIsServer : 1;
bool mVerifyPeerCertificate : 1;
CipherSuite mCipherSuite;
uint8_t mPskLength;
uint16_t mMaxConnectionAttempts;
uint16_t mRemainingConnectionAttempts;
SecureSession *mSession;
TransportSocket mSocket;
uint8_t mPsk[kPskMaxLength];
TimerMilliContext mTimer;
Callback<AutoCloseCallback> mAutoCloseCallback;
Callback<TransportCallback> mTransportCallback;
bool mLayerTwoSecurity : 1;
bool mDatagramTransport : 1;
bool mIsOpen : 1;
bool mIsClosing : 1;
bool mVerifyPeerCertificate : 1;
CipherSuite mCipherSuite;
uint8_t mPskLength;
uint16_t mMaxConnectionAttempts;
uint16_t mRemainingConnectionAttempts;
LinkedList<SecureSession> mSessions;
TransportSocket mSocket;
uint8_t mPsk[kPskMaxLength];
TimerMilliContext mTimer;
TaskletContext mUpdateTask;
Callback<AutoCloseCallback> mAutoCloseCallback;
Callback<AcceptCallback> mAcceptCallback;
Callback<RemoveSessionCallback> mRemoveSessionCallback;
Callback<TransportCallback> mTransportCallback;
#if OPENTHREAD_CONFIG_TLS_API_ENABLE
Extension *mExtension;
#endif
@@ -767,9 +838,6 @@ public:
: SecureTransport(aInstance, aLayerTwoSecurity, /* aDatagramTransport */ true)
{
}
private:
void SetSession(Session &aSesssion) { SecureTransport::SetSession(aSesssion); }
};
/**
@@ -786,7 +854,6 @@ public:
Session(Transport &aTransport)
: SecureSession(aTransport)
{
aTransport.SetSession(*this);
}
/**
@@ -817,9 +884,13 @@ public:
: SecureTransport(aInstance, aLayerTwoSecurity, /* aDatagramTransport */ false)
, SecureSession(*static_cast<SecureTransport *>(this))
{
SetSession(*static_cast<SecureSession *>(this));
SetExtension(aExtension);
SetAcceptCallback(&HandleAccept, this);
}
private:
static SecureSession *HandleAccept(void *aContext, const Ip6::MessageInfo &aMessageInfo);
SecureSession *HandleAccept(void);
};
#endif
+14
View File
@@ -276,11 +276,25 @@ SecureAgent::SecureAgent(Instance &aInstance)
: Coap::Dtls::Transport(aInstance, kNoLinkSecurity)
, Coap::SecureSession(aInstance, static_cast<Coap::Dtls::Transport &>(*this))
{
SetAcceptCallback(&HandleDtlsAccept, this);
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
SetResourceHandler(&HandleResource);
#endif
}
MeshCoP::SecureSession *SecureAgent::HandleDtlsAccept(void *aContext, const Ip6::MessageInfo &aMessageInfo)
{
OT_UNUSED_VARIABLE(aMessageInfo);
return static_cast<SecureAgent *>(aContext)->HandleDtlsAccept();
}
Coap::SecureSession *SecureAgent::HandleDtlsAccept(void)
{
return IsSessionInUse() ? nullptr : static_cast<Coap::SecureSession *>(this);
}
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
bool SecureAgent::HandleResource(CoapBase &aCoapBase,
+3
View File
@@ -208,6 +208,9 @@ public:
explicit SecureAgent(Instance &aInstance);
private:
static MeshCoP::SecureSession *HandleDtlsAccept(void *aContext, const Ip6::MessageInfo &aMessageInfo);
Coap::SecureSession *HandleDtlsAccept(void);
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
static bool HandleResource(CoapBase &aCoapBase,
const char *aUriPath,
+277 -11
View File
@@ -48,6 +48,7 @@ static const uint8_t kPsk[] = {0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16};
static Dtls::Session::ConnectEvent sDtlsEvent[kMaxNodes];
static Array<uint8_t, kMessageSize> sDtlsLastReceive[kMaxNodes];
static bool sDtlsAutoClosed[kMaxNodes];
static uint32_t sHeapSessionsAllocated = 0;
const char *ConnectEventToString(Dtls::Session::ConnectEvent aEvent)
{
@@ -131,24 +132,113 @@ OwnedPtr<Message> PrepareMessage(Node &aNode)
return OwnedPtr<Message>(message);
}
class DtlsTransportAndSession : public InstanceLocator, public Dtls::Transport, public Dtls::Session
class DtlsTransportAndSingleSession : public InstanceLocator, public Dtls::Transport, public Dtls::Session
{
// A DTLS transport and single session
public:
explicit DtlsTransportAndSession(Node &aNode)
explicit DtlsTransportAndSingleSession(Node &aNode)
: InstanceLocator(aNode.GetInstance())
, Dtls::Transport(aNode.GetInstance(), kWithLinkSecurity)
, Dtls::Session(static_cast<Dtls::Transport &>(*this))
, mNode(aNode)
{
SetAcceptCallback(HandleAccept, this);
VerifyOrQuit(!IsSessionInUse());
}
private:
static MeshCoP::SecureSession *HandleAccept(void *aContext, const Ip6::MessageInfo &aMessageInfo)
{
return static_cast<DtlsTransportAndSingleSession *>(aContext)->HandleAccept();
}
Dtls::Session *HandleAccept(void)
{
Dtls::Session *session = IsSessionInUse() ? nullptr : static_cast<Dtls::Session *>(this);
Log(" node%u: HandleAccept(), %s", mNode.GetId(), (session != nullptr) ? "accepted" : "rejected");
return session;
}
Node &mNode;
};
void TestDtls(void)
class DtlsTransportAndHeapSession : public InstanceLocator, public Dtls::Transport
{
// A DTLS session with heap allocated sessions.
public:
explicit DtlsTransportAndHeapSession(Node &aNode)
: InstanceLocator(aNode.GetInstance())
, Dtls::Transport(aNode.GetInstance(), kWithLinkSecurity)
, mNode(aNode)
{
SetAcceptCallback(HandleAccept, this);
SetRemoveSessionCallback(HandleRemoveSession, this);
}
private:
class HeapDtlsSession : public Dtls::Session, public Heap::Allocatable<HeapDtlsSession>
{
friend Heap::Allocatable<HeapDtlsSession>;
private:
HeapDtlsSession(Dtls::Transport &aTransport)
: Dtls::Session(aTransport)
{
sHeapSessionsAllocated++;
}
};
static MeshCoP::SecureSession *HandleAccept(void *aContext, const Ip6::MessageInfo &aMessageInfo)
{
DtlsTransportAndHeapSession *transport;
HeapDtlsSession *session;
VerifyOrQuit(aContext != nullptr);
transport = static_cast<DtlsTransportAndHeapSession *>(aContext);
Log(" node%u: HandleAccept()", transport->mNode.GetId());
session = HeapDtlsSession::Allocate(*transport);
VerifyOrQuit(session != nullptr);
session->SetReceiveCallback(&ot::Nexus::HandleReceive, &transport->mNode);
session->SetConnectCallback(&ot::Nexus::HandleConnectEvent, &transport->mNode);
return session;
}
static void HandleRemoveSession(void *aContext, MeshCoP::SecureSession &aSesssion)
{
DtlsTransportAndHeapSession *transport;
VerifyOrQuit(aContext != nullptr);
transport = static_cast<DtlsTransportAndHeapSession *>(aContext);
Log(" node%u: HandleRemoveSession()", transport->mNode.GetId());
VerifyOrQuit(sHeapSessionsAllocated > 0);
static_cast<HeapDtlsSession &>(aSesssion).Free();
sHeapSessionsAllocated--;
}
private:
Node &mNode;
};
void TestDtlsSingleSession(void)
{
Core nexus;
Node &node0 = nexus.CreateNode();
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
Log("------------------------------------------------------------------------------------------------------");
Log("TestDtlsSingleSession");
nexus.AdvanceTime(0);
// Form the topology: node0 leader, with node1 & node2 as its FTD children
@@ -167,13 +257,11 @@ void TestDtls(void)
nexus.AdvanceTime(20 * Time::kOneSecondInMsec);
VerifyOrQuit(node2.Get<Mle::Mle>().IsChild());
Log("------------------------------------------------------------------------------------------------------");
{
DtlsTransportAndSession dtls0(node0);
DtlsTransportAndSession dtls1(node1);
DtlsTransportAndSession dtls2(node2);
Ip6::SockAddr sockAddr;
DtlsTransportAndSingleSession dtls0(node0);
DtlsTransportAndSingleSession dtls1(node1);
DtlsTransportAndSingleSession dtls2(node2);
Ip6::SockAddr sockAddr;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Start DTLS (server) on node0 bound to port %u", kUdpPort);
@@ -370,7 +458,7 @@ void TestDtls(void)
SuccessOrQuit(dtls1.Connect(sockAddr));
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::Session::kDisconnectedMaxAttempts);
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::Session::kDisconnectedError);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::Session::kDisconnectedError);
VerifyOrQuit(sDtlsAutoClosed[node0.GetId()]);
@@ -383,12 +471,190 @@ void TestDtls(void)
}
}
void TestDtlsMultiSession(void)
{
Core nexus;
Node &node0 = nexus.CreateNode();
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
Log("------------------------------------------------------------------------------------------------------");
Log("TestDtlsMultiSession");
nexus.AdvanceTime(0);
// Form the topology: node0 leader, with node1 & node2 as its FTD children
node0.Form();
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
SuccessOrQuit(node1.Get<Mle::MleRouter>().SetRouterEligible(false));
node1.Join(node0);
nexus.AdvanceTime(20 * Time::kOneSecondInMsec);
VerifyOrQuit(node1.Get<Mle::Mle>().IsChild());
SuccessOrQuit(node2.Get<Mle::MleRouter>().SetRouterEligible(false));
node2.Join(node0);
nexus.AdvanceTime(20 * Time::kOneSecondInMsec);
VerifyOrQuit(node2.Get<Mle::Mle>().IsChild());
{
DtlsTransportAndHeapSession dtls0(node0);
DtlsTransportAndSingleSession dtls1(node1);
DtlsTransportAndSingleSession dtls2(node2);
Ip6::SockAddr sockAddr;
uint16_t numSessions;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Start DTLS (server) on node0 bound to port %u", kUdpPort);
SuccessOrQuit(dtls0.SetPsk(kPsk, sizeof(kPsk)));
SuccessOrQuit(dtls0.Open());
SuccessOrQuit(dtls0.Bind(kUdpPort));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls0.GetUdpPort() == kUdpPort);
sockAddr.SetAddress(node0.Get<Mle::Mle>().GetMeshLocalRloc());
sockAddr.SetPort(kUdpPort);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Establish a DTLS connection with node 0 from node1");
memset(sDtlsEvent, Dtls::Session::kDisconnectedError, sizeof(sDtlsEvent));
SuccessOrQuit(dtls1.SetPsk(kPsk, sizeof(kPsk)));
dtls1.SetReceiveCallback(HandleReceive, &node1);
dtls1.SetConnectCallback(HandleConnectEvent, &node1);
SuccessOrQuit(dtls1.Open());
SuccessOrQuit(dtls1.Connect(sockAddr));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls1.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::Session::kConnected);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::Session::kConnected);
numSessions = 0;
for (MeshCoP::SecureSession &session : dtls0.GetSessions())
{
VerifyOrQuit(session.IsConnected());
numSessions++;
}
VerifyOrQuit(numSessions == 1);
VerifyOrQuit(sHeapSessionsAllocated == 1);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Establish a second DTLS connection with node0 from node2");
memset(sDtlsEvent, Dtls::Session::kDisconnectedError, sizeof(sDtlsEvent));
SuccessOrQuit(dtls2.SetPsk(kPsk, sizeof(kPsk)));
dtls2.SetReceiveCallback(HandleReceive, &node2);
dtls2.SetConnectCallback(HandleConnectEvent, &node2);
SuccessOrQuit(dtls2.Open());
SuccessOrQuit(dtls2.Connect(sockAddr));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls2.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::Session::kConnected);
VerifyOrQuit(sDtlsEvent[node2.GetId()] == Dtls::Session::kConnected);
numSessions = 0;
for (MeshCoP::SecureSession &session : dtls0.GetSessions())
{
VerifyOrQuit(session.IsConnected());
numSessions++;
}
VerifyOrQuit(numSessions == 2);
VerifyOrQuit(sHeapSessionsAllocated == 2);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disconnect from node1 - validate the disconnect events");
dtls1.Disconnect();
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!dtls1.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::Session::kDisconnectedPeerClosed);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::Session::kDisconnectedLocalClosed);
numSessions = 0;
for (MeshCoP::SecureSession &session : dtls0.GetSessions())
{
VerifyOrQuit(session.IsConnected());
numSessions++;
}
VerifyOrQuit(numSessions == 1);
VerifyOrQuit(sHeapSessionsAllocated == 1);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disconnect session with node2 from node0 (server) - validate the disconnect events");
memset(sDtlsEvent, Dtls::Session::kConnected, sizeof(sDtlsEvent));
dtls0.GetSessions().GetHead()->Disconnect();
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
VerifyOrQuit(!dtls2.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::Session::kDisconnectedLocalClosed);
VerifyOrQuit(sDtlsEvent[node2.GetId()] == Dtls::Session::kDisconnectedPeerClosed);
VerifyOrQuit(dtls0.GetSessions().IsEmpty());
VerifyOrQuit(sHeapSessionsAllocated == 0);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Establish two DTLS connections from node1 and node2 at the same time");
memset(sDtlsEvent, Dtls::Session::kDisconnectedError, sizeof(sDtlsEvent));
SuccessOrQuit(dtls1.Connect(sockAddr));
SuccessOrQuit(dtls2.Connect(sockAddr));
nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
VerifyOrQuit(dtls1.IsConnected());
VerifyOrQuit(dtls2.IsConnected());
VerifyOrQuit(sDtlsEvent[node0.GetId()] == Dtls::Session::kConnected);
VerifyOrQuit(sDtlsEvent[node1.GetId()] == Dtls::Session::kConnected);
VerifyOrQuit(sDtlsEvent[node2.GetId()] == Dtls::Session::kConnected);
numSessions = 0;
for (MeshCoP::SecureSession &session : dtls0.GetSessions())
{
VerifyOrQuit(session.IsConnected());
numSessions++;
}
VerifyOrQuit(numSessions == 2);
VerifyOrQuit(sHeapSessionsAllocated == 2);
}
}
} // namespace Nexus
} // namespace ot
int main(void)
{
ot::Nexus::TestDtls();
ot::Nexus::TestDtlsSingleSession();
ot::Nexus::TestDtlsMultiSession();
printf("All tests passed\n");
return 0;
}