mirror of
https://github.com/espressif/openthread.git
synced 2026-09-23 17:37:42 +00:00
This commit introduces a new platform abstraction layer for TCP connections and listeners, enabling OpenThread to leverage platform- provided TCP stacks. The API is designed for asynchronous, event-driven environments and can easily support POSIX as well as various embedded network stacks. In the core, `Ip6::PlatTcp` and its nested `Connection` and `Listener` classes are introduced to manage these platform interactions, providing a clean C++ interface for the OpenThread core. The `PlatTcp` manager is integrated into the `Instance` class and utilizes `Tasklet` for asynchronous resource cleanup. Additionally, this commit adds a comprehensive unit test to verify the TCP platform abstraction and `Ip6::PlatTcp` implementation. The tests cover listener and connection lifecycles, data flow, flow control, incoming connection acceptance, and active object iteration.
734 lines
24 KiB
C++
734 lines
24 KiB
C++
/*
|
|
* Copyright (c) 2026, 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/as_core_type.hpp"
|
|
#include "instance/instance.hpp"
|
|
#include "net/plat_tcp.hpp"
|
|
|
|
namespace ot {
|
|
|
|
#if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
|
|
|
// Logs a message
|
|
#define Log(...) printf(OT_FIRST_ARG(__VA_ARGS__) "\n" OT_REST_ARGS(__VA_ARGS__))
|
|
|
|
using SockAddr = Ip6::PlatTcp::SockAddr;
|
|
using Listener = Ip6::PlatTcp::Listener;
|
|
using Connection = Ip6::PlatTcp::Connection;
|
|
|
|
struct ListenerInfo
|
|
{
|
|
Listener *mListener;
|
|
SockAddr mLocalSockAddr;
|
|
bool mEnabled;
|
|
};
|
|
|
|
static constexpr uint16_t kTxBufferSize = 1000;
|
|
|
|
struct ConnInfo
|
|
{
|
|
Connection *mConnection;
|
|
bool mConnected;
|
|
bool mConnecting;
|
|
bool mClosing;
|
|
bool mAborted;
|
|
bool mNotifyTxPending;
|
|
SockAddr mLocalSockAddr;
|
|
SockAddr mPeerSockAddr;
|
|
uint16_t mTxBufferUsed;
|
|
uint8_t mTxBuffer[kTxBufferSize];
|
|
};
|
|
|
|
static ListenerInfo sListenerInfo;
|
|
static ConnInfo sConnInfo;
|
|
|
|
static bool sAcceptHandlerCalled;
|
|
static bool sDeleteHandlerCalled;
|
|
static bool sEventHandlerCalled;
|
|
static Connection::Event sLastEvent;
|
|
|
|
static void ResetTestState(void)
|
|
{
|
|
ClearAllBytes(sListenerInfo);
|
|
ClearAllBytes(sConnInfo);
|
|
|
|
sAcceptHandlerCalled = false;
|
|
sDeleteHandlerCalled = false;
|
|
sEventHandlerCalled = false;
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// otPlatTcp
|
|
|
|
extern "C" {
|
|
|
|
otError otPlatTcpEnableListener(otPlatTcpListener *aListener, const otPlatTcpSockAddr *aLocalSockAddr)
|
|
{
|
|
sListenerInfo.mListener = AsCoreTypePtr(aListener);
|
|
sListenerInfo.mLocalSockAddr = AsCoreType(aLocalSockAddr);
|
|
sListenerInfo.mEnabled = true;
|
|
|
|
Log(" - otPlatTcpEnableListener(%s)", sListenerInfo.mLocalSockAddr.ToString().AsCString());
|
|
|
|
return kErrorNone;
|
|
}
|
|
|
|
void otPlatTcpDisableListener(otPlatTcpListener *aListener)
|
|
{
|
|
Log(" - otPlatTcpDisableListener()");
|
|
|
|
VerifyOrQuit(sListenerInfo.mListener == aListener);
|
|
sListenerInfo.mEnabled = false;
|
|
}
|
|
|
|
otError otPlatTcpConnect(otPlatTcpConnection *aConn,
|
|
const otPlatTcpSockAddr *aPeerSockAddr,
|
|
const otPlatTcpSockAddr *aLocalSockAddr)
|
|
{
|
|
sConnInfo.mConnection = AsCoreTypePtr(aConn);
|
|
sConnInfo.mConnecting = true;
|
|
sConnInfo.mPeerSockAddr = AsCoreType(aPeerSockAddr);
|
|
|
|
Log(" - otPlatTcpConnect(peerAddr:%s)", sConnInfo.mPeerSockAddr.ToString().AsCString());
|
|
|
|
if (aLocalSockAddr != nullptr)
|
|
{
|
|
sConnInfo.mLocalSockAddr = AsCoreType(aLocalSockAddr);
|
|
Log(" localAddr:%s", sConnInfo.mLocalSockAddr.ToString().AsCString());
|
|
}
|
|
|
|
return kErrorNone;
|
|
}
|
|
|
|
void otPlatTcpNotifyTxPending(otPlatTcpConnection *aConn)
|
|
{
|
|
Log(" - otPlatTcpNotifyTxPending()");
|
|
VerifyOrQuit(sConnInfo.mConnection == aConn);
|
|
sConnInfo.mNotifyTxPending = true;
|
|
}
|
|
|
|
uint16_t otPlatTcpSend(otPlatTcpConnection *aConn, const uint8_t *aBuffer, uint16_t aLength)
|
|
{
|
|
uint16_t copyLength;
|
|
|
|
VerifyOrQuit(sConnInfo.mConnection == aConn);
|
|
|
|
copyLength = Min<uint16_t>(aLength, kTxBufferSize - sConnInfo.mTxBufferUsed);
|
|
|
|
memcpy(&sConnInfo.mTxBuffer[sConnInfo.mTxBufferUsed], aBuffer, copyLength);
|
|
sConnInfo.mTxBufferUsed += copyLength;
|
|
|
|
Log(" - otPlatTcpSend(aLength:%u), copied:%u", aLength, copyLength);
|
|
|
|
return copyLength;
|
|
}
|
|
|
|
void otPlatTcpClose(otPlatTcpConnection *aConn)
|
|
{
|
|
Log(" - otPlatTcpClose()");
|
|
|
|
VerifyOrQuit(sConnInfo.mConnection == aConn);
|
|
sConnInfo.mClosing = true;
|
|
}
|
|
|
|
void otPlatTcpAbort(otPlatTcpConnection *aConn)
|
|
{
|
|
Log(" - otPlatTcpAbort()");
|
|
|
|
VerifyOrQuit(sConnInfo.mConnection == aConn);
|
|
sConnInfo.mAborted = true;
|
|
}
|
|
|
|
} // extern "C"
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Callbacks
|
|
|
|
static Connection *AcceptHandler(Listener &aListener, const SockAddr &aPeerSockAddr)
|
|
{
|
|
Log(" - AcceptHandler(peerAddr:%s)", aPeerSockAddr.ToString().AsCString());
|
|
|
|
OT_UNUSED_VARIABLE(aListener);
|
|
sAcceptHandlerCalled = true;
|
|
|
|
return sConnInfo.mConnection;
|
|
}
|
|
|
|
static void ListenerDeleteHandler(Listener &aListener)
|
|
{
|
|
Log(" - ListenerDeleteHandler()");
|
|
|
|
sDeleteHandlerCalled = true;
|
|
OT_UNUSED_VARIABLE(aListener);
|
|
}
|
|
|
|
static void ConnectionDeleteHandler(Connection &aConnection)
|
|
{
|
|
Log(" - ConnectionDeleteHandler()");
|
|
|
|
sDeleteHandlerCalled = true;
|
|
OT_UNUSED_VARIABLE(aConnection);
|
|
}
|
|
|
|
static void EventHandler(Connection &aConnection, Connection::Event aEvent)
|
|
{
|
|
Log(" - EventHandler(%s)", Connection::EventToString(aEvent));
|
|
|
|
sLastEvent = aEvent;
|
|
sEventHandlerCalled = true;
|
|
OT_UNUSED_VARIABLE(aConnection);
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void TestPlatTcpListener(void)
|
|
{
|
|
Instance *instance = testInitInstance();
|
|
Listener listener(*instance, AcceptHandler, ListenerDeleteHandler);
|
|
SockAddr localAddr;
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("TestPlatTcpListener");
|
|
|
|
ResetTestState();
|
|
|
|
localAddr.SetPort(1234);
|
|
|
|
Log(" Enable listener");
|
|
SuccessOrQuit(listener.Enable(localAddr));
|
|
VerifyOrQuit(listener.GetState() == Listener::kStateEnabled);
|
|
VerifyOrQuit(sListenerInfo.mEnabled);
|
|
VerifyOrQuit(sListenerInfo.mLocalSockAddr == localAddr);
|
|
|
|
Log(" Check active listeners");
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetListeners().GetHead() == &listener);
|
|
VerifyOrQuit(listener.GetNext() == nullptr);
|
|
|
|
Log(" Disable listener");
|
|
sDeleteHandlerCalled = false;
|
|
listener.Disable();
|
|
VerifyOrQuit(listener.GetState() == Listener::kStateDisabled);
|
|
VerifyOrQuit(!sListenerInfo.mEnabled);
|
|
|
|
Log(" Verify delete handler is called");
|
|
VerifyOrQuit(!sDeleteHandlerCalled);
|
|
otTaskletsProcess(instance);
|
|
VerifyOrQuit(sDeleteHandlerCalled);
|
|
VerifyOrQuit(listener.GetState() == Listener::kStateUnused);
|
|
|
|
Log(" Check active listeners list is empty");
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetListeners().IsEmpty());
|
|
|
|
testFreeInstance(instance);
|
|
|
|
Log("End of TestPlatTcpListener");
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void TestPlatTcpConnection(void)
|
|
{
|
|
Instance *instance = testInitInstance();
|
|
Connection connection(*instance, EventHandler, ConnectionDeleteHandler);
|
|
SockAddr peerAddr;
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("TestPlatTcpConnection");
|
|
|
|
ResetTestState();
|
|
|
|
SuccessOrQuit(peerAddr.GetAddress().FromString("fd00:1234::cafe"));
|
|
peerAddr.SetPort(5678);
|
|
peerAddr.SetIfIndex(4);
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().IsEmpty());
|
|
|
|
Log(" Connect");
|
|
sEventHandlerCalled = false;
|
|
SuccessOrQuit(connection.Connect(peerAddr));
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateConnecting);
|
|
VerifyOrQuit(sConnInfo.mConnecting);
|
|
VerifyOrQuit(!sEventHandlerCalled);
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().GetHead() == &connection);
|
|
VerifyOrQuit(connection.GetNext() == nullptr);
|
|
|
|
Log(" Signal connected `otPlatTcpHandleConnected`");
|
|
otPlatTcpHandleConnected(&connection);
|
|
VerifyOrQuit(sEventHandlerCalled);
|
|
VerifyOrQuit(sLastEvent == Connection::kEventConnected);
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateConnected);
|
|
|
|
Log(" Disconnect from platform `otPlatTcpHandleDisconnected`");
|
|
sEventHandlerCalled = false;
|
|
otPlatTcpHandleDisconnected(&connection, OT_PLAT_TCP_DISCONNECT_REASON_CLOSED);
|
|
VerifyOrQuit(sEventHandlerCalled);
|
|
VerifyOrQuit(sLastEvent == Connection::kEventDisconnected);
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(connection.GetDisconnectReason() == Ip6::PlatTcp::kDisconnectReasonClosed);
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().GetHead() == &connection);
|
|
VerifyOrQuit(connection.GetNext() == nullptr);
|
|
|
|
Log(" Cleanup");
|
|
sDeleteHandlerCalled = false;
|
|
otTaskletsProcess(instance);
|
|
VerifyOrQuit(sDeleteHandlerCalled);
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateUnused);
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().IsEmpty());
|
|
|
|
testFreeInstance(instance);
|
|
|
|
Log("End of TestPlatTcpConnection");
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void TestPlatTcpSendRecv(void)
|
|
{
|
|
static const uint8_t kData[] = {1, 2, 3, 4, 5, 6, 7};
|
|
static const uint8_t kData2[] = {0xfe, 0xdc, 0xba};
|
|
|
|
Instance *instance = testInitInstance();
|
|
Connection connection(*instance, EventHandler, ConnectionDeleteHandler);
|
|
SockAddr localAddr;
|
|
SockAddr peerAddr;
|
|
OwnedPtr<Message> message;
|
|
const Message *rxMessage;
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("TestPlatTcpSendRecv");
|
|
|
|
ResetTestState();
|
|
sConnInfo.mConnection = &connection;
|
|
|
|
SuccessOrQuit(localAddr.GetAddress().FromString("fd00:beef:cafe::2"));
|
|
localAddr.SetPort(4321);
|
|
localAddr.SetIfIndex(7);
|
|
|
|
SuccessOrQuit(peerAddr.GetAddress().FromString("fd00:beef:cafe::1"));
|
|
peerAddr.SetPort(5678);
|
|
peerAddr.SetIfIndex(7);
|
|
|
|
Log(" Connect");
|
|
|
|
SuccessOrQuit(connection.BindAndConnect(localAddr, peerAddr));
|
|
otPlatTcpHandleConnected(&connection);
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateConnected);
|
|
|
|
VerifyOrQuit(sConnInfo.mConnection == &connection);
|
|
VerifyOrQuit(sConnInfo.mLocalSockAddr == localAddr);
|
|
VerifyOrQuit(sConnInfo.mPeerSockAddr == peerAddr);
|
|
|
|
Log(" Send data");
|
|
|
|
message.Reset(instance->Get<MessagePool>().Allocate(Message::kTypeOther));
|
|
SuccessOrQuit(message->AppendBytes(kData, sizeof(kData)));
|
|
|
|
sEventHandlerCalled = false;
|
|
SuccessOrQuit(connection.Send(message.PassOwnership()));
|
|
VerifyOrQuit(!sEventHandlerCalled);
|
|
VerifyOrQuit(sConnInfo.mNotifyTxPending);
|
|
|
|
Log(" Signal `otPlatTcpHandleTxReady`");
|
|
otPlatTcpHandleTxReady(&connection);
|
|
VerifyOrQuit(sConnInfo.mTxBufferUsed == sizeof(kData));
|
|
VerifyOrQuit(memcmp(sConnInfo.mTxBuffer, kData, sizeof(kData)) == 0);
|
|
|
|
VerifyOrQuit(sEventHandlerCalled);
|
|
VerifyOrQuit(sLastEvent == Connection::kEventSendDone);
|
|
|
|
Log(" Send more data");
|
|
|
|
message.Reset(instance->Get<MessagePool>().Allocate(Message::kTypeOther));
|
|
SuccessOrQuit(message->AppendBytes(kData2, sizeof(kData2)));
|
|
|
|
sEventHandlerCalled = false;
|
|
SuccessOrQuit(connection.Send(message.PassOwnership()));
|
|
VerifyOrQuit(!sEventHandlerCalled);
|
|
VerifyOrQuit(sConnInfo.mNotifyTxPending);
|
|
|
|
Log(" Signal `otPlatTcpHandleTxReady`");
|
|
otPlatTcpHandleTxReady(&connection);
|
|
VerifyOrQuit(sConnInfo.mTxBufferUsed == sizeof(kData) + sizeof(kData2));
|
|
VerifyOrQuit(memcmp(sConnInfo.mTxBuffer, kData, sizeof(kData)) == 0);
|
|
VerifyOrQuit(memcmp(&sConnInfo.mTxBuffer[sizeof(kData)], kData2, sizeof(kData2)) == 0);
|
|
|
|
VerifyOrQuit(sEventHandlerCalled);
|
|
VerifyOrQuit(sLastEvent == Connection::kEventSendDone);
|
|
|
|
Log(" Receive data `otPlatTcpHandleReceive`");
|
|
sEventHandlerCalled = false;
|
|
otPlatTcpHandleReceive(&connection, kData, sizeof(kData));
|
|
VerifyOrQuit(sEventHandlerCalled);
|
|
VerifyOrQuit(sLastEvent == Connection::kEventReceive);
|
|
|
|
rxMessage = connection.GetRxMessage();
|
|
VerifyOrQuit(rxMessage != nullptr);
|
|
VerifyOrQuit(rxMessage->GetLength() == sizeof(kData));
|
|
VerifyOrQuit(rxMessage->CompareBytes(/* aOffset */ 0, kData, sizeof(kData)));
|
|
|
|
Log(" Remove part of rx data");
|
|
connection.RemoveParsedLengthFromRxMessage(2);
|
|
rxMessage = connection.GetRxMessage();
|
|
VerifyOrQuit(rxMessage->GetLength() == sizeof(kData) - 2);
|
|
|
|
Log(" Receive more data `otPlatTcpHandleReceive`");
|
|
sEventHandlerCalled = false;
|
|
otPlatTcpHandleReceive(&connection, kData2, sizeof(kData2));
|
|
VerifyOrQuit(sEventHandlerCalled);
|
|
VerifyOrQuit(sLastEvent == Connection::kEventReceive);
|
|
|
|
rxMessage = connection.GetRxMessage();
|
|
VerifyOrQuit(rxMessage != nullptr);
|
|
VerifyOrQuit(rxMessage->GetLength() == sizeof(kData) + sizeof(kData2) - 2);
|
|
VerifyOrQuit(rxMessage->CompareBytes(/* aOffset */ sizeof(kData) - 2, kData2, sizeof(kData2)));
|
|
VerifyOrQuit(rxMessage->GetOffset() == sizeof(kData) - 2);
|
|
|
|
Log(" Free Rx message");
|
|
connection.FreeRxMessage();
|
|
VerifyOrQuit(connection.GetRxMessage() == nullptr);
|
|
|
|
Log(" Close");
|
|
sEventHandlerCalled = false;
|
|
connection.Close();
|
|
VerifyOrQuit(sConnInfo.mClosing);
|
|
VerifyOrQuit(!sEventHandlerCalled);
|
|
|
|
Log(" Signal `otPlatTcpHandleDisconnected`");
|
|
otPlatTcpHandleDisconnected(&connection, OT_PLAT_TCP_DISCONNECT_REASON_CLOSED);
|
|
|
|
VerifyOrQuit(sEventHandlerCalled);
|
|
VerifyOrQuit(sLastEvent == Connection::kEventDisconnected);
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateDisconnected);
|
|
|
|
Log(" Cleanup");
|
|
sDeleteHandlerCalled = false;
|
|
otTaskletsProcess(instance);
|
|
VerifyOrQuit(sDeleteHandlerCalled);
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateUnused);
|
|
|
|
testFreeInstance(instance);
|
|
|
|
Log("End of TestPlatTcpSendRecv");
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void TestPlatTcpFlowControl(void)
|
|
{
|
|
Instance *instance = testInitInstance();
|
|
Connection connection(*instance, EventHandler, ConnectionDeleteHandler);
|
|
SockAddr peerAddr;
|
|
OwnedPtr<Message> message1, message2;
|
|
uint8_t data[600];
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("TestPlatTcpFlowControl");
|
|
|
|
ResetTestState();
|
|
|
|
sConnInfo.mConnection = &connection;
|
|
|
|
SuccessOrQuit(peerAddr.GetAddress().FromString("fd00:3f12::aaaa"));
|
|
peerAddr.SetPort(9876);
|
|
peerAddr.SetIfIndex(12);
|
|
|
|
for (uint16_t i = 0; i < sizeof(data); i++)
|
|
{
|
|
data[i] = static_cast<uint8_t>(i & 0xff) ^ static_cast<uint8_t>(i >> 8);
|
|
}
|
|
|
|
Log(" Connect");
|
|
SuccessOrQuit(connection.Connect(peerAddr));
|
|
otPlatTcpHandleConnected(&connection);
|
|
|
|
Log(" Partial send (buffer full)");
|
|
|
|
message1.Reset(instance->Get<MessagePool>().Allocate(Message::kTypeIp6));
|
|
SuccessOrQuit(message1->AppendBytes(data, 600));
|
|
message2.Reset(instance->Get<MessagePool>().Allocate(Message::kTypeIp6));
|
|
SuccessOrQuit(message2->AppendBytes(data, 600));
|
|
|
|
SuccessOrQuit(connection.Send(message1.PassOwnership()));
|
|
SuccessOrQuit(connection.Send(message2.PassOwnership()));
|
|
|
|
Log(" Platform tx buffer size 1000. Validate partial write");
|
|
|
|
sEventHandlerCalled = false;
|
|
|
|
Log(" Signal `otPlatTcpHandleTxReady`");
|
|
otPlatTcpHandleTxReady(&connection);
|
|
|
|
VerifyOrQuit(sConnInfo.mTxBufferUsed == kTxBufferSize);
|
|
VerifyOrQuit(memcmp(sConnInfo.mTxBuffer, data, sizeof(data)) == 0);
|
|
VerifyOrQuit(memcmp(&sConnInfo.mTxBuffer[sizeof(data)], data, kTxBufferSize - sizeof(data)) == 0);
|
|
|
|
VerifyOrQuit(!sEventHandlerCalled, "SendDone should NOT be called yet");
|
|
|
|
Log(" Now clear platform tx buffer. Validate the rest of data is passed to platform");
|
|
sConnInfo.mTxBufferUsed = 0;
|
|
|
|
Log(" Signal `otPlatTcpHandleTxReady`");
|
|
otPlatTcpHandleTxReady(&connection);
|
|
|
|
VerifyOrQuit(sConnInfo.mTxBufferUsed == 2 * sizeof(data) - kTxBufferSize);
|
|
VerifyOrQuit(memcmp(sConnInfo.mTxBuffer, &data[sizeof(data) - sConnInfo.mTxBufferUsed], sConnInfo.mTxBufferUsed) ==
|
|
0);
|
|
|
|
VerifyOrQuit(sEventHandlerCalled, "SendDone should be called now");
|
|
VerifyOrQuit(sLastEvent == Connection::kEventSendDone);
|
|
|
|
Log(" Abort");
|
|
connection.Abort();
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateDisconnected);
|
|
VerifyOrQuit(sConnInfo.mAborted);
|
|
|
|
testFreeInstance(instance);
|
|
Log("End of TestPlatTcpFlowControl");
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void TestPlatTcpAccept(void)
|
|
{
|
|
Instance *instance = testInitInstance();
|
|
Listener listener(*instance, AcceptHandler, ListenerDeleteHandler);
|
|
Connection connection(*instance, EventHandler, ConnectionDeleteHandler);
|
|
Connection *acceptedConn;
|
|
SockAddr localAddr;
|
|
SockAddr peerAddr;
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("TestPlatTcpAccept");
|
|
|
|
ResetTestState();
|
|
|
|
sConnInfo.mConnection = &connection;
|
|
|
|
localAddr.SetPort(1234);
|
|
|
|
SuccessOrQuit(peerAddr.GetAddress().FromString("fe80::1234"));
|
|
peerAddr.SetPort(5678);
|
|
|
|
Log(" Enable listener");
|
|
SuccessOrQuit(listener.Enable(localAddr));
|
|
|
|
Log(" Signal incoming connection request `otPlatTcpAccept`");
|
|
sAcceptHandlerCalled = false;
|
|
acceptedConn = AsCoreTypePtr(otPlatTcpAccept(&listener, &peerAddr));
|
|
|
|
VerifyOrQuit(sAcceptHandlerCalled);
|
|
VerifyOrQuit(acceptedConn == &connection);
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateConnecting);
|
|
|
|
Log(" Signal `otPlatTcpHandleConnected`");
|
|
otPlatTcpHandleConnected(&connection);
|
|
VerifyOrQuit(connection.GetState() == Connection::kStateConnected);
|
|
|
|
testFreeInstance(instance);
|
|
|
|
Log("End of TestPlatTcpAccept");
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void TestPlatTcpIteration(void)
|
|
{
|
|
Instance *instance = testInitInstance();
|
|
Listener listener1(*instance, AcceptHandler, ListenerDeleteHandler);
|
|
Listener listener2(*instance, AcceptHandler, ListenerDeleteHandler);
|
|
Connection connection1(*instance, EventHandler, ConnectionDeleteHandler);
|
|
Connection connection2(*instance, EventHandler, ConnectionDeleteHandler);
|
|
Listener *listener;
|
|
Connection *connection;
|
|
SockAddr sockAddr;
|
|
|
|
Log("- - - - - - - - - - - - - - - - - - - - - - - - - ");
|
|
Log("TestPlatTcpIteration");
|
|
|
|
ResetTestState();
|
|
|
|
Log(" Enable two listeners");
|
|
|
|
sockAddr.SetPort(1);
|
|
SuccessOrQuit(listener1.Enable(sockAddr));
|
|
sockAddr.SetPort(2);
|
|
SuccessOrQuit(listener2.Enable(sockAddr));
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetListeners().GetHead() == &listener2);
|
|
VerifyOrQuit(listener2.GetNext() == &listener1);
|
|
VerifyOrQuit(listener1.GetNext() == nullptr);
|
|
|
|
Log(" Validate IterateListeners");
|
|
|
|
listener = nullptr;
|
|
listener = instance->Get<Ip6::PlatTcp>().IterateListeners(listener);
|
|
VerifyOrQuit(listener == &listener2);
|
|
listener = instance->Get<Ip6::PlatTcp>().IterateListeners(listener);
|
|
VerifyOrQuit(listener == &listener1);
|
|
listener = instance->Get<Ip6::PlatTcp>().IterateListeners(listener);
|
|
VerifyOrQuit(listener == nullptr);
|
|
|
|
Log(" Disable first listener");
|
|
|
|
sListenerInfo.mListener = &listener1;
|
|
listener1.Disable();
|
|
|
|
Log(" Validate IterateListeners skips the disabled entry");
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetListeners().GetHead() == &listener2);
|
|
VerifyOrQuit(listener2.GetNext() == &listener1);
|
|
VerifyOrQuit(listener1.GetNext() == nullptr);
|
|
|
|
listener = nullptr;
|
|
listener = instance->Get<Ip6::PlatTcp>().IterateListeners(listener);
|
|
VerifyOrQuit(listener == &listener2);
|
|
listener = instance->Get<Ip6::PlatTcp>().IterateListeners(listener);
|
|
VerifyOrQuit(listener == nullptr);
|
|
|
|
Log(" otTaskletsProcess");
|
|
otTaskletsProcess(instance);
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetListeners().GetHead() == &listener2);
|
|
VerifyOrQuit(listener2.GetNext() == nullptr);
|
|
|
|
Log(" Disable second listener");
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetListeners().GetHead() == &listener2);
|
|
VerifyOrQuit(listener2.GetNext() == nullptr);
|
|
|
|
sListenerInfo.mListener = &listener2;
|
|
listener2.Disable();
|
|
|
|
listener = nullptr;
|
|
listener = instance->Get<Ip6::PlatTcp>().IterateListeners(listener);
|
|
VerifyOrQuit(listener == nullptr);
|
|
|
|
Log(" otTaskletsProcess");
|
|
otTaskletsProcess(instance);
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetListeners().IsEmpty());
|
|
|
|
listener = nullptr;
|
|
listener = instance->Get<Ip6::PlatTcp>().IterateListeners(listener);
|
|
VerifyOrQuit(listener == nullptr);
|
|
|
|
Log(" Start two connections");
|
|
|
|
sockAddr.SetPort(3);
|
|
SuccessOrQuit(connection1.Connect(sockAddr));
|
|
sockAddr.SetPort(4);
|
|
SuccessOrQuit(connection2.Connect(sockAddr));
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().GetHead() == &connection2);
|
|
VerifyOrQuit(connection2.GetNext() == &connection1);
|
|
VerifyOrQuit(connection1.GetNext() == nullptr);
|
|
|
|
connection = nullptr;
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == &connection2);
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == &connection1);
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == nullptr);
|
|
|
|
Log(" Abort second connection");
|
|
|
|
sConnInfo.mConnection = &connection2;
|
|
connection2.Abort();
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().GetHead() == &connection2);
|
|
VerifyOrQuit(connection2.GetNext() == &connection1);
|
|
VerifyOrQuit(connection1.GetNext() == nullptr);
|
|
|
|
connection = nullptr;
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == &connection1);
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == nullptr);
|
|
|
|
Log(" otTaskletsProcess");
|
|
otTaskletsProcess(instance);
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().GetHead() == &connection1);
|
|
VerifyOrQuit(connection1.GetNext() == nullptr);
|
|
|
|
connection = nullptr;
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == &connection1);
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == nullptr);
|
|
|
|
Log(" Abort first connection");
|
|
|
|
sConnInfo.mConnection = &connection1;
|
|
connection1.Abort();
|
|
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().GetHead() == &connection1);
|
|
VerifyOrQuit(connection1.GetNext() == nullptr);
|
|
|
|
connection = nullptr;
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == nullptr);
|
|
|
|
Log(" otTaskletsProcess");
|
|
otTaskletsProcess(instance);
|
|
VerifyOrQuit(instance->Get<Ip6::PlatTcp>().GetConnections().IsEmpty());
|
|
|
|
connection = nullptr;
|
|
connection = instance->Get<Ip6::PlatTcp>().IterateConnections(connection);
|
|
VerifyOrQuit(connection == nullptr);
|
|
|
|
testFreeInstance(instance);
|
|
|
|
Log("End of TestPlatTcpIteration");
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
|
|
|
} // namespace ot
|
|
|
|
int main(void)
|
|
{
|
|
#if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
|
ot::TestPlatTcpListener();
|
|
ot::TestPlatTcpConnection();
|
|
ot::TestPlatTcpSendRecv();
|
|
ot::TestPlatTcpFlowControl();
|
|
ot::TestPlatTcpAccept();
|
|
ot::TestPlatTcpIteration();
|
|
#endif
|
|
|
|
printf("All tests passed\n");
|
|
return 0;
|
|
}
|