From 876aa821645f2cf01574dc2faca2225b7bf1352e Mon Sep 17 00:00:00 2001 From: Sam Kumar Date: Sat, 20 Nov 2021 18:06:50 -0800 Subject: [PATCH] [tcp] add rewrite of TCPlp's interface code to support OpenThread's TCP API (#7190) --- include/openthread/instance.h | 2 +- include/openthread/tcp.h | 49 +- src/core/api/tcp_api.cpp | 2 +- src/core/net/tcp6.cpp | 718 +++++++++++++++++++++++++-- src/core/net/tcp6.hpp | 114 ++++- tests/unit/Makefile.am | 207 +++++--- third_party/tcplp/bsdtcp/tcp_const.h | 16 +- third_party/tcplp/bsdtcp/tcp_subr.c | 10 +- third_party/tcplp/bsdtcp/tcp_timer.c | 33 +- third_party/tcplp/bsdtcp/tcp_timer.h | 14 +- third_party/tcplp/tcplp.h | 9 +- 11 files changed, 1003 insertions(+), 171 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 699941c5d..4aec89d3b 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (187) +#define OPENTHREAD_API_VERSION (188) /** * @addtogroup api-instance diff --git a/include/openthread/tcp.h b/include/openthread/tcp.h index a0d4b3a09..46f12c459 100644 --- a/include/openthread/tcp.h +++ b/include/openthread/tcp.h @@ -191,6 +191,16 @@ typedef enum otTcpDisconnectedReason */ typedef void (*otTcpDisconnected)(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); +/** + * OT_TCP_ENDPOINT_TCB_SIZE_BASE and OT_TCP_ENDPOINT_TCB_NUM_POINTERS are + * chosen such that the mTcb field of otTcpEndpoint has the same size as + * struct tcpcb in TCPlp. This is necessary because the mTcb field, although + * opaque in its declaration, is treated as struct tcpcb in the TCP + * implementation. + */ +#define OT_TCP_ENDPOINT_TCB_SIZE_BASE 368 +#define OT_TCP_ENDPOINT_TCB_NUM_PTR 36 + /** * This structure represents a TCP endpoint. * @@ -205,9 +215,14 @@ typedef void (*otTcpDisconnected)(otTcpEndpoint *aEndpoint, otTcpDisconnectedRea */ struct otTcpEndpoint { - struct otTcpEndpoint *mNext; ///< A pointer to the next TCP endpoint (internal use only) - otInstance * mInstance; ///< A pointer to the OpenThread instance associated with this TCP endpoint - void * mContext; ///< A pointer to application-specific context + union + { + uint8_t mSize[OT_TCP_ENDPOINT_TCB_SIZE_BASE + OT_TCP_ENDPOINT_TCB_NUM_PTR * sizeof(void *)]; + uint64_t mAlign; + } mTcb; + + struct otTcpEndpoint *mNext; ///< A pointer to the next TCP endpoint (internal use only) + void * mContext; ///< A pointer to application-specific context otTcpEstablished mEstablishedCallback; ///< "Established" callback function otTcpSendDone mSendDoneCallback; ///< "Send done" callback function @@ -217,7 +232,8 @@ struct otTcpEndpoint uint32_t mTimers[4]; - /* Other implementation-defined fields go here. */ + otLinkedBuffer mReceiveLinks[2]; + otSockAddr mSockAddr; }; /** @@ -438,7 +454,7 @@ otError otTcpSendByExtension(otTcpEndpoint *aEndpoint, size_t aNumBytes, uint32_ * @retval OT_ERROR_FAILED Failed to complete the operation. * */ -otError otTcpReceiveByReference(const otTcpEndpoint *aEndpoint, const otLinkedBuffer **aBuffer); +otError otTcpReceiveByReference(otTcpEndpoint *aEndpoint, const otLinkedBuffer **aBuffer); /** * Reorganizes the receive buffer to be entirely contiguous in memory. @@ -598,6 +614,16 @@ typedef otTcpIncomingConnectionAction (*otTcpAcceptReady)(otTcpListener * aLis */ typedef void (*otTcpAcceptDone)(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer); +/** + * OT_TCP_LISTENER_TCB_SIZE_BASE and OT_TCP_LISTENER_TCB_NUM_POINTERS are + * chosen such that the mTcbListener field of otTcpListener has the same size + * as struct tcpcb_listen in TCPlp. This is necessary because the mTcbListen + * field, though opaque in its declaration, is treated as struct tcpcb in the + * TCP implementation. + */ +#define OT_TCP_LISTENER_TCB_SIZE_BASE 16 +#define OT_TCP_LISTENER_TCB_NUM_PTR 3 + /** * This structure represents a TCP listener. * @@ -610,14 +636,17 @@ typedef void (*otTcpAcceptDone)(otTcpListener *aListener, otTcpEndpoint *aEndpoi */ struct otTcpListener { - struct otTcpListener *mNext; ///< A pointer to the next TCP listener (internal use only) - otInstance * mInstance; ///< A pointer to the OpenThread instance associated with this TCP listener - void * mContext; ///< A pointer to application-specific context + union + { + uint8_t mSize[OT_TCP_LISTENER_TCB_SIZE_BASE + OT_TCP_LISTENER_TCB_NUM_PTR * sizeof(void *)]; + void * mAlign; + } mTcbListen; + + struct otTcpListener *mNext; ///< A pointer to the next TCP listener (internal use only) + void * mContext; ///< A pointer to application-specific context otTcpAcceptReady mAcceptReadyCallback; ///< "Accept ready" callback function otTcpAcceptDone mAcceptDoneCallback; ///< "Accept done" callback function - - /* Other implementation-defined fields go here. */ }; /** diff --git a/src/core/api/tcp_api.cpp b/src/core/api/tcp_api.cpp index 62a1de50a..8721d1e7a 100644 --- a/src/core/api/tcp_api.cpp +++ b/src/core/api/tcp_api.cpp @@ -87,7 +87,7 @@ otError otTcpSendByExtension(otTcpEndpoint *aEndpoint, size_t aNumBytes, uint32_ return AsCoreType(aEndpoint).SendByExtension(aNumBytes, aFlags); } -otError otTcpReceiveByReference(const otTcpEndpoint *aEndpoint, const otLinkedBuffer **aBuffer) +otError otTcpReceiveByReference(otTcpEndpoint *aEndpoint, const otLinkedBuffer **aBuffer) { return AsCoreType(aEndpoint).ReceiveByReference(*aBuffer); } diff --git a/src/core/net/tcp6.cpp b/src/core/net/tcp6.cpp index 9ef8be467..d12ba76f2 100644 --- a/src/core/net/tcp6.cpp +++ b/src/core/net/tcp6.cpp @@ -42,11 +42,19 @@ #include "common/error.hpp" #include "common/instance.hpp" #include "common/logging.hpp" +#include "common/random.hpp" +#include "net/checksum.hpp" #include "net/ip6.hpp" +#include "net/netif.hpp" + +#include "../../third_party/tcplp/tcplp.h" namespace ot { namespace Ip6 { +using ot::Encoding::BigEndian::HostSwap16; +using ot::Encoding::BigEndian::HostSwap32; + Tcp::Tcp(Instance &aInstance) : InstanceLocator(aInstance) , mTimer(aInstance, Tcp::HandleTimer) @@ -57,7 +65,8 @@ Tcp::Tcp(Instance &aInstance) Error Tcp::Endpoint::Initialize(Instance &aInstance, otTcpEndpointInitializeArgs &aArgs) { - Error error; + Error error; + struct tcpcb &tp = GetTcb(); SuccessOrExit(error = aInstance.Get().mEndpoints.Add(*this)); @@ -68,9 +77,30 @@ Error Tcp::Endpoint::Initialize(Instance &aInstance, otTcpEndpointInitializeArgs mReceiveAvailableCallback = aArgs.mReceiveAvailableCallback; mDisconnectedCallback = aArgs.mDisconnectedCallback; - mInstance = &aInstance; - memset(mTimers, 0x00, sizeof(mTimers)); + memset(&mSockAddr, 0x00, sizeof(mSockAddr)); + memset(&tp, 0x00, sizeof(tp)); + + /* + * Initialize buffers --- formerly in initialize_tcb. + */ + { + uint8_t *recvbuf = static_cast(aArgs.mReceiveBuffer); + size_t recvbuflen = aArgs.mReceiveBufferSize - ((aArgs.mReceiveBufferSize + 8) / 9); + uint8_t *reassbmp = recvbuf + recvbuflen; + + lbuf_init(&tp.sendbuf); + cbuf_init(&tp.recvbuf, recvbuf, recvbuflen); + tp.reassbmp = reassbmp; + bmp_init(tp.reassbmp, BITS_TO_BYTES(recvbuflen)); + } + + tp.accepted_from = nullptr; + initialize_tcb(&tp); + + /* Note that we do not need to zero-initialize mReceiveLinks. */ + + tp.instance = &aInstance; exit: return error; @@ -78,57 +108,100 @@ exit: Instance &Tcp::Endpoint::GetInstance(void) { - return AsCoreType(mInstance); + struct tcpcb &tp = GetTcb(); + + return AsCoreType(tp.instance); } const SockAddr &Tcp::Endpoint::GetLocalAddress(void) const { + const struct tcpcb &tp = GetTcb(); + static otSockAddr temp; + + memcpy(&temp.mAddress, &tp.laddr, sizeof(temp.mAddress)); + temp.mPort = HostSwap16(tp.lport); + return AsCoreType(&temp); } const SockAddr &Tcp::Endpoint::GetPeerAddress(void) const { + const struct tcpcb &tp = GetTcb(); + static otSockAddr temp; + + memcpy(&temp.mAddress, &tp.faddr, sizeof(temp.mAddress)); + temp.mPort = HostSwap16(tp.fport); + return AsCoreType(&temp); } Error Tcp::Endpoint::Bind(const SockAddr &aSockName) { - OT_UNUSED_VARIABLE(aSockName); + Error error; + struct tcpcb &tp = GetTcb(); - return kErrorNotImplemented; + VerifyOrExit(!AsCoreType(&aSockName.mAddress).IsUnspecified(), error = kErrorInvalidArgs); + VerifyOrExit(GetInstance().Get().CanBind(aSockName), error = kErrorInvalidState); + + memcpy(&tp.laddr, &aSockName.mAddress, sizeof(tp.laddr)); + tp.lport = HostSwap16(aSockName.mPort); + error = kErrorNone; + +exit: + return error; } Error Tcp::Endpoint::Connect(const SockAddr &aSockName, uint32_t aFlags) { - OT_UNUSED_VARIABLE(aSockName); + Error error = kErrorNone; + struct tcpcb & tp = GetTcb(); + struct sockaddr_in6 sin6p; + OT_UNUSED_VARIABLE(aFlags); - return kErrorNotImplemented; + VerifyOrExit(tp.t_state == TCP6S_CLOSED, error = kErrorInvalidState); + + memcpy(&sin6p.sin6_addr, &aSockName.mAddress, sizeof(sin6p.sin6_addr)); + sin6p.sin6_port = HostSwap16(aSockName.mPort); + error = BsdErrorToOtError(tcp6_usr_connect(&tp, &sin6p)); + +exit: + return error; } Error Tcp::Endpoint::SendByReference(otLinkedBuffer &aBuffer, uint32_t aFlags) { - OT_UNUSED_VARIABLE(aBuffer); - OT_UNUSED_VARIABLE(aFlags); + struct tcpcb &tp = GetTcb(); - return kErrorNotImplemented; + return BsdErrorToOtError(tcp_usr_send(&tp, (aFlags & OT_TCP_SEND_MORE_TO_COME) != 0, &aBuffer, 0)); } Error Tcp::Endpoint::SendByExtension(size_t aNumBytes, uint32_t aFlags) { - OT_UNUSED_VARIABLE(aNumBytes); - OT_UNUSED_VARIABLE(aFlags); + Error error; + bool moreToCome = (aFlags & OT_TCP_SEND_MORE_TO_COME) != 0; + struct tcpcb &tp = GetTcb(); + int bsdError; - return kErrorNotImplemented; + VerifyOrExit(lbuf_head(&tp.sendbuf) != nullptr, error = kErrorInvalidState); + + bsdError = tcp_usr_send(&tp, moreToCome ? 1 : 0, nullptr, aNumBytes); + SuccessOrExit(error = BsdErrorToOtError(bsdError)); + +exit: + return error; } -Error Tcp::Endpoint::ReceiveByReference(const otLinkedBuffer *&aBuffer) const +Error Tcp::Endpoint::ReceiveByReference(const otLinkedBuffer *&aBuffer) { - OT_UNUSED_VARIABLE(aBuffer); + struct tcpcb &tp = GetTcb(); - return kErrorNotImplemented; + cbuf_reference(&tp.recvbuf, &mReceiveLinks[0], &mReceiveLinks[1]); + aBuffer = &mReceiveLinks[0]; + + return kErrorNone; } Error Tcp::Endpoint::ReceiveContiguify(void) @@ -138,20 +211,35 @@ Error Tcp::Endpoint::ReceiveContiguify(void) Error Tcp::Endpoint::CommitReceive(size_t aNumBytes, uint32_t aFlags) { - OT_UNUSED_VARIABLE(aNumBytes); + Error error = kErrorNone; + struct tcpcb &tp = GetTcb(); + OT_UNUSED_VARIABLE(aFlags); - return kErrorNotImplemented; + VerifyOrExit(cbuf_used_space(&tp.recvbuf) >= aNumBytes, error = kErrorFailed); + VerifyOrExit(aNumBytes > 0, error = kErrorNone); + + cbuf_pop(&tp.recvbuf, aNumBytes); + error = BsdErrorToOtError(tcp_usr_rcvd(&tp)); + +exit: + return error; } Error Tcp::Endpoint::SendEndOfStream(void) { - return kErrorNotImplemented; + struct tcpcb &tp = GetTcb(); + + return BsdErrorToOtError(tcp_usr_shutdown(&tp)); } Error Tcp::Endpoint::Abort(void) { - return kErrorNotImplemented; + struct tcpcb &tp = GetTcb(); + + tcp_usr_abort(&tp); + /* connection_lost will do any reinitialization work for this socket. */ + return kErrorNone; } Error Tcp::Endpoint::Deinitialize(void) @@ -163,25 +251,59 @@ Error Tcp::Endpoint::Deinitialize(void) SuccessOrExit(error = tcp.mEndpoints.Remove(*this)); SetNext(nullptr); + SuccessOrExit(error = Abort()); + exit: return error; } uint8_t Tcp::Endpoint::TimerFlagToIndex(uint8_t aTimerFlag) { - OT_UNUSED_VARIABLE(aTimerFlag); - /* - * TODO: Convert from the timer flag provided by TCPlp to the index in - * our timers array. - */ - return 0; + uint8_t timerIndex = 0; + + switch (aTimerFlag) + { + case TT_DELACK: + timerIndex = kTimerDelack; + break; + case TT_REXMT: + case TT_PERSIST: + timerIndex = kTimerRexmtPersist; + break; + case TT_KEEP: + timerIndex = kTimerKeep; + break; + case TT_2MSL: + timerIndex = kTimer2Msl; + break; + } + + return timerIndex; } bool Tcp::Endpoint::IsTimerActive(uint8_t aTimerIndex) { - OT_UNUSED_VARIABLE(aTimerIndex); - /* TODO: Check whether TCPlp has marked this timer as active. */ - return false; + bool active = false; + struct tcpcb *tp = &GetTcb(); + + OT_ASSERT(aTimerIndex < kNumTimers); + switch (aTimerIndex) + { + case kTimerDelack: + active = tcp_timer_active(tp, TT_DELACK); + break; + case kTimerRexmtPersist: + active = tcp_timer_active(tp, TT_REXMT) || tcp_timer_active(tp, TT_PERSIST); + break; + case kTimerKeep: + active = tcp_timer_active(tp, TT_KEEP); + break; + case kTimer2Msl: + active = tcp_timer_active(tp, TT_2MSL); + break; + } + + return active; } void Tcp::Endpoint::SetTimer(uint8_t aTimerFlag, uint32_t aDelay) @@ -219,6 +341,9 @@ void Tcp::Endpoint::CancelTimer(uint8_t aTimerFlag) bool Tcp::Endpoint::FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, TimeMilli &aEarliestFutureExpiry) { + bool calledUserCallback = false; + struct tcpcb *tp = &GetTcb(); + /* * NOTE: Firing a timer might potentially activate/deactivate other timers. * If timers x and y expire at the same time, but the callback for timer x @@ -242,8 +367,36 @@ bool Tcp::Endpoint::FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, Tim if (expiry <= aNow) { - /* TODO: Call TCPlp's callback for this timer. */ - /* If a user callback is called, then return true. */ + /* + * If a user callback is called, then return true. For TCPlp, + * this only happens if the connection is dropped (e.g., it + * times out). + */ + int dropped; + + switch (timerIndex) + { + case kTimerDelack: + dropped = tcp_timer_delack(tp); + break; + case kTimerRexmtPersist: + if (tcp_timer_active(tp, TT_REXMT)) + { + dropped = tcp_timer_rexmt(tp); + } + else + { + dropped = tcp_timer_persist(tp); + } + break; + case kTimerKeep: + dropped = tcp_timer_keep(tp); + break; + case kTimer2Msl: + dropped = tcp_timer_2msl(tp); + break; + } + VerifyOrExit(dropped == 0, calledUserCallback = true); } else { @@ -253,12 +406,31 @@ bool Tcp::Endpoint::FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, Tim } } - return false; +exit: + return calledUserCallback; +} + +bool Tcp::Endpoint::Matches(const MessageInfo &aMessageInfo) const +{ + bool matches = false; + const struct tcpcb *tp = &GetTcb(); + + VerifyOrExit(tp->t_state != TCP6S_CLOSED); + VerifyOrExit(tp->lport == HostSwap16(aMessageInfo.GetSockPort())); + VerifyOrExit(tp->fport == HostSwap16(aMessageInfo.GetPeerPort())); + VerifyOrExit(GetLocalIp6Address().IsUnspecified() || GetLocalIp6Address() == aMessageInfo.GetSockAddr()); + VerifyOrExit(GetForeignIp6Address() == aMessageInfo.GetPeerAddr()); + + matches = true; + +exit: + return matches; } Error Tcp::Listener::Initialize(Instance &aInstance, otTcpListenerInitializeArgs &aArgs) { - Error error; + Error error; + struct tcpcb_listen *tpl = &GetTcbListen(); SuccessOrExit(error = aInstance.Get().mListeners.Add(*this)); @@ -266,7 +438,8 @@ Error Tcp::Listener::Initialize(Instance &aInstance, otTcpListenerInitializeArgs mAcceptReadyCallback = aArgs.mAcceptReadyCallback; mAcceptDoneCallback = aArgs.mAcceptDoneCallback; - mInstance = &aInstance; + memset(tpl, 0x00, sizeof(struct tcpcb_listen)); + tpl->instance = &aInstance; exit: return error; @@ -274,19 +447,36 @@ exit: Instance &Tcp::Listener::GetInstance(void) { - return AsCoreType(mInstance); + struct tcpcb_listen *tpl = &GetTcbListen(); + + return AsCoreType(tpl->instance); } Error Tcp::Listener::Listen(const SockAddr &aSockName) { - OT_UNUSED_VARIABLE(aSockName); + Error error; + uint16_t port = HostSwap16(aSockName.mPort); + struct tcpcb_listen *tpl = &GetTcbListen(); - return kErrorNotImplemented; + VerifyOrExit(GetInstance().Get().CanBind(aSockName), error = kErrorInvalidState); + + memcpy(&tpl->laddr, &aSockName.mAddress, sizeof(tpl->laddr)); + tpl->lport = port; + tpl->t_state = TCP6S_LISTEN; + error = kErrorNone; + +exit: + return error; } Error Tcp::Listener::StopListening(void) { - return kErrorNotImplemented; + struct tcpcb_listen *tpl = &GetTcbListen(); + + memset(&tpl->laddr, 0x00, sizeof(tpl->laddr)); + tpl->lport = 0; + tpl->t_state = TCP6S_CLOSED; + return kErrorNone; } Error Tcp::Listener::Deinitialize(void) @@ -302,27 +492,238 @@ exit: return error; } +bool Tcp::Listener::Matches(const MessageInfo &aMessageInfo) const +{ + bool matches = false; + const struct tcpcb_listen *tpl = &GetTcbListen(); + + VerifyOrExit(tpl->t_state == TCP6S_LISTEN); + VerifyOrExit(tpl->lport == HostSwap16(aMessageInfo.GetSockPort())); + VerifyOrExit(GetLocalIp6Address().IsUnspecified() || GetLocalIp6Address() == aMessageInfo.GetSockAddr()); + + matches = true; + +exit: + return matches; +} + Error Tcp::HandleMessage(ot::Ip6::Header &aIp6Header, Message &aMessage, MessageInfo &aMessageInfo) { - OT_UNUSED_VARIABLE(aIp6Header); - OT_UNUSED_VARIABLE(aMessage); - OT_UNUSED_VARIABLE(aMessageInfo); - Error error = kErrorNotImplemented; - for (Endpoint &active : mEndpoints) + /* + * The type uint32_t was chosen for alignment purposes. The size is the + * maximum TCP header size, including options. + */ + uint32_t header[15]; + + uint16_t length = aIp6Header.GetPayloadLength(); + uint8_t headerSize; + + struct ip6_hdr *ip6Header; + struct tcphdr * tcpHeader; + + Endpoint *endpoint; + Endpoint *endpointPrev; + + Listener *listener; + Listener *listenerPrev; + + VerifyOrExit(length == aMessage.GetLength() - aMessage.GetOffset(), error = kErrorParse); + VerifyOrExit(length >= sizeof(Tcp::Header), error = kErrorParse); + SuccessOrExit(error = aMessage.Read(aMessage.GetOffset() + offsetof(struct tcphdr, th_off_x2), headerSize)); + headerSize = static_cast((headerSize >> TH_OFF_SHIFT) << 2); + VerifyOrExit(headerSize >= sizeof(struct tcphdr) && headerSize <= sizeof(header) && + static_cast(headerSize) <= length, + error = kErrorParse); + SuccessOrExit(error = Checksum::VerifyMessageChecksum(aMessage, aMessageInfo, kProtoTcp)); + SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), &header[0], headerSize)); + + ip6Header = reinterpret_cast(&aIp6Header); + tcpHeader = reinterpret_cast(&header[0]); + tcp_fields_to_host(tcpHeader); + + aMessageInfo.mPeerPort = HostSwap16(tcpHeader->th_sport); + aMessageInfo.mSockPort = HostSwap16(tcpHeader->th_dport); + + endpoint = mEndpoints.FindMatching(aMessageInfo, endpointPrev); + if (endpoint != nullptr) { - OT_UNUSED_VARIABLE(active); + struct signals sig; + int nextAction; + struct tcpcb * tp = &endpoint->GetTcb(); + + otLinkedBuffer *priorHead = lbuf_head(&tp->sendbuf); + + memset(&sig, 0x00, sizeof(sig)); + nextAction = tcp_input(ip6Header, tcpHeader, &aMessage, tp, nullptr, &sig); + if (nextAction != RELOOKUP_REQUIRED) + { + ProcessSignals(*endpoint, priorHead, sig); + ExitNow(); + } + /* If the matching socket was in the TIME-WAIT state, then we try passive sockets. */ } - for (Listener &passive : mListeners) + listener = mListeners.FindMatching(aMessageInfo, listenerPrev); + if (listener != nullptr) { - OT_UNUSED_VARIABLE(passive); + struct tcpcb_listen *tpl = &listener->GetTcbListen(); + + tcp_input(ip6Header, tcpHeader, &aMessage, nullptr, tpl, nullptr); + ExitNow(); + } + + tcp_dropwithreset(ip6Header, tcpHeader, nullptr, &InstanceLocator::GetInstance(), length - headerSize, + ECONNREFUSED); + +exit: + return error; +} + +void Tcp::ProcessSignals(Endpoint &aEndpoint, otLinkedBuffer *aPriorHead, struct signals &aSignals) +{ + VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed()); + if (aEndpoint.mSendDoneCallback != nullptr) + { + otLinkedBuffer *curr = aPriorHead; + + for (int i = 0; i != aSignals.links_popped; i++) + { + otLinkedBuffer *next = curr->mNext; + + VerifyOrExit(i == 0 || (IsInitialized(aEndpoint) && !aEndpoint.IsClosed())); + + curr->mNext = nullptr; + aEndpoint.mSendDoneCallback(&aEndpoint, curr); + curr = next; + } + } + + VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed()); + if (aSignals.conn_established && aEndpoint.mEstablishedCallback != nullptr) + { + aEndpoint.mEstablishedCallback(&aEndpoint); + } + + VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed()); + if ((aSignals.recvbuf_notempty || aSignals.rcvd_fin) && aEndpoint.mReceiveAvailableCallback != nullptr) + { + aEndpoint.mReceiveAvailableCallback(&aEndpoint, cbuf_used_space(&aEndpoint.GetTcb().recvbuf), + aEndpoint.GetTcb().reass_fin_index != -1, + cbuf_free_space(&aEndpoint.GetTcb().recvbuf)); + } + + VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed()); + if (aEndpoint.GetTcb().t_state == TCP6S_TIME_WAIT && aEndpoint.mDisconnectedCallback != nullptr) + { + aEndpoint.mDisconnectedCallback(&aEndpoint, OT_TCP_DISCONNECTED_REASON_TIME_WAIT); + } + +exit: + return; +} + +Error Tcp::BsdErrorToOtError(int aBsdError) +{ + Error error = kErrorFailed; + + switch (aBsdError) + { + case 0: + error = kErrorNone; + break; } return error; } +bool Tcp::CanBind(const SockAddr &aSockName) +{ + uint16_t port = HostSwap16(aSockName.mPort); + bool allowed = false; + + for (Endpoint *endpoint = mEndpoints.GetHead(); endpoint != nullptr; endpoint = endpoint->GetNext()) + { + struct tcpcb *tp = &endpoint->GetTcb(); + + if (tp->lport == port) + { + VerifyOrExit(!aSockName.GetAddress().IsUnspecified()); + VerifyOrExit(!reinterpret_cast
(&tp->laddr)->IsUnspecified()); + VerifyOrExit(memcmp(&endpoint->GetTcb().laddr, &aSockName.mAddress, sizeof(tp->laddr)) != 0); + } + } + + for (Listener *listener = mListeners.GetHead(); listener != nullptr; listener = listener->GetNext()) + { + struct tcpcb_listen *tpl = &listener->GetTcbListen(); + + if (tpl->lport == port) + { + VerifyOrExit(!aSockName.GetAddress().IsUnspecified()); + VerifyOrExit(!reinterpret_cast
(&tpl->laddr)->IsUnspecified()); + VerifyOrExit(memcmp(&tpl->laddr, &aSockName.mAddress, sizeof(tpl->laddr)) != 0); + } + } + + allowed = true; + +exit: + return allowed; +} + +bool Tcp::AutoBind(const SockAddr &aPeer, SockAddr &aToBind, bool aBindAddress, bool aBindPort) +{ + bool success; + + if (aBindAddress) + { + MessageInfo peerInfo; + const Netif::UnicastAddress *netifAddress; + + peerInfo.Clear(); + peerInfo.SetPeerAddr(aPeer.GetAddress()); + netifAddress = InstanceLocator::GetInstance().Get().SelectSourceAddress(peerInfo); + VerifyOrExit(netifAddress != nullptr, success = false); + aToBind.GetAddress() = netifAddress->GetAddress(); + } + + if (aBindPort) + { + /* + * TODO: Use a less naive algorithm to allocate ephemeral ports. For + * example, see RFC 6056. + */ + + for (uint16_t i = 0; i != kDynamicPortMax - kDynamicPortMin + 1; i++) + { + aToBind.SetPort(mEphemeralPort); + + if (mEphemeralPort == kDynamicPortMax) + { + mEphemeralPort = kDynamicPortMin; + } + else + { + mEphemeralPort++; + } + + if (CanBind(aToBind)) + { + ExitNow(success = true); + } + } + + ExitNow(success = false); + } + + success = CanBind(aToBind); + +exit: + return success; +} + void Tcp::HandleTimer(Timer &aTimer) { OT_ASSERT(&aTimer == &aTimer.GetInstance().Get().mTimer); @@ -395,4 +796,227 @@ restart: } // namespace Ip6 } // namespace ot +/* + * Implement TCPlp system stubs declared in tcplp.h. + * + * Because these functions have C linkage, it is important that only one + * definition is given for each function name, regardless of the namespace it + * in. For example, if we give two definitions of tcplp_sys_new_message, we + * will get errors, even if they are in different namespaces. To avoid + * confusion, I've put these functions outside of any namespace. + */ + +using namespace ot; +using namespace ot::Ip6; + +extern "C" { + +otMessage *tcplp_sys_new_message(otInstance *aInstance) +{ + Instance &instance = AsCoreType(aInstance); + Message * message = instance.Get().NewMessage(0); + + if (message) + { + message->SetLinkSecurityEnabled(true); + } + + return message; +} + +void tcplp_sys_free_message(otInstance *aInstance, otMessage *aMessage) +{ + OT_UNUSED_VARIABLE(aInstance); + Message &message = AsCoreType(aMessage); + message.Free(); +} + +void tcplp_sys_send_message(otInstance *aInstance, otMessage *aMessage, otMessageInfo *aMessageInfo) +{ + Instance & instance = AsCoreType(aInstance); + Message & message = AsCoreType(aMessage); + MessageInfo &info = AsCoreType(aMessageInfo); + + otLogDebgTcp("Sending TCP segment: payload_size = %d", static_cast(message.GetLength())); + + IgnoreError(instance.Get().SendDatagram(message, info, kProtoTcp)); +} + +uint32_t tcplp_sys_get_ticks(void) +{ + return TimerMilli::GetNow().GetValue(); +} + +uint32_t tcplp_sys_get_millis(void) +{ + return TimerMilli::GetNow().GetValue(); +} + +void tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay) +{ + Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aTcb); + endpoint.SetTimer(aTimerFlag, aDelay); +} + +void tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag) +{ + Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aTcb); + endpoint.CancelTimer(aTimerFlag); +} + +struct tcpcb *tcplp_sys_accept_ready(struct tcpcb_listen *aTcbListen, struct in6_addr *aAddr, uint16_t aPort) +{ + Tcp::Listener & listener = Tcp::Listener::FromTcbListen(*aTcbListen); + Tcp & tcp = listener.GetInstance().Get(); + struct tcpcb * rv = (struct tcpcb *)-1; + otSockAddr addr; + otTcpEndpoint * endpointPtr; + otTcpIncomingConnectionAction action; + + VerifyOrExit(listener.mAcceptReadyCallback != nullptr); + + memcpy(&addr.mAddress, aAddr, sizeof(addr.mAddress)); + addr.mPort = HostSwap16(aPort); + action = listener.mAcceptReadyCallback(&listener, &addr, &endpointPtr); + + VerifyOrExit(tcp.IsInitialized(listener) && !listener.IsClosed()); + + switch (action) + { + case OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT: + { + Tcp::Endpoint &endpoint = AsCoreType(endpointPtr); + + /* + * The documentation says that the user must initialize the + * endpoint before passing it here, so we do a sanity check to make + * sure the endpoint is initialized and closed. That check may not + * be necessary, but we do it anyway. + */ + VerifyOrExit(tcp.IsInitialized(endpoint) && endpoint.IsClosed()); + + rv = &endpoint.GetTcb(); + + break; + } + case OT_TCP_INCOMING_CONNECTION_ACTION_DEFER: + rv = nullptr; + break; + case OT_TCP_INCOMING_CONNECTION_ACTION_REFUSE: + rv = (struct tcpcb *)-1; + break; + } + +exit: + return rv; +} + +bool tcplp_sys_accepted_connection(struct tcpcb_listen *aTcbListen, + struct tcpcb * aAccepted, + struct in6_addr * aAddr, + uint16_t aPort) +{ + Tcp::Listener &listener = Tcp::Listener::FromTcbListen(*aTcbListen); + Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aAccepted); + Tcp & tcp = endpoint.GetInstance().Get(); + bool accepted = true; + + if (listener.mAcceptDoneCallback != nullptr) + { + otSockAddr addr; + + memcpy(&addr.mAddress, aAddr, sizeof(addr.mAddress)); + addr.mPort = HostSwap16(aPort); + listener.mAcceptDoneCallback(&listener, &endpoint, &addr); + + if (!tcp.IsInitialized(endpoint) || endpoint.IsClosed()) + { + accepted = false; + } + } + + return accepted; +} + +void tcplp_sys_connection_lost(struct tcpcb *aTcb, uint8_t aErrNum) +{ + Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aTcb); + + if (endpoint.mDisconnectedCallback != nullptr) + { + otTcpDisconnectedReason reason; + + switch (aErrNum) + { + case CONN_LOST_NORMAL: + reason = OT_TCP_DISCONNECTED_REASON_NORMAL; + break; + case ECONNREFUSED: + reason = OT_TCP_DISCONNECTED_REASON_REFUSED; + break; + case ETIMEDOUT: + reason = OT_TCP_DISCONNECTED_REASON_TIMED_OUT; + break; + case ECONNRESET: + default: + reason = OT_TCP_DISCONNECTED_REASON_RESET; + break; + } + endpoint.mDisconnectedCallback(&endpoint, reason); + } +} + +void tcplp_sys_on_state_change(struct tcpcb *aTcb, int aNewState) +{ + if (aNewState == TCP6S_CLOSED) + { + /* Re-initialize the TCB. */ + cbuf_pop(&aTcb->recvbuf, cbuf_used_space(&aTcb->recvbuf)); + aTcb->accepted_from = nullptr; + initialize_tcb(aTcb); + } + /* Any adaptive changes to the sleep interval would go here. */ +} + +void tcplp_sys_log(const char *aFormat, ...) +{ + char buffer[128]; + va_list args; + va_start(args, aFormat); + vsnprintf(buffer, sizeof(buffer), aFormat, args); + va_end(args); + + otLogDebgTcp(buffer); +} + +bool tcplp_sys_autobind(otInstance * aInstance, + const otSockAddr *aPeer, + otSockAddr * aToBind, + bool aBindAddress, + bool aBindPort) +{ + Instance &instance = AsCoreType(aInstance); + + return instance.Get().AutoBind(*static_cast(aPeer), *static_cast(aToBind), + aBindAddress, aBindPort); +} + +uint32_t tcplp_sys_generate_isn() +{ + uint32_t isn; + IgnoreError(Random::Crypto::FillBuffer(reinterpret_cast(&isn), sizeof(isn))); + return isn; +} + +uint16_t tcplp_sys_hostswap16(uint16_t aHostPort) +{ + return HostSwap16(aHostPort); +} + +uint32_t tcplp_sys_hostswap32(uint32_t aHostPort) +{ + return HostSwap32(aHostPort); +} +} + #endif // OPENTHREAD_CONFIG_TCP_ENABLE diff --git a/src/core/net/tcp6.hpp b/src/core/net/tcp6.hpp index 76761b46e..9cd666283 100644 --- a/src/core/net/tcp6.hpp +++ b/src/core/net/tcp6.hpp @@ -46,6 +46,8 @@ #include "net/ip6_headers.hpp" #include "net/socket.hpp" +#include "../../third_party/tcplp/tcplp.h" + namespace ot { namespace Ip6 { @@ -73,6 +75,7 @@ public: class Endpoint : public otTcpEndpoint, public LinkedListEntry { friend class Tcp; + friend class LinkedList; public: /** @@ -236,7 +239,7 @@ public: * @retval kErrorNone Successfully completed the operation. * @retval kErrorFailed Failed to complete the operation. */ - Error ReceiveByReference(const otLinkedBuffer *&aBuffer) const; + Error ReceiveByReference(const otLinkedBuffer *&aBuffer); /** * Reorganizes the receive buffer to be entirely contiguous in memory. @@ -329,7 +332,32 @@ public: */ Error Deinitialize(void); + /** + * Converts a reference to a struct tcpcb to a reference to its + * enclosing Endpoint. + */ + static Endpoint &FromTcb(struct tcpcb &aTcb) { return *reinterpret_cast(&aTcb); } + + /** + * Obtains a reference to this Endpoint's struct tcpcb. + */ + struct tcpcb &GetTcb(void) { return *reinterpret_cast(&mTcb); } + + /** + * Obtains a const reference to this Endpoint's struct tcpcb. + */ + const struct tcpcb &GetTcb(void) const { return *reinterpret_cast(&mTcb); } + + /** + * Checks if this Endpoint is in the closed state. + */ + bool IsClosed(void) const { return GetTcb().t_state == TCP6S_CLOSED; } + private: + friend void ::tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay); + friend void ::tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag); + friend void ::tcplp_sys_connection_lost(struct tcpcb *aTcb, uint8_t aErrNum); + enum : uint8_t { kTimerDelack = 0, @@ -345,14 +373,27 @@ public: void SetTimer(uint8_t aTimerFlag, uint32_t aDelay); void CancelTimer(uint8_t aTimerFlag); bool FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, TimeMilli &aEarliestFutureExpiry); + + Address & GetLocalIp6Address(void) { return *reinterpret_cast
(&GetTcb().laddr); } + const Address &GetLocalIp6Address(void) const { return *reinterpret_cast(&GetTcb().laddr); } + Address & GetForeignIp6Address(void) { return *reinterpret_cast
(&GetTcb().faddr); } + const Address &GetForeignIp6Address(void) const { return *reinterpret_cast(&GetTcb().faddr); } + bool Matches(const MessageInfo &aMessageInfo) const; }; + static_assert(sizeof(struct tcpcb) == sizeof(Endpoint::mTcb), "mTcb field in otTcpEndpoint is sized incorrectly"); + static_assert(alignof(struct tcpcb) == alignof(decltype(Endpoint::mTcb)), + "mTcb field in otTcpEndpoint is aligned incorrectly"); + static_assert(offsetof(Endpoint, mTcb) == 0, "mTcb field in otTcpEndpoint has nonzero offset"); + /** * This class represents a TCP/IPv6 listener. * */ class Listener : public otTcpListener, public LinkedListEntry { + friend class LinkedList; + public: /** * Initializes a TCP listener. @@ -438,8 +479,49 @@ public: * */ Error Deinitialize(void); + + /** + * Converts a reference to a struct tcpcb_listen to a reference to its + * enclosing Listener. + */ + static Listener &FromTcbListen(struct tcpcb_listen &aTcbListen) + { + return *reinterpret_cast(&aTcbListen); + } + + /** + * Obtains a reference to this Listener's struct tcpcb_listen. + */ + struct tcpcb_listen &GetTcbListen(void) { return *reinterpret_cast(&mTcbListen); } + + /** + * Obtains a const reference to this Listener's struct tcpcb_listen. + */ + const struct tcpcb_listen &GetTcbListen(void) const + { + return *reinterpret_cast(&mTcbListen); + } + + /** + * Checks if this Listener is in the closed state. + */ + bool IsClosed(void) const { return GetTcbListen().t_state == TCP6S_CLOSED; } + + private: + Address & GetLocalIp6Address(void) { return *reinterpret_cast
(&GetTcbListen().laddr); } + const Address &GetLocalIp6Address(void) const + { + return *reinterpret_cast(&GetTcbListen().laddr); + } + bool Matches(const MessageInfo &aMessageInfo) const; }; + static_assert(sizeof(struct tcpcb_listen) == sizeof(Listener::mTcbListen), + "mTcbListen field in otTcpListener is sized incorrectly"); + static_assert(alignof(struct tcpcb_listen) == alignof(decltype(Listener::mTcbListen)), + "mTcbListen field in otTcpListener is aligned incorrectly"); + static_assert(offsetof(Listener, mTcbListen) == 0, "mTcbListen field in otTcpEndpoint has nonzero offset"); + /** * This class implements TCP header parsing. * @@ -546,6 +628,31 @@ public: */ Error HandleMessage(ot::Ip6::Header &aIp6Header, Message &aMessage, MessageInfo &aMessageInfo); + /** + * Automatically selects a local address and/or port for communication with the specified peer. + * + * @param[in] aPeer The peer's address and port. + * @param[in,out] aToBind The SockAddr into which to store the selected address and/or port. + * @param[in] aBindAddress If true, the local address is selected; if not, the current address + * in @p aToBind is treated as a given. + * @param[in] aBindPort If true, the local port is selected; if not, the current port in + * @p aToBind is treated as a given. + * + * @returns True if successful, false otherwise. + * + */ + bool AutoBind(const SockAddr &aPeer, SockAddr &aToBind, bool aBindAddress, bool aBindPort); + + /** + * Checks if an Endpoint is in the list of initialized endpoints. + */ + bool IsInitialized(const Endpoint &aEndpoint) const { return mEndpoints.Contains(aEndpoint); } + + /** + * Checks if a Listener is in the list of initialized Listeners. + */ + bool IsInitialized(const Listener &aListener) const { return mListeners.Contains(aListener); } + private: enum { @@ -553,6 +660,11 @@ private: kDynamicPortMax = 65535, ///< Service Name and Transport Protocol Port Number Registry }; + void ProcessSignals(Endpoint &aEndpoint, otLinkedBuffer *aPriorHead, struct signals &aSignals); + + static Error BsdErrorToOtError(int aBsdError); + bool CanBind(const SockAddr &aSockName); + static void HandleTimer(Timer &aTimer); void ProcessTimers(void); diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 863548095..ac7df2f03 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -28,6 +28,8 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am +COMMON_LIBTOOLFLAGS = --preserve-dup-deps + # # Local headers to build against and distribute but not to install # since they are not part of the package. @@ -74,7 +76,6 @@ AM_CPPFLAGS += \ endif COMMON_LDADD = \ - $(top_builddir)/third_party/tcplp/libtcplp.a \ $(NULL) if OPENTHREAD_ENABLE_NCP @@ -84,6 +85,8 @@ COMMON_LDADD += \ endif COMMON_LDADD += \ + $(top_builddir)/src/core/libopenthread-ftd.a \ + $(top_builddir)/third_party/tcplp/libtcplp.a \ $(top_builddir)/src/core/libopenthread-ftd.a \ -lpthread \ $(NULL) @@ -184,128 +187,168 @@ COMMON_SOURCES = test_platform.cpp test_util.cpp # Source, compiler, and linker options for test programs. -ot_test_aes_LDADD = $(COMMON_LDADD) -ot_test_aes_SOURCES = $(COMMON_SOURCES) test_aes.cpp +ot_test_aes_LDADD = $(COMMON_LDADD) +ot_test_aes_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_aes_SOURCES = $(COMMON_SOURCES) test_aes.cpp -ot_test_array_LDADD = $(COMMON_LDADD) -ot_test_array_SOURCES = $(COMMON_SOURCES) test_array.cpp +ot_test_array_LDADD = $(COMMON_LDADD) +ot_test_array_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_array_SOURCES = $(COMMON_SOURCES) test_array.cpp -ot_test_binary_search_LDADD = $(COMMON_LDADD) -ot_test_binary_search_SOURCES = $(COMMON_SOURCES) test_binary_search.cpp +ot_test_binary_search_LDADD = $(COMMON_LDADD) +ot_test_binary_search_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_binary_search_SOURCES = $(COMMON_SOURCES) test_binary_search.cpp -ot_test_checksum_LDADD = $(COMMON_LDADD) -ot_test_checksum_SOURCES = $(COMMON_SOURCES) test_checksum.cpp +ot_test_checksum_LDADD = $(COMMON_LDADD) +ot_test_checksum_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_checksum_SOURCES = $(COMMON_SOURCES) test_checksum.cpp -ot_test_child_LDADD = $(COMMON_LDADD) -ot_test_child_SOURCES = $(COMMON_SOURCES) test_child.cpp +ot_test_child_LDADD = $(COMMON_LDADD) +ot_test_child_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_child_SOURCES = $(COMMON_SOURCES) test_child.cpp -ot_test_child_table_LDADD = $(COMMON_LDADD) -ot_test_child_table_SOURCES = $(COMMON_SOURCES) test_child_table.cpp +ot_test_child_table_LDADD = $(COMMON_LDADD) +ot_test_child_table_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_child_table_SOURCES = $(COMMON_SOURCES) test_child_table.cpp -ot_test_cmd_line_parser_LDADD = $(COMMON_LDADD) -ot_test_cmd_line_parser_SOURCES = $(COMMON_SOURCES) test_cmd_line_parser.cpp +ot_test_cmd_line_parser_LDADD = $(COMMON_LDADD) +ot_test_cmd_line_parser_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_cmd_line_parser_SOURCES = $(COMMON_SOURCES) test_cmd_line_parser.cpp -ot_test_data_LDADD = $(COMMON_LDADD) -ot_test_data_SOURCES = $(COMMON_SOURCES) test_data.cpp +ot_test_data_LDADD = $(COMMON_LDADD) +ot_test_data_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_data_SOURCES = $(COMMON_SOURCES) test_data.cpp -ot_test_dns_LDADD = $(COMMON_LDADD) -ot_test_dns_SOURCES = $(COMMON_SOURCES) test_dns.cpp +ot_test_dns_LDADD = $(COMMON_LDADD) +ot_test_dns_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_dns_SOURCES = $(COMMON_SOURCES) test_dns.cpp -ot_test_dso_LDADD = $(COMMON_LDADD) -ot_test_dso_SOURCES = $(COMMON_SOURCES) test_dso.cpp +ot_test_dso_LDADD = $(COMMON_LDADD) +ot_test_dso_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_dso_SOURCES = $(COMMON_SOURCES) test_dso.cpp -ot_test_ecdsa_LDADD = $(COMMON_LDADD) -ot_test_ecdsa_SOURCES = $(COMMON_SOURCES) test_ecdsa.cpp +ot_test_ecdsa_LDADD = $(COMMON_LDADD) +ot_test_ecdsa_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_ecdsa_SOURCES = $(COMMON_SOURCES) test_ecdsa.cpp -ot_test_flash_LDADD = $(COMMON_LDADD) -ot_test_flash_SOURCES = $(COMMON_SOURCES) test_flash.cpp +ot_test_flash_LDADD = $(COMMON_LDADD) +ot_test_flash_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_flash_SOURCES = $(COMMON_SOURCES) test_flash.cpp -ot_test_hdlc_LDADD = $(COMMON_LDADD) -ot_test_hdlc_SOURCES = $(COMMON_SOURCES) test_hdlc.cpp +ot_test_hdlc_LDADD = $(COMMON_LDADD) +ot_test_hdlc_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_hdlc_SOURCES = $(COMMON_SOURCES) test_hdlc.cpp -ot_test_heap_LDADD = $(COMMON_LDADD) -ot_test_heap_SOURCES = $(COMMON_SOURCES) test_heap.cpp +ot_test_heap_LDADD = $(COMMON_LDADD) +ot_test_heap_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_heap_SOURCES = $(COMMON_SOURCES) test_heap.cpp -ot_test_heap_string_LDADD = $(COMMON_LDADD) -ot_test_heap_string_SOURCES = $(COMMON_SOURCES) test_heap_string.cpp +ot_test_heap_string_LDADD = $(COMMON_LDADD) +ot_test_heap_string_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_heap_string_SOURCES = $(COMMON_SOURCES) test_heap_string.cpp -ot_test_hkdf_sha256_LDADD = $(COMMON_LDADD) -ot_test_hkdf_sha256_SOURCES = $(COMMON_SOURCES) test_hkdf_sha256.cpp +ot_test_hkdf_sha256_LDADD = $(COMMON_LDADD) +ot_test_hkdf_sha256_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_hkdf_sha256_SOURCES = $(COMMON_SOURCES) test_hkdf_sha256.cpp -ot_test_hmac_sha256_LDADD = $(COMMON_LDADD) -ot_test_hmac_sha256_SOURCES = $(COMMON_SOURCES) test_hmac_sha256.cpp +ot_test_hmac_sha256_LDADD = $(COMMON_LDADD) +ot_test_hmac_sha256_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_hmac_sha256_SOURCES = $(COMMON_SOURCES) test_hmac_sha256.cpp -ot_test_ip_address_LDADD = $(COMMON_LDADD) -ot_test_ip_address_SOURCES = $(COMMON_SOURCES) test_ip_address.cpp +ot_test_ip_address_LDADD = $(COMMON_LDADD) +ot_test_ip_address_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_ip_address_SOURCES = $(COMMON_SOURCES) test_ip_address.cpp -ot_test_link_quality_LDADD = $(COMMON_LDADD) -ot_test_link_quality_SOURCES = $(COMMON_SOURCES) test_link_quality.cpp +ot_test_link_quality_LDADD = $(COMMON_LDADD) +ot_test_link_quality_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_link_quality_SOURCES = $(COMMON_SOURCES) test_link_quality.cpp -ot_test_linked_list_LDADD = $(COMMON_LDADD) -ot_test_linked_list_SOURCES = $(COMMON_SOURCES) test_linked_list.cpp +ot_test_linked_list_LDADD = $(COMMON_LDADD) +ot_test_linked_list_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_linked_list_SOURCES = $(COMMON_SOURCES) test_linked_list.cpp -ot_test_lowpan_LDADD = $(COMMON_LDADD) -ot_test_lowpan_SOURCES = $(COMMON_SOURCES) test_lowpan.cpp +ot_test_lowpan_LDADD = $(COMMON_LDADD) +ot_test_lowpan_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_lowpan_SOURCES = $(COMMON_SOURCES) test_lowpan.cpp -ot_test_mac_frame_LDADD = $(COMMON_LDADD) -ot_test_mac_frame_SOURCES = $(COMMON_SOURCES) test_mac_frame.cpp +ot_test_mac_frame_LDADD = $(COMMON_LDADD) +ot_test_mac_frame_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_mac_frame_SOURCES = $(COMMON_SOURCES) test_mac_frame.cpp -ot_test_macros_LDADD = $(COMMON_LDADD) -ot_test_macros_SOURCES = $(COMMON_SOURCES) test_macros.cpp +ot_test_macros_LDADD = $(COMMON_LDADD) +ot_test_macros_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_macros_SOURCES = $(COMMON_SOURCES) test_macros.cpp -ot_test_message_LDADD = $(COMMON_LDADD) -ot_test_message_SOURCES = $(COMMON_SOURCES) test_message.cpp +ot_test_message_LDADD = $(COMMON_LDADD) +ot_test_message_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_message_SOURCES = $(COMMON_SOURCES) test_message.cpp -ot_test_message_queue_LDADD = $(COMMON_LDADD) -ot_test_message_queue_SOURCES = $(COMMON_SOURCES) test_message_queue.cpp +ot_test_message_queue_LDADD = $(COMMON_LDADD) +ot_test_message_queue_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_message_queue_SOURCES = $(COMMON_SOURCES) test_message_queue.cpp -ot_test_multicast_listeners_table_LDADD = $(COMMON_LDADD) +ot_test_multicast_listeners_table_LDADD = $(COMMON_LDADD) +ot_test_multicast_listeners_table_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) ot_test_multicast_listeners_table_SOURCES = $(COMMON_SOURCES) test_multicast_listeners_table.cpp -ot_test_spinel_buffer_LDADD = $(COMMON_LDADD) -ot_test_spinel_buffer_SOURCES = $(COMMON_SOURCES) test_spinel_buffer.cpp +ot_test_spinel_buffer_LDADD = $(COMMON_LDADD) +ot_test_spinel_buffer_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_spinel_buffer_SOURCES = $(COMMON_SOURCES) test_spinel_buffer.cpp -ot_test_ndproxy_table_LDADD = $(COMMON_LDADD) -ot_test_ndproxy_table_SOURCES = $(COMMON_SOURCES) test_ndproxy_table.cpp +ot_test_ndproxy_table_LDADD = $(COMMON_LDADD) +ot_test_ndproxy_table_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_ndproxy_table_SOURCES = $(COMMON_SOURCES) test_ndproxy_table.cpp -ot_test_netif_LDADD = $(COMMON_LDADD) -ot_test_netif_SOURCES = $(COMMON_SOURCES) test_netif.cpp +ot_test_netif_LDADD = $(COMMON_LDADD) +ot_test_netif_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_netif_SOURCES = $(COMMON_SOURCES) test_netif.cpp -ot_test_network_data_LDADD = $(COMMON_LDADD) -ot_test_network_data_SOURCES = $(COMMON_SOURCES) test_network_data.cpp +ot_test_network_data_LDADD = $(COMMON_LDADD) +ot_test_network_data_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_network_data_SOURCES = $(COMMON_SOURCES) test_network_data.cpp -ot_test_pool_LDADD = $(COMMON_LDADD) -ot_test_pool_SOURCES = $(COMMON_SOURCES) test_pool.cpp +ot_test_pool_LDADD = $(COMMON_LDADD) +ot_test_pool_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_pool_SOURCES = $(COMMON_SOURCES) test_pool.cpp -ot_test_priority_queue_LDADD = $(COMMON_LDADD) -ot_test_priority_queue_SOURCES = $(COMMON_SOURCES) test_priority_queue.cpp +ot_test_priority_queue_LDADD = $(COMMON_LDADD) +ot_test_priority_queue_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_priority_queue_SOURCES = $(COMMON_SOURCES) test_priority_queue.cpp -ot_test_pskc_LDADD = $(COMMON_LDADD) -ot_test_pskc_SOURCES = $(COMMON_SOURCES) test_pskc.cpp +ot_test_pskc_LDADD = $(COMMON_LDADD) +ot_test_pskc_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_pskc_SOURCES = $(COMMON_SOURCES) test_pskc.cpp -ot_test_smart_ptrs_LDADD = $(COMMON_LDADD) -ot_test_smart_ptrs_SOURCES = $(COMMON_SOURCES) test_smart_ptrs.cpp +ot_test_smart_ptrs_LDADD = $(COMMON_LDADD) +ot_test_smart_ptrs_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_smart_ptrs_SOURCES = $(COMMON_SOURCES) test_smart_ptrs.cpp -ot_test_meshcop_LDADD = $(COMMON_LDADD) -ot_test_meshcop_SOURCES = $(COMMON_SOURCES) test_meshcop.cpp +ot_test_meshcop_LDADD = $(COMMON_LDADD) +ot_test_meshcop_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_meshcop_SOURCES = $(COMMON_SOURCES) test_meshcop.cpp -ot_test_serial_number_LDADD = $(COMMON_LDADD) -ot_test_serial_number_SOURCES = $(COMMON_SOURCES) test_serial_number.cpp +ot_test_serial_number_LDADD = $(COMMON_LDADD) +ot_test_serial_number_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_serial_number_SOURCES = $(COMMON_SOURCES) test_serial_number.cpp -ot_test_string_LDADD = $(COMMON_LDADD) -ot_test_string_SOURCES = $(COMMON_SOURCES) test_string.cpp +ot_test_string_LDADD = $(COMMON_LDADD) +ot_test_string_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_string_SOURCES = $(COMMON_SOURCES) test_string.cpp -ot_test_spinel_decoder_LDADD = $(COMMON_LDADD) -ot_test_spinel_decoder_SOURCES = $(COMMON_SOURCES) test_spinel_decoder.cpp +ot_test_spinel_decoder_LDADD = $(COMMON_LDADD) +ot_test_spinel_decoder_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_spinel_decoder_SOURCES = $(COMMON_SOURCES) test_spinel_decoder.cpp -ot_test_spinel_encoder_LDADD = $(COMMON_LDADD) -ot_test_spinel_encoder_SOURCES = $(COMMON_SOURCES) test_spinel_encoder.cpp +ot_test_spinel_encoder_LDADD = $(COMMON_LDADD) +ot_test_spinel_encoder_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_spinel_encoder_SOURCES = $(COMMON_SOURCES) test_spinel_encoder.cpp -ot_test_timer_LDADD = $(COMMON_LDADD) -ot_test_timer_SOURCES = $(COMMON_SOURCES) test_timer.cpp +ot_test_timer_LDADD = $(COMMON_LDADD) +ot_test_timer_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_timer_SOURCES = $(COMMON_SOURCES) test_timer.cpp -ot_test_toolchain_LDADD = $(NULL) -ot_test_toolchain_SOURCES = test_toolchain.cpp test_toolchain_c.c +ot_test_toolchain_LDADD = $(NULL) +ot_test_toolchain_SOURCES = test_toolchain.cpp test_toolchain_c.c if OPENTHREAD_BUILD_COVERAGE CLEANFILES = $(wildcard *.gcda *.gcno) diff --git a/third_party/tcplp/bsdtcp/tcp_const.h b/third_party/tcplp/bsdtcp/tcp_const.h index 6728fa6ed..851643891 100644 --- a/third_party/tcplp/bsdtcp/tcp_const.h +++ b/third_party/tcplp/bsdtcp/tcp_const.h @@ -43,9 +43,23 @@ #include "tcp_var.h" #include "tcp_timer.h" +/* + * samkumar: these are TCPlp-specific constants that I added. They were not + * present in the FreeBSD-derived code. + */ + +#define FRAMES_PER_SEG 5 +#define FRAMECAP_6LOWPAN (122 - 11 - 5) +#define IP6HDR_SIZE (2 + 1 + 1 + 16 + 16) // IPHC header (2) + Next header (1) + Hop count (1) + Dest. addr (16) + Src. addr (16) #define MSS_6LOWPAN ((FRAMES_PER_SEG * FRAMECAP_6LOWPAN) - IP6HDR_SIZE - sizeof(struct tcphdr)) -// I may change some of these flags later +/* + * samkumar: The remaining constants were present in the original FreeBSD code, + * but I set their values. + */ + +#define hz 1000 // number of ticks per second, assuming millisecond ticks + enum tcp_input_consts { tcp_keepcnt = TCPTV_KEEPCNT, tcp_fast_finwait2_recycle = 0, diff --git a/third_party/tcplp/bsdtcp/tcp_subr.c b/third_party/tcplp/bsdtcp/tcp_subr.c index d90461c97..02c2b3ea4 100644 --- a/third_party/tcplp/bsdtcp/tcp_subr.c +++ b/third_party/tcplp/bsdtcp/tcp_subr.c @@ -48,12 +48,12 @@ #include "tcp_const.h" -/* samkumar: Eventually, replace this with OpenThread's random generator. */ -// A simple linear congruential number generator -tcp_seq seed = (tcp_seq) 0xbeaddeed; +/* + * samkumar: This is rewritten to have the host network stack to generate the + * ISN with appropriate randomness. + */ tcp_seq tcp_new_isn(struct tcpcb* tp) { - seed = (((tcp_seq) 0xfaded011) * seed) + (tcp_seq) 0x1ead1eaf; - return seed; + return (uint32_t) tcplp_sys_generate_isn(); } /* diff --git a/third_party/tcplp/bsdtcp/tcp_timer.c b/third_party/tcplp/bsdtcp/tcp_timer.c index a2737ceda..7643b5e2c 100644 --- a/third_party/tcplp/bsdtcp/tcp_timer.c +++ b/third_party/tcplp/bsdtcp/tcp_timer.c @@ -59,10 +59,11 @@ int V_tcp_pmtud_blackhole_activated_min_mss = 0; /* * samkumar: I changed these functions to accept "struct tcpcb* tp" their * argument instead of "void *xtp". This is possible since we're no longer - * relying on FreeBSD's callout subsystem in TCPlp. + * relying on FreeBSD's callout subsystem in TCPlp. I also changed them to + * return 1 if the connection is dropped, or 0 otherwise. */ -void +int tcp_timer_delack(struct tcpcb* tp) { /* samkumar: I added this, to replace the code I removed below. */ @@ -80,9 +81,10 @@ tcp_timer_delack(struct tcpcb* tp) */ tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); + return 0; } -void +int tcp_timer_keep(struct tcpcb* tp) { uint32_t ticks = tcplp_sys_get_ticks(); @@ -158,17 +160,19 @@ tcp_timer_keep(struct tcpcb* tp) * that handled debug tracing/probes, vnet, and locking. I removed that * code. */ - return; + return 0; dropit: tp = tcp_drop(tp, ETIMEDOUT); (void) tp; /* samkumar: prevent a compiler warning */ + return 1; } -void +int tcp_timer_persist(struct tcpcb* tp) { uint32_t ticks = tcplp_sys_get_ticks(); + int dropped = 0; /* samkumar: I added this, to replace the code I removed below. */ KASSERT(tpistimeractive(tp, TT_PERSIST), ("Persist timer running, but unmarked\n")); @@ -202,6 +206,7 @@ tcp_timer_persist(struct tcpcb* tp) (ticks - tp->t_rcvtime >= tcp_maxpersistidle || ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff)) { tp = tcp_drop(tp, ETIMEDOUT); + dropped = 1; goto out; } @@ -212,6 +217,7 @@ tcp_timer_persist(struct tcpcb* tp) if (tp->t_state > TCPS_CLOSE_WAIT && (ticks - tp->t_rcvtime) >= TCPTV_PERSMAX) { tp = tcp_drop(tp, ETIMEDOUT); + dropped = 1; goto out; } @@ -227,13 +233,14 @@ out: * tracing/probes, vnet, and locking. I removed that code. */ (void) tp; /* samkumar: prevent a compiler warning */ - return; + return dropped; } -void +int tcp_timer_2msl(struct tcpcb* tp) { uint32_t ticks = tcplp_sys_get_ticks(); + int dropped = 0; /* samkumar: I added this, to replace the code I removed below. */ KASSERT(tpistimeractive(tp, TT_2MSL), ("2MSL timer running, but unmarked\n")); @@ -281,7 +288,8 @@ tcp_timer_2msl(struct tcpcb* tp) if (tp->t_state == TCP6S_TIME_WAIT) { tp = tcp_close(tp); tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); - return; + dropped = 1; + return dropped; } /* * samkumar: This if statement also used to check that an inpcb is still @@ -297,6 +305,7 @@ tcp_timer_2msl(struct tcpcb* tp) tpiscantrcv(tp)) { tp = tcp_close(tp); tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); + dropped = 1; } else { if (ticks - tp->t_rcvtime <= TP_MAXIDLE(tp)) { /* @@ -308,19 +317,22 @@ tcp_timer_2msl(struct tcpcb* tp) } else { tp = tcp_close(tp); tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); + dropped = 1; } } /* * samkumar: There used to be some code here that handled debug * tracing/probes, vnet, and locking. I removed that code. */ + return dropped; } -void +int tcp_timer_rexmt(struct tcpcb *tp) { int rexmt; uint32_t ticks = tcplp_sys_get_ticks(); + int dropped = 0; /* samkumar: I added this, to replace the code I removed below. */ KASSERT(tpistimeractive(tp, TT_REXMT), ("Rexmt timer running, but unmarked\n")); @@ -348,6 +360,7 @@ tcp_timer_rexmt(struct tcpcb *tp) tp = tcp_drop(tp, tp->t_softerror ? tp->t_softerror : ETIMEDOUT); + dropped = 1; goto out; } if (tp->t_state == TCPS_SYN_SENT) { @@ -450,7 +463,7 @@ out: * tracing/probes, vnet, and locking. I removed that code. */ (void) tp; /* samkumar: Prevent a compiler warning */ - return; + return dropped; } int diff --git a/third_party/tcplp/bsdtcp/tcp_timer.h b/third_party/tcplp/bsdtcp/tcp_timer.h index 03389f84a..45bafc48d 100644 --- a/third_party/tcplp/bsdtcp/tcp_timer.h +++ b/third_party/tcplp/bsdtcp/tcp_timer.h @@ -154,11 +154,15 @@ static const int tcp_backoff[TCP_MAXRXTSHIFT + 1] = static const int tcp_totbackoff = 2559; /* sum of tcp_backoff[] */ -void tcp_timer_delack(struct tcpcb* tp); -void tcp_timer_keep(struct tcpcb* tp); -void tcp_timer_persist(struct tcpcb* tp); -void tcp_timer_2msl(struct tcpcb* tp); -void tcp_timer_rexmt(struct tcpcb *tp); +/* + * samkumar: Changed return value to int to indicate whether connection was + * dropped or not. + */ +int tcp_timer_delack(struct tcpcb* tp); +int tcp_timer_keep(struct tcpcb* tp); +int tcp_timer_persist(struct tcpcb* tp); +int tcp_timer_2msl(struct tcpcb* tp); +int tcp_timer_rexmt(struct tcpcb *tp); int tcp_timer_active(struct tcpcb *tp, uint32_t timer_type); /* diff --git a/third_party/tcplp/tcplp.h b/third_party/tcplp/tcplp.h index eccd98bed..e14fd90c3 100644 --- a/third_party/tcplp/tcplp.h +++ b/third_party/tcplp/tcplp.h @@ -50,14 +50,6 @@ extern "C" { #include #include -#define hz 1000 // number of ticks per second -#define MICROS_PER_TICK 1000 // number of microseconds per tick - -#define FRAMES_PER_SEG 5 -#define FRAMECAP_6LOWPAN (122 - 11 - 5) - -#define IP6HDR_SIZE (2 + 1 + 1 + 16 + 16) // IPHC header (2) + Next header (1) + Hop count (1) + Dest. addr (16) + Src. addr (16) - #define RELOOKUP_REQUIRED -1 #define CONN_LOST_NORMAL 0 @@ -85,6 +77,7 @@ void tcplp_sys_connection_lost(struct tcpcb* tcb, uint8_t errnum); void tcplp_sys_on_state_change(struct tcpcb* tcb, int newstate); void tcplp_sys_log(const char* format, ...); bool tcplp_sys_autobind(otInstance *aInstance, const otSockAddr *aPeer, otSockAddr *aToBind, bool aBindAddress, bool aBindPort); +uint32_t tcplp_sys_generate_isn(); #ifdef __cplusplus } // extern "C"