mirror of
https://github.com/espressif/openthread.git
synced 2026-07-09 13:50:28 +00:00
5b9a1c0467
This commit adds support for DNS Stateful Operations (DSO) as specified in RFC 8490. It adds `platform/dso_transport.hpp` header file which defines the platform APIs/callbacks for DSO transport layer (e.g., DSN-over-TLS or DNS-over-TCP). The `Dso` module handles establishing connection with a peer, acting either as a DSO client or server, establishing a DSO session over a connection, and then sending and processing DSO request, response, and unidirectional messages (including support for DSO TLV formats). The `Dso` module also manages the session life cycle and timeouts, namely the "Inactivity" and "Keep Alive" timeouts (including sending and processing of Keep Alive messages when needed). It also handles adding encryption padding before sending a message. It implements the padding policy "Random-Block-Length Padding" from RFC 8467. This commit also adds a detailed unit test `test_dso` covering the behavior (including corner cases) of the `Dso` implementation. The unit test provides an implementation of the DSO platform APIs which emulate the DSO transport layer. It also includes a simplified alarm platform implementation (emulating timers and allowing time to advance in the unit test). These allow the unit test to cover more complicated situations and behaviors (timeouts, failures, etc).
1241 lines
50 KiB
C++
1241 lines
50 KiB
C++
/*
|
|
* Copyright (c) 2021, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <openthread/config.h>
|
|
|
|
#include "test_platform.h"
|
|
#include "test_util.hpp"
|
|
|
|
#include "common/arg_macros.hpp"
|
|
#include "common/array.hpp"
|
|
#include "common/as_core_type.hpp"
|
|
#include "common/instance.hpp"
|
|
#include "net/dns_dso.hpp"
|
|
|
|
#if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
|
|
|
|
extern "C" {
|
|
|
|
static uint32_t sNow = 0;
|
|
static uint32_t sAlarmTime;
|
|
static bool sAlarmOn = false;
|
|
static otInstance *sInstance;
|
|
|
|
// Logs a message and adds current time (sNow) as "<hours>:<min>:<secs>.<msec>"
|
|
#define Log(...) \
|
|
printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \
|
|
(sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__))
|
|
|
|
void otPlatAlarmMilliStop(otInstance *)
|
|
{
|
|
sAlarmOn = false;
|
|
}
|
|
|
|
void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
|
|
{
|
|
sAlarmOn = true;
|
|
sAlarmTime = aT0 + aDt;
|
|
|
|
Log(" otPlatAlarmMilliStartAt(time:%u.%03u, dt:%u.%03u)", sAlarmTime / 1000, sAlarmTime % 1000,
|
|
(sAlarmTime - sNow) / 1000, (sAlarmTime - sNow) % 1000);
|
|
}
|
|
|
|
uint32_t otPlatAlarmMilliGetNow(void)
|
|
{
|
|
return sNow;
|
|
}
|
|
|
|
} // extern "C"
|
|
|
|
void AdvanceTime(uint32_t aDuration)
|
|
{
|
|
uint32_t time = sNow + aDuration;
|
|
|
|
Log(" AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000);
|
|
|
|
while (sAlarmTime <= time)
|
|
{
|
|
sNow = sAlarmTime;
|
|
otPlatAlarmMilliFired(sInstance);
|
|
}
|
|
|
|
sNow = time;
|
|
}
|
|
|
|
namespace ot {
|
|
namespace Dns {
|
|
|
|
OT_TOOL_PACKED_BEGIN
|
|
class TestTlv : public Dso::Tlv
|
|
{
|
|
public:
|
|
static constexpr Type kType = 0xf800;
|
|
|
|
void Init(uint8_t aValue)
|
|
{
|
|
Tlv::Init(kType, sizeof(*this) - sizeof(Tlv));
|
|
mValue = aValue;
|
|
}
|
|
|
|
bool IsValid(void) const { return GetSize() >= sizeof(*this); }
|
|
uint8_t GetValue(void) const { return mValue; }
|
|
|
|
private:
|
|
uint8_t mValue;
|
|
|
|
} OT_TOOL_PACKED_END;
|
|
|
|
extern "C" void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage);
|
|
|
|
class Connection : public Dso::Connection
|
|
{
|
|
friend void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage);
|
|
|
|
public:
|
|
explicit Connection(Instance & aInstance,
|
|
const char * aName,
|
|
const Ip6::SockAddr &aLocalSockAddr,
|
|
const Ip6::SockAddr &aPeerSockAddr)
|
|
: Dso::Connection(aInstance, aPeerSockAddr, sCallbacks)
|
|
, mName(aName)
|
|
, mLocalSockAddr(aLocalSockAddr)
|
|
{
|
|
ClearTestFlags();
|
|
}
|
|
|
|
const char * GetName(void) const { return mName; }
|
|
const Ip6::SockAddr &GetLocalSockAddr(void) const { return mLocalSockAddr; }
|
|
|
|
void ClearTestFlags(void)
|
|
{
|
|
mDidGetConnectedSignal = false;
|
|
mDidGetSessionEstablishedSignal = false;
|
|
mDidGetDisconnectSignal = false;
|
|
mDidSendMessage = false;
|
|
mDidReceiveMessage = false;
|
|
mDidProcessRequest = false;
|
|
mDidProcessUnidirectional = false;
|
|
mDidProcessResponse = false;
|
|
}
|
|
|
|
bool DidGetConnectedSignal(void) const { return mDidGetConnectedSignal; }
|
|
bool DidGetSessionEstablishedSignal(void) const { return mDidGetSessionEstablishedSignal; }
|
|
bool DidGetDisconnectSignal(void) const { return mDidGetDisconnectSignal; }
|
|
bool DidSendMessage(void) const { return mDidSendMessage; }
|
|
bool DidReceiveMessage(void) const { return mDidReceiveMessage; }
|
|
bool DidProcessRequest(void) const { return mDidProcessRequest; }
|
|
bool DidProcessUnidirectional(void) const { return mDidProcessUnidirectional; }
|
|
bool DidProcessResponse(void) const { return mDidProcessResponse; }
|
|
|
|
uint8_t GetLastRxTestTlvValue(void) const { return mLastRxTestTlvValue; }
|
|
Dns::Header::Response GetLastRxResponseCode(void) const { return mLastRxResponseCode; }
|
|
|
|
void SendTestRequestMessage(uint8_t aValue = 0, uint32_t aResponseTimeout = Dso::kResponseTimeout)
|
|
{
|
|
MessageId messageId;
|
|
|
|
mLastTxTestTlvValue = aValue;
|
|
SuccessOrQuit(SendRequestMessage(PrepareTestMessage(aValue), messageId, aResponseTimeout));
|
|
}
|
|
|
|
void SendTestUnidirectionalMessage(uint8_t aValue = 0)
|
|
{
|
|
mLastTxTestTlvValue = aValue;
|
|
SuccessOrQuit(SendUnidirectionalMessage(PrepareTestMessage(aValue)));
|
|
}
|
|
|
|
private:
|
|
Message &PrepareTestMessage(uint8_t aValue)
|
|
{
|
|
TestTlv testTlv;
|
|
Message *message = NewMessage();
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
testTlv.Init(aValue);
|
|
SuccessOrQuit(message->Append(testTlv));
|
|
|
|
return *message;
|
|
}
|
|
|
|
void ParseTestMessage(const Message &aMessage)
|
|
{
|
|
TestTlv testTlv;
|
|
Dso::Tlv tlv;
|
|
uint16_t offset = aMessage.GetOffset();
|
|
|
|
// Test message MUST only contain Test TLV and Encryption
|
|
// Padding TLV.
|
|
|
|
SuccessOrQuit(aMessage.Read(offset, testTlv));
|
|
VerifyOrQuit(testTlv.GetType() == TestTlv::kType);
|
|
VerifyOrQuit(testTlv.IsValid());
|
|
offset += testTlv.GetSize();
|
|
mLastRxTestTlvValue = testTlv.GetValue();
|
|
|
|
SuccessOrQuit(aMessage.Read(offset, tlv));
|
|
VerifyOrQuit(tlv.GetType() == Dso::Tlv::kEncryptionPaddingType);
|
|
offset += tlv.GetSize();
|
|
|
|
VerifyOrQuit(offset == aMessage.GetLength());
|
|
}
|
|
|
|
void SendTestResponseMessage(MessageId aResponseId, uint8_t aValue)
|
|
{
|
|
mLastTxTestTlvValue = aValue;
|
|
SuccessOrQuit(SendResponseMessage(PrepareTestMessage(aValue), aResponseId));
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Callback methods
|
|
|
|
void HandleConnected(void) { mDidGetConnectedSignal = true; }
|
|
void HandleSessionEstablished(void) { mDidGetSessionEstablishedSignal = true; }
|
|
void HandleDisconnected(void) { mDidGetDisconnectSignal = true; }
|
|
|
|
Error ProcessRequestMessage(MessageId aMessageId, const Message &aMessage, Dso::Tlv::Type aPrimaryTlvType)
|
|
{
|
|
Error error = kErrorNone;
|
|
|
|
Log(" ProcessRequestMessage(primaryTlv:0x%04x) on %s", aPrimaryTlvType, mName);
|
|
mDidProcessRequest = true;
|
|
|
|
VerifyOrExit(aPrimaryTlvType == TestTlv::kType, error = kErrorNotFound);
|
|
ParseTestMessage(aMessage);
|
|
SendTestResponseMessage(aMessageId, mLastRxTestTlvValue);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error ProcessUnidirectionalMessage(const Message &aMessage, Dso::Tlv::Type aPrimaryTlvType)
|
|
{
|
|
Log(" ProcessUnidirectionalMessage(primaryTlv:0x%04x) on %s", aPrimaryTlvType, mName);
|
|
mDidProcessUnidirectional = true;
|
|
|
|
if (aPrimaryTlvType == TestTlv::kType)
|
|
{
|
|
ParseTestMessage(aMessage);
|
|
}
|
|
|
|
return kErrorNone;
|
|
}
|
|
|
|
Error ProcessResponseMessage(const Dns::Header &aHeader,
|
|
const Message & aMessage,
|
|
Dso::Tlv::Type aResponseTlvType,
|
|
Dso::Tlv::Type aRequestTlvType)
|
|
{
|
|
Error error = kErrorNone;
|
|
|
|
mDidProcessResponse = true;
|
|
mLastRxResponseCode = aHeader.GetResponseCode();
|
|
Log(" ProcessResponseMessage(responseTlv:0x%04x) on %s (response-Code:%u) ", aResponseTlvType, mName,
|
|
mLastRxResponseCode);
|
|
|
|
VerifyOrExit(mLastRxResponseCode == Dns::Header::kResponseSuccess);
|
|
|
|
// During test we only expect a Test TLV response with
|
|
// a matching TLV value to what was sent last.
|
|
|
|
VerifyOrQuit(aResponseTlvType == TestTlv::kType);
|
|
VerifyOrQuit(aRequestTlvType == TestTlv::kType);
|
|
ParseTestMessage(aMessage);
|
|
VerifyOrQuit(mLastRxTestTlvValue == mLastTxTestTlvValue);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
static void HandleConnected(Dso::Connection &aConnection)
|
|
{
|
|
static_cast<Connection &>(aConnection).HandleConnected();
|
|
}
|
|
|
|
static void HandleSessionEstablished(Dso::Connection &aConnection)
|
|
{
|
|
static_cast<Connection &>(aConnection).HandleSessionEstablished();
|
|
}
|
|
|
|
static void HandleDisconnected(Dso::Connection &aConnection)
|
|
{
|
|
static_cast<Connection &>(aConnection).HandleDisconnected();
|
|
}
|
|
|
|
static Error ProcessRequestMessage(Dso::Connection &aConnection,
|
|
MessageId aMessageId,
|
|
const Message & aMessage,
|
|
Dso::Tlv::Type aPrimaryTlvType)
|
|
{
|
|
return static_cast<Connection &>(aConnection).ProcessRequestMessage(aMessageId, aMessage, aPrimaryTlvType);
|
|
}
|
|
|
|
static Error ProcessUnidirectionalMessage(Dso::Connection &aConnection,
|
|
const Message & aMessage,
|
|
Dso::Tlv::Type aPrimaryTlvType)
|
|
{
|
|
return static_cast<Connection &>(aConnection).ProcessUnidirectionalMessage(aMessage, aPrimaryTlvType);
|
|
}
|
|
|
|
static Error ProcessResponseMessage(Dso::Connection & aConnection,
|
|
const Dns::Header &aHeader,
|
|
const Message & aMessage,
|
|
Dso::Tlv::Type aResponseTlvType,
|
|
Dso::Tlv::Type aRequestTlvType)
|
|
{
|
|
return static_cast<Connection &>(aConnection)
|
|
.ProcessResponseMessage(aHeader, aMessage, aResponseTlvType, aRequestTlvType);
|
|
}
|
|
|
|
const char * mName;
|
|
Ip6::SockAddr mLocalSockAddr;
|
|
bool mDidGetConnectedSignal;
|
|
bool mDidGetSessionEstablishedSignal;
|
|
bool mDidGetDisconnectSignal;
|
|
bool mDidSendMessage;
|
|
bool mDidReceiveMessage;
|
|
bool mDidProcessRequest;
|
|
bool mDidProcessUnidirectional;
|
|
bool mDidProcessResponse;
|
|
uint8_t mLastTxTestTlvValue;
|
|
uint8_t mLastRxTestTlvValue;
|
|
Dns::Header::Response mLastRxResponseCode;
|
|
|
|
static Callbacks sCallbacks;
|
|
};
|
|
|
|
Dso::Connection::Callbacks Connection::sCallbacks(Connection::HandleConnected,
|
|
Connection::HandleSessionEstablished,
|
|
Connection::HandleDisconnected,
|
|
Connection::ProcessRequestMessage,
|
|
Connection::ProcessUnidirectionalMessage,
|
|
Connection::ProcessResponseMessage);
|
|
|
|
static constexpr uint16_t kMaxConnections = 5;
|
|
|
|
static Array<Connection *, kMaxConnections> sConnections;
|
|
|
|
static Connection *FindPeerConnection(const Connection &aConnetion)
|
|
{
|
|
Connection *peerConn = nullptr;
|
|
|
|
for (Connection *conn : sConnections)
|
|
{
|
|
if (conn->GetLocalSockAddr() == aConnetion.GetPeerSockAddr())
|
|
{
|
|
peerConn = conn;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return peerConn;
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
static bool sDsoListening = false;
|
|
|
|
// This test flag indicates whether the `otPlatDso` API should
|
|
// forward a sent message to the peer connection. It can be set to
|
|
// `false` to drop the messages to test timeout behaviors on the
|
|
// peer.
|
|
static bool sTestDsoForwardMessageToPeer = true;
|
|
|
|
// This test flag indicate whether when disconnecting a connection
|
|
// (using `otPlatDsoDisconnect()` to signal the peer connection about
|
|
// the disconnect. Default behavior is set to `true`. It can be set
|
|
// to `false` to test certain timeout behavior on peer side.
|
|
static bool sTestDsoSignalDisconnectToPeer = true;
|
|
|
|
void otPlatDsoEnableListening(otInstance *, bool aEnable)
|
|
{
|
|
Log(" otPlatDsoEnableListening(%s)", aEnable ? "true" : "false");
|
|
sDsoListening = aEnable;
|
|
}
|
|
|
|
void otPlatDsoConnect(otPlatDsoConnection *aConnection, const otSockAddr *aPeerSockAddr)
|
|
{
|
|
Connection & conn = *static_cast<Connection *>(aConnection);
|
|
Connection * peerConn = nullptr;
|
|
const Ip6::SockAddr &peerSockAddr = AsCoreType(aPeerSockAddr);
|
|
|
|
Log(" otPlatDsoConnect(%s, aPeer:0x%04x)", conn.GetName(), peerSockAddr.GetPort());
|
|
|
|
VerifyOrQuit(conn.GetPeerSockAddr() == peerSockAddr);
|
|
VerifyOrQuit(conn.GetState() == Connection::kStateConnecting);
|
|
|
|
if (!sDsoListening)
|
|
{
|
|
Log(" Server is not listening");
|
|
ExitNow();
|
|
}
|
|
|
|
peerConn = static_cast<Connection *>(otPlatDsoAccept(otPlatDsoGetInstance(aConnection), aPeerSockAddr));
|
|
|
|
if (peerConn == nullptr)
|
|
{
|
|
Log(" Request rejected");
|
|
ExitNow();
|
|
}
|
|
|
|
Log(" Request accepted");
|
|
VerifyOrQuit(peerConn->GetState() == Connection::kStateConnecting);
|
|
|
|
Log(" Signalling `Connected` on peer connection (%s)", peerConn->GetName());
|
|
otPlatDsoHandleConnected(peerConn);
|
|
|
|
Log(" Signalling `Connected` on connection (%s)", conn.GetName());
|
|
otPlatDsoHandleConnected(aConnection);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage)
|
|
{
|
|
Connection &conn = *static_cast<Connection *>(aConnection);
|
|
Connection *peerConn = nullptr;
|
|
|
|
Log(" otPlatDsoSend(%s), message-len:%u", conn.GetName(), AsCoreType(aMessage).GetLength());
|
|
|
|
VerifyOrQuit(conn.GetState() != Connection::kStateDisconnected);
|
|
VerifyOrQuit(conn.GetState() != Connection::kStateConnecting);
|
|
conn.mDidSendMessage = true;
|
|
|
|
if (sTestDsoForwardMessageToPeer)
|
|
{
|
|
peerConn = FindPeerConnection(conn);
|
|
VerifyOrQuit(peerConn != nullptr);
|
|
|
|
VerifyOrQuit(peerConn->GetState() != Connection::kStateDisconnected);
|
|
VerifyOrQuit(peerConn->GetState() != Connection::kStateConnecting);
|
|
|
|
Log(" Sending the message to peer connection (%s)", peerConn->GetName());
|
|
|
|
peerConn->mDidReceiveMessage = true;
|
|
otPlatDsoHandleReceive(peerConn, aMessage);
|
|
}
|
|
else
|
|
{
|
|
Log(" Dropping the message");
|
|
}
|
|
}
|
|
|
|
void otPlatDsoDisconnect(otPlatDsoConnection *aConnection, otPlatDsoDisconnectMode aMode)
|
|
{
|
|
Connection &conn = *static_cast<Connection *>(aConnection);
|
|
Connection *peerConn = nullptr;
|
|
|
|
Log(" otPlatDsoDisconnect(%s, mode:%s)", conn.GetName(),
|
|
(aMode == OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE) ? "close" : "abort");
|
|
|
|
VerifyOrQuit(conn.GetState() == Connection::kStateDisconnected);
|
|
|
|
if (sTestDsoSignalDisconnectToPeer)
|
|
{
|
|
peerConn = FindPeerConnection(conn);
|
|
|
|
if (peerConn == nullptr)
|
|
{
|
|
Log(" No peer connection found");
|
|
}
|
|
else if (peerConn->GetState() == Connection::kStateDisconnected)
|
|
{
|
|
Log(" Peer connection (%s) already disconnected", peerConn->GetName());
|
|
}
|
|
else
|
|
{
|
|
Log(" Signaling `Disconnected` on peer connection (%s)", peerConn->GetName());
|
|
otPlatDsoHandleDisconnected(peerConn, aMode);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // extern "C"
|
|
|
|
Dso::Connection *AcceptConnection(Instance &aInstance, const Ip6::SockAddr &aPeerSockAddr)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
Connection *rval = nullptr;
|
|
|
|
Log(" AcceptConnection(peer:0x%04x)", aPeerSockAddr.GetPort());
|
|
|
|
for (Connection *conn : sConnections)
|
|
{
|
|
if (conn->GetLocalSockAddr() == aPeerSockAddr)
|
|
{
|
|
VerifyOrQuit(conn->GetState() == Connection::kStateDisconnected);
|
|
rval = conn;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rval != nullptr)
|
|
{
|
|
Log(" Accepting and returning connection %s", rval->GetName());
|
|
}
|
|
else
|
|
{
|
|
Log(" Rejecting");
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static constexpr uint8_t kKeepAliveTestIterations = 3;
|
|
|
|
static void VerifyKeepAliveExchange(Connection &aClientConn,
|
|
Connection &aServerConn,
|
|
uint32_t aKeepAliveInterval,
|
|
uint8_t aNumIterations = kKeepAliveTestIterations)
|
|
{
|
|
for (uint8_t n = 0; n < aNumIterations; n++)
|
|
{
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Test Keep Alive message exchange, iter %d", n + 1);
|
|
|
|
aClientConn.ClearTestFlags();
|
|
aServerConn.ClearTestFlags();
|
|
|
|
AdvanceTime(aKeepAliveInterval - 1);
|
|
VerifyOrQuit(!aClientConn.DidSendMessage());
|
|
VerifyOrQuit(!aServerConn.DidReceiveMessage());
|
|
Log("No message before keep alive timeout");
|
|
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(aClientConn.DidSendMessage());
|
|
VerifyOrQuit(aServerConn.DidReceiveMessage());
|
|
Log("KeepAlive message exchanged after keep alive time elapses");
|
|
}
|
|
}
|
|
|
|
void TestDso(void)
|
|
{
|
|
static constexpr uint16_t kPortA = 0xaaaa;
|
|
static constexpr uint16_t kPortB = 0xbbbb;
|
|
|
|
static constexpr Dso::Tlv::Type kUnknownTlvType = 0xf801;
|
|
|
|
static constexpr uint32_t kRetryDelayInterval = TimeMilli::SecToMsec(3600);
|
|
static constexpr uint32_t kLongResponseTimeout = Dso::kResponseTimeout + TimeMilli::SecToMsec(17);
|
|
|
|
Instance & instance = *static_cast<Instance *>(testInitInstance());
|
|
Ip6::SockAddr serverSockAddr(kPortA);
|
|
Ip6::SockAddr clientSockAddr(kPortB);
|
|
Connection serverConn(instance, "serverConn", serverSockAddr, clientSockAddr);
|
|
Connection clientConn(instance, "clinetConn", clientSockAddr, serverSockAddr);
|
|
Message * message;
|
|
Dso::Tlv tlv;
|
|
Connection::MessageId messageId;
|
|
|
|
sNow = 0;
|
|
sInstance = &instance;
|
|
|
|
SuccessOrQuit(sConnections.PushBack(&serverConn));
|
|
SuccessOrQuit(sConnections.PushBack(&clientConn));
|
|
|
|
VerifyOrQuit(serverConn.GetPeerSockAddr() == clientSockAddr);
|
|
VerifyOrQuit(clientConn.GetPeerSockAddr() == serverSockAddr);
|
|
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
|
|
instance.Get<Dso>().StartListening(AcceptConnection);
|
|
|
|
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(clientSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(clientSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
|
|
|
|
Log("-------------------------------------------------------------------------------------------");
|
|
Log("Connect from client to server");
|
|
|
|
clientConn.Connect();
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateConnectedButSessionless);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
|
|
|
|
VerifyOrQuit(clientConn.IsClient());
|
|
VerifyOrQuit(!clientConn.IsServer());
|
|
|
|
VerifyOrQuit(!serverConn.IsClient());
|
|
VerifyOrQuit(serverConn.IsServer());
|
|
|
|
// Note that we find connection with a peer address
|
|
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == &clientConn);
|
|
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(clientSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(clientSockAddr) == &serverConn);
|
|
|
|
VerifyOrQuit(clientConn.DidGetConnectedSignal());
|
|
VerifyOrQuit(!clientConn.DidGetSessionEstablishedSignal());
|
|
VerifyOrQuit(!clientConn.DidGetDisconnectSignal());
|
|
|
|
VerifyOrQuit(serverConn.DidGetConnectedSignal());
|
|
VerifyOrQuit(!serverConn.DidGetSessionEstablishedSignal());
|
|
VerifyOrQuit(!serverConn.DidGetDisconnectSignal());
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Send keep alive message to establish connection");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
VerifyOrQuit(!clientConn.DidGetConnectedSignal());
|
|
VerifyOrQuit(clientConn.DidGetSessionEstablishedSignal());
|
|
VerifyOrQuit(!clientConn.DidGetDisconnectSignal());
|
|
|
|
VerifyOrQuit(!serverConn.DidGetConnectedSignal());
|
|
VerifyOrQuit(serverConn.DidGetSessionEstablishedSignal());
|
|
VerifyOrQuit(!serverConn.DidGetDisconnectSignal());
|
|
|
|
VerifyOrQuit(clientConn.GetKeepAliveInterval() == Dso::kDefaultTimeout);
|
|
VerifyOrQuit(clientConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
|
|
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kDefaultTimeout);
|
|
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Close connection");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
clientConn.Disconnect(Connection::kGracefullyClose, Connection::kReasonInactivityTimeout);
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
|
|
|
|
VerifyOrQuit(!clientConn.DidGetConnectedSignal());
|
|
VerifyOrQuit(!clientConn.DidGetSessionEstablishedSignal());
|
|
VerifyOrQuit(!clientConn.DidGetDisconnectSignal());
|
|
|
|
VerifyOrQuit(!serverConn.DidGetConnectedSignal());
|
|
VerifyOrQuit(!serverConn.DidGetSessionEstablishedSignal());
|
|
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
|
|
|
|
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(clientSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(clientSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
|
|
|
|
Log("-------------------------------------------------------------------------------------------");
|
|
Log("Connection timeout when server is not listening");
|
|
|
|
instance.Get<Dso>().StopListening();
|
|
|
|
clientConn.ClearTestFlags();
|
|
|
|
clientConn.Connect();
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateConnecting);
|
|
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == &clientConn);
|
|
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
|
|
|
|
AdvanceTime(Dso::kConnectingTimeout);
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonFailedToConnect);
|
|
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == nullptr);
|
|
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
|
|
|
|
VerifyOrQuit(!clientConn.DidGetConnectedSignal());
|
|
VerifyOrQuit(!clientConn.DidGetSessionEstablishedSignal());
|
|
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
|
|
|
|
Log("-------------------------------------------------------------------------------------------");
|
|
Log("Keep Alive Timeout behavior");
|
|
|
|
// Keep Alive timeout smaller than min value should be rejected.
|
|
VerifyOrQuit(clientConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kMinKeepAliveInterval - 1) == kErrorInvalidArgs);
|
|
|
|
instance.Get<Dso>().StartListening(AcceptConnection);
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kMinKeepAliveInterval));
|
|
|
|
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
|
|
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
|
|
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
|
|
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
|
|
VerifyOrQuit(clientConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
|
|
VerifyOrQuit(clientConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
|
|
|
|
VerifyKeepAliveExchange(clientConn, serverConn, Dso::kMinKeepAliveInterval);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Change Keep Alive interval on server");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kDefaultTimeout));
|
|
|
|
VerifyOrQuit(serverConn.DidSendMessage());
|
|
VerifyOrQuit(clientConn.DidReceiveMessage());
|
|
VerifyOrQuit(!clientConn.DidSendMessage());
|
|
|
|
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kDefaultTimeout);
|
|
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
|
|
VerifyOrQuit(clientConn.GetKeepAliveInterval() == Dso::kDefaultTimeout);
|
|
VerifyOrQuit(clientConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
|
|
|
|
VerifyKeepAliveExchange(clientConn, serverConn, Dso::kDefaultTimeout);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Keep Alive timer clear on message send or receive");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
AdvanceTime(Dso::kDefaultTimeout / 2);
|
|
|
|
clientConn.SendTestUnidirectionalMessage();
|
|
VerifyOrQuit(clientConn.DidSendMessage());
|
|
VerifyOrQuit(serverConn.DidReceiveMessage());
|
|
VerifyOrQuit(!serverConn.DidSendMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
Log("Sent unidirectional message (client->server) at half the keep alive interval");
|
|
VerifyKeepAliveExchange(clientConn, serverConn, Dso::kDefaultTimeout, 1);
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
AdvanceTime(Dso::kDefaultTimeout / 2);
|
|
|
|
serverConn.SendTestUnidirectionalMessage();
|
|
VerifyOrQuit(serverConn.DidSendMessage());
|
|
VerifyOrQuit(clientConn.DidReceiveMessage());
|
|
VerifyOrQuit(!clientConn.DidSendMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
Log("Sent unidirectional message (server->client) at half the keep alive interval");
|
|
VerifyKeepAliveExchange(clientConn, serverConn, Dso::kDefaultTimeout, 1);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Keep Alive timeout on server");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
Log("Drop all sent message (drop Keep Alive msg from client->server)");
|
|
sTestDsoForwardMessageToPeer = false;
|
|
|
|
AdvanceTime(Dso::kDefaultTimeout);
|
|
VerifyOrQuit(clientConn.DidSendMessage());
|
|
VerifyOrQuit(!serverConn.DidReceiveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
Log("Sever waits for twice the interval before Keep Alive timeout");
|
|
AdvanceTime(Dso::kDefaultTimeout);
|
|
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonKeepAliveTimeout);
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonPeerAborted);
|
|
Log("Server aborted connection on Keep Alive timeout");
|
|
sTestDsoForwardMessageToPeer = true;
|
|
|
|
Log("-------------------------------------------------------------------------------------------");
|
|
Log("Inactivity Timeout behavior");
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kMinKeepAliveInterval));
|
|
|
|
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
|
|
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
|
|
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
|
|
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
|
|
VerifyOrQuit(clientConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
|
|
VerifyOrQuit(clientConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
|
|
|
|
Log("Sending a unidirectional message should clear inactivity timer");
|
|
AdvanceTime(Dso::kDefaultTimeout / 2);
|
|
clientConn.SendTestUnidirectionalMessage();
|
|
|
|
AdvanceTime(Dso::kDefaultTimeout - 1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
Log("Client keeps the connection up to the inactivity timeout");
|
|
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
|
|
Log("Client closes the connection gracefully on inactivity timeout");
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Increasing inactivity timeout in middle");
|
|
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(10));
|
|
Log("After 10 sec elapses, change the inactivity timeout from 15 to 20 sec");
|
|
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(20), Dso::kMinKeepAliveInterval));
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(10) - 1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
Log("Client keeps the connection up to new 20 sec inactivity timeout");
|
|
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
|
|
Log("Client closes the connection gracefully on inactivity timeout of 20 sec");
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Decreasing inactivity timeout in middle");
|
|
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(10));
|
|
Log("After 10 sec elapses, change the inactivity timeout from 15 to 10 sec");
|
|
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(10), Dso::kMinKeepAliveInterval));
|
|
|
|
AdvanceTime(0);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
|
|
Log("Client closes the connection gracefully on new shorter inactivity timeout of 10 sec");
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Changing inactivity timeout from infinite to finite");
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kInfiniteTimeout));
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(6));
|
|
Log("After 6 sec, change the inactivity to infinite");
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(4));
|
|
Log("After 4 sec, change the inactivity timeout from infinite to 20 sec");
|
|
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(20), Dso::kInfiniteTimeout));
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(10) - 1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
|
|
Log("Client closes the connection gracefully after 20 sec since last activity");
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Tracking activity while inactivity timeout is infinite");
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(7));
|
|
Log("After 7 sec, send a test message, this clears inactivity timer");
|
|
serverConn.SendTestUnidirectionalMessage();
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(10));
|
|
Log("After 10 sec, change the inactivity timeout from infinite to 15 sec");
|
|
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(15), Dso::kInfiniteTimeout));
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(5) - 1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
|
|
Log("Client closes the connection gracefully after 15 sec since last activity");
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Inactivity timeout on server");
|
|
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kInfiniteTimeout));
|
|
|
|
Log("Wait for inactivity timeout and ensure client disconnect");
|
|
Log("Configure test for client not to signal its disconnect to server");
|
|
sTestDsoSignalDisconnectToPeer = false;
|
|
|
|
AdvanceTime(Dso::kDefaultTimeout);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
sTestDsoSignalDisconnectToPeer = true;
|
|
|
|
Log("Server should disconnect after twice the inactivity timeout");
|
|
AdvanceTime(Dso::kDefaultTimeout - 1);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Server reducing inactivity timeout to expired (on server)");
|
|
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kInfiniteTimeout));
|
|
|
|
AdvanceTime(TimeMilli::SecToMsec(10));
|
|
Log("After 11 sec elapses, change the inactivity timeout from 15 to 2 sec");
|
|
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(2), Dso::kMinKeepAliveInterval));
|
|
|
|
sTestDsoSignalDisconnectToPeer = false;
|
|
AdvanceTime(0);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
sTestDsoSignalDisconnectToPeer = true;
|
|
Log("Client closes the connection gracefully on expired timeout");
|
|
Log("Configure test for client not to signal its disconnect to server");
|
|
|
|
AdvanceTime(Dso::kMinServerInactivityWaitTime - 1);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
Log("Server wait for kMinServerInactivityWaitTime (5 sec) before closing on expired timeout");
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Long-lived operation");
|
|
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kInfiniteTimeout));
|
|
|
|
clientConn.SetLongLivedOperation(true);
|
|
serverConn.SetLongLivedOperation(true);
|
|
|
|
AdvanceTime(2 * Dso::kDefaultTimeout);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
clientConn.SetLongLivedOperation(false);
|
|
AdvanceTime(0);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
|
|
Log("-------------------------------------------------------------------------------------------");
|
|
Log("Request, response, and unidirectional message exchange");
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kDefaultTimeout));
|
|
clientConn.Connect();
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateConnectedButSessionless);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Establish connection using test message request/response");
|
|
clientConn.SendTestRequestMessage();
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.DidProcessRequest());
|
|
VerifyOrQuit(clientConn.DidProcessResponse());
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Send unidirectional test message");
|
|
|
|
serverConn.ClearTestFlags();
|
|
clientConn.SendTestUnidirectionalMessage(0x10);
|
|
VerifyOrQuit(serverConn.DidProcessUnidirectional());
|
|
VerifyOrQuit(serverConn.GetLastRxTestTlvValue() == 0x10);
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.SendTestUnidirectionalMessage(0x20);
|
|
VerifyOrQuit(clientConn.DidProcessUnidirectional());
|
|
VerifyOrQuit(clientConn.GetLastRxTestTlvValue() == 0x20);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Exchange request and response");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
serverConn.SendTestRequestMessage(0x30);
|
|
VerifyOrQuit(clientConn.DidProcessRequest());
|
|
VerifyOrQuit(!clientConn.DidProcessResponse());
|
|
VerifyOrQuit(!serverConn.DidProcessRequest());
|
|
VerifyOrQuit(serverConn.DidProcessResponse());
|
|
VerifyOrQuit(serverConn.GetLastRxTestTlvValue() == 0x30);
|
|
VerifyOrQuit(clientConn.GetLastRxTestTlvValue() == 0x30);
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
clientConn.SendTestRequestMessage(0x40);
|
|
VerifyOrQuit(!clientConn.DidProcessRequest());
|
|
VerifyOrQuit(clientConn.DidProcessResponse());
|
|
VerifyOrQuit(serverConn.DidProcessRequest());
|
|
VerifyOrQuit(!serverConn.DidProcessResponse());
|
|
VerifyOrQuit(serverConn.GetLastRxTestTlvValue() == 0x40);
|
|
VerifyOrQuit(clientConn.GetLastRxTestTlvValue() == 0x40);
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Send unknown TLV request");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
message = clientConn.NewMessage();
|
|
VerifyOrQuit(message != nullptr);
|
|
tlv.Init(kUnknownTlvType, 0);
|
|
SuccessOrQuit(message->Append(tlv));
|
|
SuccessOrQuit(clientConn.SendRequestMessage(*message, messageId));
|
|
|
|
VerifyOrQuit(!clientConn.DidProcessRequest());
|
|
VerifyOrQuit(clientConn.DidProcessResponse());
|
|
VerifyOrQuit(serverConn.DidProcessRequest());
|
|
VerifyOrQuit(!serverConn.DidProcessResponse());
|
|
VerifyOrQuit(clientConn.GetLastRxResponseCode() == Dns::Header::kDsoTypeNotImplemented);
|
|
Log("Received a response with DSO Type Unknown error code");
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Send unknown TLV unidirectional");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
message = clientConn.NewMessage();
|
|
VerifyOrQuit(message != nullptr);
|
|
tlv.Init(kUnknownTlvType, 0);
|
|
SuccessOrQuit(message->Append(tlv));
|
|
SuccessOrQuit(clientConn.SendUnidirectionalMessage(*message));
|
|
VerifyOrQuit(serverConn.DidProcessUnidirectional());
|
|
Log("Unknown TLV unidirectional is correctly ignored");
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Send malformed/invalid request");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
message = clientConn.NewMessage();
|
|
VerifyOrQuit(message != nullptr);
|
|
tlv.Init(Dso::Tlv::kEncryptionPaddingType, 0);
|
|
SuccessOrQuit(message->Append(tlv));
|
|
|
|
SuccessOrQuit(serverConn.SendRequestMessage(*message, messageId));
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonPeerMisbehavior);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerAborted);
|
|
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
|
|
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
|
|
Log("Client aborted on invalid request message");
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Response timeout during session establishment");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kResponseTimeout, Dso::kInfiniteTimeout));
|
|
clientConn.Connect();
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateConnectedButSessionless);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
|
|
|
|
sTestDsoForwardMessageToPeer = false;
|
|
clientConn.SendTestRequestMessage();
|
|
sTestDsoForwardMessageToPeer = true;
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateEstablishingSession);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
|
|
|
|
sTestDsoSignalDisconnectToPeer = false;
|
|
AdvanceTime(Dso::kResponseTimeout);
|
|
sTestDsoSignalDisconnectToPeer = true;
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonResponseTimeout);
|
|
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
|
|
Log("Client disconnected after response timeout");
|
|
|
|
AdvanceTime(Dso::kResponseTimeout);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
|
|
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
|
|
Log("Server disconnected after twice the inactivity timeout");
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
Log("Response timeout after session establishment");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
sTestDsoForwardMessageToPeer = false;
|
|
serverConn.SendTestRequestMessage();
|
|
sTestDsoForwardMessageToPeer = true;
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(Dso::kResponseTimeout - 1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonResponseTimeout);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonPeerAborted);
|
|
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
|
|
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
sTestDsoForwardMessageToPeer = false;
|
|
serverConn.SendTestRequestMessage(0, kLongResponseTimeout);
|
|
sTestDsoForwardMessageToPeer = true;
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(kLongResponseTimeout - 1);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
AdvanceTime(1);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonResponseTimeout);
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonPeerAborted);
|
|
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
|
|
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
|
|
|
|
Log("-------------------------------------------------------------------------------------------");
|
|
Log("Retry Delay message");
|
|
|
|
clientConn.ClearTestFlags();
|
|
serverConn.ClearTestFlags();
|
|
|
|
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
|
|
clientConn.Connect();
|
|
SuccessOrQuit(clientConn.SendKeepAliveMessage());
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
|
|
|
|
SuccessOrQuit(serverConn.SendRetryDelayMessage(kRetryDelayInterval, Dns::Header::kResponseServerFailure));
|
|
|
|
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
|
|
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
|
|
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonServerRetryDelayRequest);
|
|
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
|
|
|
|
VerifyOrQuit(clientConn.GetRetryDelay() == kRetryDelayInterval);
|
|
VerifyOrQuit(clientConn.GetRetryDelayErrorCode() == Dns::Header::kResponseServerFailure);
|
|
|
|
Log("End of test");
|
|
|
|
testFreeInstance(&instance);
|
|
}
|
|
|
|
} // namespace Dns
|
|
} // namespace ot
|
|
|
|
#endif // OPENTHREAD_CONFIG_DNS_DSO_ENABLE
|
|
|
|
int main(void)
|
|
{
|
|
#if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
|
|
ot::Dns::TestDso();
|
|
printf("All tests passed\n");
|
|
#else
|
|
printf("DSO feature is not enabled\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|