mirror of
https://github.com/espressif/openthread.git
synced 2026-07-30 15:47:46 +00:00
[posix] implement otPlatTcp platform abstraction (#13193)
This commit provides a complete POSIX implementation of the new `otPlatTcp` platform abstraction APIs. It leverages the existing OpenThread `Mainloop` polling mechanism to build a fully event-driven, non-blocking TCP socket implementation without requiring extra threads. Key features and implementation details: - Uses `Mainloop` read/write/error sets to monitor socket states. - Implements `otPlatTcpEnableListener` and `otPlatTcpAccept` to handle incoming connection requests over IPv6. - Implements `otPlatTcpConnect` for asynchronous non-blocking outgoing TCP connections, monitoring `SO_ERROR` to detect handshake completion. - Handles data transmission utilizing `otPlatTcpIsTxPending` to monitor the write descriptor and correctly signal `HandleTxReady`. - Implements `otPlatTcpSend` and `otPlatTcpReceive` ensuring proper buffering, suppressing `SIGPIPE` safely using `SO_NOSIGPIPE` and `MSG_NOSIGNAL`. - Encodes socket file descriptors directly within the platform data `mData.mDescriptor` union, avoiding dynamic memory allocation.
This commit is contained in:
@@ -152,6 +152,7 @@ add_library(openthread-posix
|
||||
spinel_manager.cpp
|
||||
spi_interface.cpp
|
||||
system.cpp
|
||||
tcp.cpp
|
||||
tmp_storage.cpp
|
||||
trel.cpp
|
||||
udp.cpp
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#include "posix/platform/radio_url.hpp"
|
||||
#include "posix/platform/spinel_driver_getter.hpp"
|
||||
#include "posix/platform/spinel_manager.hpp"
|
||||
#include "posix/platform/tcp.hpp"
|
||||
#include "posix/platform/udp.hpp"
|
||||
|
||||
otInstance *gInstance = nullptr;
|
||||
@@ -149,6 +150,10 @@ void platformInitRcpMode(otPlatformConfig *aPlatformConfig)
|
||||
ot::Posix::InfraNetif::Get().Init();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
||||
ot::Posix::Tcp::Get().Init();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
|
||||
ot::Posix::MdnsSocket::Get().Init();
|
||||
#endif
|
||||
@@ -169,6 +174,7 @@ void platformInitRcpMode(otPlatformConfig *aPlatformConfig)
|
||||
ot::Posix::Udp::Get().Init(aPlatformConfig->mInterfaceName);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
@@ -248,6 +254,10 @@ void platformSetUp(otPlatformConfig *aPlatformConfig)
|
||||
ot::Posix::Udp::Get().SetUp();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
||||
ot::Posix::Tcp::Get().SetUp();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
|
||||
ot::Posix::MdnsSocket::Get().SetUp();
|
||||
#endif
|
||||
@@ -310,6 +320,10 @@ void platformTearDown(void)
|
||||
ot::Posix::Udp::Get().TearDown();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
||||
ot::Posix::Tcp::Get().TearDown();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
|
||||
platformNetifTearDown();
|
||||
#endif
|
||||
@@ -341,6 +355,11 @@ void platformDeinitRcpMode(void)
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
|
||||
ot::Posix::Udp::Get().Deinit();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
||||
ot::Posix::Tcp::Get().Deinit();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
|
||||
platformNetifDeinit();
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief
|
||||
* This file implements the platform TCP support for POSIX.
|
||||
*/
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define __APPLE_USE_RFC_3542
|
||||
#endif
|
||||
|
||||
#include "openthread-posix-config.h"
|
||||
#include "platform-posix.h"
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "posix/platform/infra_if.hpp"
|
||||
#include "posix/platform/ip6_utils.hpp"
|
||||
#include "posix/platform/mainloop.hpp"
|
||||
#include "posix/platform/tcp.hpp"
|
||||
#include "posix/platform/utils.hpp"
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
using namespace ot::Posix::Ip6Utils;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kMaxListenBacklog = 5;
|
||||
constexpr int kMaxReceiveBufferSize = 1500;
|
||||
|
||||
int GetFd(otPlatTcpListener *aListener) { return aListener->mData.mDescriptor; }
|
||||
|
||||
void SetFd(otPlatTcpListener *aListener, int aFd) { aListener->mData.mDescriptor = aFd; }
|
||||
|
||||
int GetFd(otPlatTcpConnection *aConn) { return aConn->mData.mDescriptor; }
|
||||
|
||||
void SetFd(otPlatTcpConnection *aConn, int aFd) { aConn->mData.mDescriptor = aFd; }
|
||||
|
||||
void ConvertSockAddr(const otPlatTcpSockAddr &aIn, struct sockaddr_in6 &aOut)
|
||||
{
|
||||
memset(&aOut, 0, sizeof(aOut));
|
||||
aOut.sin6_family = AF_INET6;
|
||||
aOut.sin6_port = htons(aIn.mSockAddr.mPort);
|
||||
aOut.sin6_scope_id = aIn.mIfIndex;
|
||||
|
||||
if (IsIp6AddressUnspecified(aIn.mSockAddr.mAddress))
|
||||
{
|
||||
aOut.sin6_addr = in6addr_any;
|
||||
}
|
||||
else
|
||||
{
|
||||
CopyIp6AddressTo(aIn.mSockAddr.mAddress, &aOut.sin6_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void SetNoSigPipe(int aFd)
|
||||
{
|
||||
#ifdef SO_NOSIGPIPE
|
||||
int opt = 1;
|
||||
setsockopt(aFd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
|
||||
#else
|
||||
OT_UNUSED_VARIABLE(aFd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetReuseAddr(int aFd)
|
||||
{
|
||||
int opt = 1;
|
||||
|
||||
setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
}
|
||||
|
||||
otError BindToInterface(int aFd, uint32_t aIfIndex)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(aIfIndex > 0);
|
||||
|
||||
#ifdef __linux__
|
||||
{
|
||||
char ifName[IFNAMSIZ];
|
||||
|
||||
VerifyOrExit(if_indextoname(aIfIndex, ifName) != nullptr, error = OT_ERROR_FAILED);
|
||||
VerifyOrExit(setsockopt(aFd, SOL_SOCKET, SO_BINDTODEVICE, ifName, strlen(ifName)) == 0,
|
||||
error = OT_ERROR_FAILED);
|
||||
}
|
||||
#else // __NetBSD__ || __FreeBSD__ || __APPLE__
|
||||
VerifyOrExit(setsockopt(aFd, IPPROTO_IPV6, IPV6_BOUND_IF, &aIfIndex, sizeof(aIfIndex)) == 0,
|
||||
error = OT_ERROR_FAILED);
|
||||
#endif // __linux__
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// otPlatTcp APIs
|
||||
|
||||
extern "C" otError otPlatTcpEnableListener(otPlatTcpListener *aListener, const otPlatTcpSockAddr *aLocalSockAddr)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
int fd = -1;
|
||||
struct sockaddr_in6 sin6;
|
||||
|
||||
ConvertSockAddr(*aLocalSockAddr, sin6);
|
||||
|
||||
fd = ot::Posix::SocketWithCloseExec(AF_INET6, SOCK_STREAM, IPPROTO_TCP, ot::Posix::kSocketNonBlock);
|
||||
VerifyOrExit(fd >= 0, error = OT_ERROR_FAILED);
|
||||
|
||||
SetReuseAddr(fd);
|
||||
SetNoSigPipe(fd);
|
||||
|
||||
SuccessOrExit(error = BindToInterface(fd, aLocalSockAddr->mIfIndex));
|
||||
|
||||
if (bind(fd, reinterpret_cast<struct sockaddr *>(&sin6), sizeof(sin6)) == -1)
|
||||
{
|
||||
error = (errno == EADDRINUSE) ? OT_ERROR_ALREADY : OT_ERROR_FAILED;
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
if (listen(fd, kMaxListenBacklog) == -1)
|
||||
{
|
||||
ExitNow(error = OT_ERROR_FAILED);
|
||||
}
|
||||
|
||||
SetFd(aListener, fd);
|
||||
fd = -1;
|
||||
|
||||
exit:
|
||||
if (fd >= 0)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
extern "C" void otPlatTcpDisableListener(otPlatTcpListener *aListener)
|
||||
{
|
||||
int fd = GetFd(aListener);
|
||||
|
||||
VerifyOrExit(fd >= 0);
|
||||
|
||||
close(fd);
|
||||
SetFd(aListener, -1);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
extern "C" otError otPlatTcpConnect(otPlatTcpConnection *aConn,
|
||||
const otPlatTcpSockAddr *aPeerSockAddr,
|
||||
const otPlatTcpSockAddr *aLocalSockAddr)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
int fd;
|
||||
struct sockaddr_in6 peerSin6;
|
||||
uint32_t ifIndex;
|
||||
|
||||
fd = ot::Posix::SocketWithCloseExec(AF_INET6, SOCK_STREAM, IPPROTO_TCP, ot::Posix::kSocketNonBlock);
|
||||
VerifyOrExit(fd >= 0, error = OT_ERROR_FAILED);
|
||||
|
||||
SetNoSigPipe(fd);
|
||||
|
||||
ifIndex = (aLocalSockAddr != nullptr && aLocalSockAddr->mIfIndex > 0) ? aLocalSockAddr->mIfIndex
|
||||
: aPeerSockAddr->mIfIndex;
|
||||
SuccessOrExit(error = BindToInterface(fd, ifIndex));
|
||||
|
||||
if (aLocalSockAddr != nullptr)
|
||||
{
|
||||
struct sockaddr_in6 localSin6;
|
||||
|
||||
ConvertSockAddr(*aLocalSockAddr, localSin6);
|
||||
|
||||
SetReuseAddr(fd);
|
||||
|
||||
if (bind(fd, reinterpret_cast<struct sockaddr *>(&localSin6), sizeof(localSin6)) == -1)
|
||||
{
|
||||
ExitNow(error = OT_ERROR_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
ConvertSockAddr(*aPeerSockAddr, peerSin6);
|
||||
|
||||
if (connect(fd, reinterpret_cast<struct sockaddr *>(&peerSin6), sizeof(peerSin6)) == -1)
|
||||
{
|
||||
if (errno != EINPROGRESS)
|
||||
{
|
||||
ExitNow(error = OT_ERROR_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
SetFd(aConn, fd);
|
||||
fd = -1;
|
||||
|
||||
exit:
|
||||
if (fd >= 0)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
extern "C" void otPlatTcpNotifyTxPending(otPlatTcpConnection *aConn)
|
||||
{
|
||||
// No action requires since we use `otPlatTcpIsTxPending()`
|
||||
// in `Update(Mainloop::Context &aContext)`.
|
||||
|
||||
OT_UNUSED_VARIABLE(aConn);
|
||||
}
|
||||
|
||||
extern "C" uint16_t otPlatTcpSend(otPlatTcpConnection *aConn, const uint8_t *aBuffer, uint16_t aLength)
|
||||
{
|
||||
uint16_t written = 0;
|
||||
int fd = GetFd(aConn);
|
||||
ssize_t ret;
|
||||
|
||||
VerifyOrExit(fd >= 0);
|
||||
|
||||
ret = send(fd, aBuffer, aLength, MSG_NOSIGNAL);
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
written = static_cast<uint16_t>(ret);
|
||||
}
|
||||
|
||||
exit:
|
||||
return written;
|
||||
}
|
||||
|
||||
extern "C" void otPlatTcpClose(otPlatTcpConnection *aConn)
|
||||
{
|
||||
int fd = GetFd(aConn);
|
||||
|
||||
if (fd >= 0)
|
||||
{
|
||||
close(fd);
|
||||
SetFd(aConn, -1);
|
||||
}
|
||||
|
||||
otPlatTcpHandleDisconnected(aConn, OT_PLAT_TCP_DISCONNECT_REASON_CLOSED);
|
||||
}
|
||||
|
||||
extern "C" void otPlatTcpAbort(otPlatTcpConnection *aConn)
|
||||
{
|
||||
struct linger sl;
|
||||
int fd = GetFd(aConn);
|
||||
|
||||
VerifyOrExit(fd >= 0);
|
||||
|
||||
sl.l_onoff = 1;
|
||||
sl.l_linger = 0;
|
||||
setsockopt(fd, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));
|
||||
|
||||
close(fd);
|
||||
SetFd(aConn, -1);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// Posix::Tcp
|
||||
|
||||
namespace ot {
|
||||
namespace Posix {
|
||||
|
||||
const char Tcp::kLogModuleName[] = "Tcp";
|
||||
|
||||
Tcp &Tcp::Get(void)
|
||||
{
|
||||
static Tcp sInstance;
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
void Tcp::Init(void) { LogDebg("Init"); }
|
||||
|
||||
void Tcp::SetUp(void) { Mainloop::Manager::Get().Add(*this); }
|
||||
|
||||
void Tcp::TearDown(void) { Mainloop::Manager::Get().Remove(*this); }
|
||||
|
||||
void Tcp::Deinit(void) { LogDebg("Deinit"); }
|
||||
|
||||
void Tcp::Update(Mainloop::Context &aContext)
|
||||
{
|
||||
otPlatTcpListener *listener = nullptr;
|
||||
otPlatTcpConnection *conn = nullptr;
|
||||
|
||||
while ((listener = otPlatTcpIterateListeners(gInstance, listener)) != nullptr)
|
||||
{
|
||||
Mainloop::AddToReadFdSet(GetFd(listener), aContext);
|
||||
}
|
||||
|
||||
while ((conn = otPlatTcpIterateConnections(gInstance, conn)) != nullptr)
|
||||
{
|
||||
int fd = GetFd(conn);
|
||||
|
||||
if (otPlatTcpIsConnecting(conn))
|
||||
{
|
||||
Mainloop::AddToWriteFdSet(fd, aContext);
|
||||
Mainloop::AddToErrorFdSet(fd, aContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mainloop::AddToReadFdSet(fd, aContext);
|
||||
|
||||
if (otPlatTcpIsTxPending(conn))
|
||||
{
|
||||
Mainloop::AddToWriteFdSet(fd, aContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tcp::Process(const Mainloop::Context &aContext)
|
||||
{
|
||||
otPlatTcpListener *listener = nullptr;
|
||||
otPlatTcpConnection *conn = nullptr;
|
||||
|
||||
while ((listener = otPlatTcpIterateListeners(gInstance, listener)) != nullptr)
|
||||
{
|
||||
ProcessListener(listener, aContext);
|
||||
}
|
||||
|
||||
while ((conn = otPlatTcpIterateConnections(gInstance, conn)) != nullptr)
|
||||
{
|
||||
ProcessConnection(conn, aContext);
|
||||
}
|
||||
}
|
||||
|
||||
void Tcp::ProcessListener(otPlatTcpListener *aListener, const Mainloop::Context &aContext)
|
||||
{
|
||||
int fd = GetFd(aListener);
|
||||
struct sockaddr_in6 peerAddr;
|
||||
socklen_t addrLen = sizeof(peerAddr);
|
||||
int newFd;
|
||||
otPlatTcpSockAddr peerSockAddr;
|
||||
otPlatTcpConnection *newConn;
|
||||
|
||||
VerifyOrExit(fd >= 0);
|
||||
|
||||
VerifyOrExit(Mainloop::IsFdReadable(fd, aContext));
|
||||
|
||||
newFd = accept(fd, reinterpret_cast<struct sockaddr *>(&peerAddr), &addrLen);
|
||||
VerifyOrExit(newFd >= 0);
|
||||
|
||||
memset(&peerSockAddr, 0, sizeof(peerSockAddr));
|
||||
peerSockAddr.mSockAddr.mPort = ntohs(peerAddr.sin6_port);
|
||||
peerSockAddr.mIfIndex = peerAddr.sin6_scope_id;
|
||||
ReadIp6AddressFrom(&peerAddr.sin6_addr, peerSockAddr.mSockAddr.mAddress);
|
||||
|
||||
newConn = otPlatTcpAccept(aListener, &peerSockAddr);
|
||||
VerifyOrExit(newConn != nullptr, close(newFd));
|
||||
|
||||
fcntl(newFd, F_SETFD, FD_CLOEXEC);
|
||||
fcntl(newFd, F_SETFL, fcntl(newFd, F_GETFL, 0) | O_NONBLOCK);
|
||||
SetNoSigPipe(newFd);
|
||||
SetFd(newConn, newFd);
|
||||
|
||||
otPlatTcpHandleConnected(newConn);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Tcp::ProcessConnection(otPlatTcpConnection *aConn, const Mainloop::Context &aContext)
|
||||
{
|
||||
int fd = GetFd(aConn);
|
||||
|
||||
VerifyOrExit(fd >= 0);
|
||||
|
||||
if (otPlatTcpIsConnecting(aConn))
|
||||
{
|
||||
int err = 0;
|
||||
socklen_t len = sizeof(err);
|
||||
|
||||
VerifyOrExit(Mainloop::IsFdWritable(fd, aContext) || Mainloop::HasFdErrored(fd, aContext));
|
||||
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0 && err == 0)
|
||||
{
|
||||
otPlatTcpHandleConnected(aConn);
|
||||
}
|
||||
else
|
||||
{
|
||||
otPlatTcpDisconnectReason reason = OT_PLAT_TCP_DISCONNECT_REASON_ERROR;
|
||||
|
||||
if (err == ECONNREFUSED)
|
||||
{
|
||||
reason = OT_PLAT_TCP_DISCONNECT_REASON_REFUSED;
|
||||
}
|
||||
else if (err == ETIMEDOUT)
|
||||
{
|
||||
reason = OT_PLAT_TCP_DISCONNECT_REASON_TIMEOUT;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
SetFd(aConn, -1);
|
||||
|
||||
otPlatTcpHandleDisconnected(aConn, reason);
|
||||
}
|
||||
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
// Connection is already connected
|
||||
|
||||
if (Mainloop::IsFdReadable(fd, aContext))
|
||||
{
|
||||
uint8_t buffer[kMaxReceiveBufferSize];
|
||||
ssize_t ret = recv(fd, buffer, sizeof(buffer), 0);
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
otPlatTcpHandleReceive(aConn, buffer, static_cast<uint16_t>(ret));
|
||||
|
||||
// The connection may be closed or aborted from within the
|
||||
// `otPlatTcpHandleReceive()` callback. We verify that the
|
||||
// file descriptor is still valid before proceeding, to
|
||||
// avoid checking `IsFdWritable()` and possibly invoking
|
||||
// `otPlatTcpHandleTxReady()` on an aborted connection.
|
||||
// Note that `otPlatTcpIterateConnections()` guarantees
|
||||
// that `aConn` remains valid during iteration (it is not
|
||||
// removed from the list).
|
||||
|
||||
VerifyOrExit(GetFd(aConn) >= 0);
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
close(fd);
|
||||
SetFd(aConn, -1);
|
||||
|
||||
otPlatTcpHandleDisconnected(aConn, OT_PLAT_TCP_DISCONNECT_REASON_CLOSED);
|
||||
ExitNow();
|
||||
}
|
||||
else if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
otPlatTcpDisconnectReason reason = OT_PLAT_TCP_DISCONNECT_REASON_ERROR;
|
||||
|
||||
if (errno == ECONNRESET)
|
||||
{
|
||||
reason = OT_PLAT_TCP_DISCONNECT_REASON_RESET;
|
||||
}
|
||||
else if (errno == ETIMEDOUT)
|
||||
{
|
||||
reason = OT_PLAT_TCP_DISCONNECT_REASON_TIMEOUT;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
SetFd(aConn, -1);
|
||||
|
||||
otPlatTcpHandleDisconnected(aConn, reason);
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
if (Mainloop::IsFdWritable(fd, aContext))
|
||||
{
|
||||
otPlatTcpHandleTxReady(aConn);
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace Posix
|
||||
} // namespace ot
|
||||
|
||||
#endif // #if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef OT_POSIX_PLATFORM_TCP_HPP_
|
||||
#define OT_POSIX_PLATFORM_TCP_HPP_
|
||||
|
||||
#include "openthread-posix-config.h"
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
||||
|
||||
#include <openthread/platform/tcp.h>
|
||||
|
||||
#include "logger.hpp"
|
||||
#include "mainloop.hpp"
|
||||
|
||||
#include "core/common/non_copyable.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Posix {
|
||||
|
||||
class Tcp : public Mainloop::Source, public Logger<Tcp>, private NonCopyable
|
||||
{
|
||||
public:
|
||||
static const char kLogModuleName[];
|
||||
|
||||
static Tcp &Get(void);
|
||||
|
||||
void Init(void);
|
||||
void SetUp(void);
|
||||
void TearDown(void);
|
||||
void Deinit(void);
|
||||
void Update(Mainloop::Context &aContext) override;
|
||||
void Process(const Mainloop::Context &aContext) override;
|
||||
|
||||
private:
|
||||
void ProcessListener(otPlatTcpListener *aListener, const Mainloop::Context &aContext);
|
||||
void ProcessConnection(otPlatTcpConnection *aConn, const Mainloop::Context &aContext);
|
||||
};
|
||||
|
||||
} // namespace Posix
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE
|
||||
|
||||
#endif // OT_POSIX_PLATFORM_TCP_HPP_
|
||||
@@ -66,6 +66,8 @@
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE 0
|
||||
#endif
|
||||
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_TCP_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE 1
|
||||
|
||||
Reference in New Issue
Block a user