openthread/src/core/net/tcp6.hpp

702 lines
26 KiB
C++

/*
* Copyright (c) 2021, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for TCP/IPv6 sockets.
*/
#ifndef TCP6_HPP_
#define TCP6_HPP_
#include "openthread-core-config.h"
#include <openthread/tcp.h>
#include "common/as_core_type.hpp"
#include "common/clearable.hpp"
#include "common/linked_list.hpp"
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/timer.hpp"
#include "net/ip6_headers.hpp"
#include "net/socket.hpp"
/*
* These structures and functions are forward-declared here to avoid
* #includ'ing third_party/tcplp/tcplp.h in this header file.
*/
extern "C" {
struct tcpcb;
struct tcpcb_listen;
struct tcplp_signals;
void tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay);
void tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag);
}
namespace ot {
namespace Ip6 {
/**
* @addtogroup core-tcp
*
* @brief
* This module includes definitions for TCP/IPv6 sockets.
*
* @{
*
*/
/**
* This class implements TCP message handling.
*
*/
class Tcp : public InstanceLocator, private NonCopyable
{
public:
/**
* This class represents an endpoint of a TCP/IPv6 connection.
*
*/
class Endpoint : public otTcpEndpoint, public LinkedListEntry<Endpoint>, public GetProvider<Endpoint>
{
friend class Tcp;
friend class LinkedList<Endpoint>;
public:
/**
* Initializes a TCP endpoint.
*
* Calling this function causes OpenThread to keep track of this Endpoint
* and store and retrieve TCP data inside of it. The application
* should refrain from directly accessing or modifying the fields in
* this Endpoint. If the application needs to reclaim the memory backing
* this Endpoint, it should call otTcpEndpointDeinitialize().
*
* @sa otTcpEndpointInitialize in include/openthread/tcp.h.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aArgs A pointer to a structure of arguments.
*
* @retval kErrorNone Successfully opened the TCP endpoint.
* @retval kErrorFailed Failed to open the TCP endpoint.
*
*/
Error Initialize(Instance &aInstance, const otTcpEndpointInitializeArgs &aArgs);
/**
* Obtains the Instance that was associated with this Endpoint upon
* initialization.
*
* @sa otTcpEndpointGetInstance
*
* @returns The Instance pointer associated with this Endpoint.
*
*/
Instance &GetInstance(void) const;
/**
* Obtains the context pointer that was associated this Endpoint upon
* initialization.
*
* @sa otTcpEndpointGetContext
*
* @returns The context pointer associated with this Endpoint.
*
*/
void *GetContext(void) { return mContext; }
/**
* Obtains a pointer to a TCP endpoint's local host and port.
*
* The contents of the host and port may be stale if this socket is not in a
* connected state and has not been bound after it was last disconnected.
*
* @sa otTcpGetLocalAddress
*
* @returns The local host and port of this Endpoint.
*
*/
const SockAddr &GetLocalAddress(void) const;
/**
* Obtains a pointer to a TCP endpoint's peer's host and port.
*
* The contents of the host and port may be stale if this socket is not in a
* connected state.
*
* @sa otTcpGetPeerAddress
*
* @returns The host and port of the connection peer of this Endpoint.
*
*/
const SockAddr &GetPeerAddress(void) const;
/**
* Binds the TCP endpoint to an IP address and port.
*
* @sa otTcpBind
*
* @param[in] aSockName The address and port to which to bind this TCP endpoint.
*
* @retval kErrorNone Successfully bound the TCP endpoint.
* @retval kErrorFailed Failed to bind the TCP endpoint.
*
*/
Error Bind(const SockAddr &aSockName);
/**
* Records the remote host and port for this connection.
*
* By default TCP Fast Open is used. This means that this function merely
* records the remote host and port, and that the TCP connection establishment
* handshake only happens on the first call to otTcpSendByReference(). TCP Fast
* Open can be explicitly disabled using @p aFlags, in which case the TCP
* connection establishment handshake is initiated immediately.
*
* @sa otTcpConnect
*
* @param[in] aSockName The IP address and port of the host to which to connect.
* @param[in] aFlags Flags specifying options for this operation (see enumeration above).
*
* @retval kErrorNone Successfully completed the operation.
* @retval kErrorFailed Failed to complete the operation.
*
*/
Error Connect(const SockAddr &aSockName, uint32_t aFlags);
/**
* Adds data referenced by the linked buffer pointed to by @p aBuffer to the
* send buffer.
*
* Upon a successful call to this function, the linked buffer and data it
* references are owned by the TCP stack; they should not be modified by the
* application until a "send done" callback returns ownership of those objects
* to the application. It is acceptable to call this function to add another
* linked buffer to the send queue, even if the "send done" callback for a
* previous invocation of this function has not yet fired.
*
* Note that @p aBuffer should not be chained; its mNext field should be
* NULL. If additional data will be added right after this call, then the
* OT_TCP_SEND_MORE_TO_COME flag should be used as a hint to the TCP
* implementation.
*
* @sa otTcpSendByReference
*
* @param[in] aBuffer A pointer to the linked buffer chain referencing data to add to the send buffer.
* @param[in] aFlags Flags specifying options for this operation (see enumeration above).
*
* @retval kErrorNone Successfully added data to the send buffer.
* @retval kErrorFailed Failed to add data to the send buffer.
*
*/
Error SendByReference(otLinkedBuffer &aBuffer, uint32_t aFlags);
/**
* Adds data to the send buffer by extending the length of the final
* otLinkedBuffer in the send buffer by the specified amount.
*
* If the send buffer is empty, then the operation fails.
*
* @sa otTcpSendByExtension
*
* @param[in] aNumBytes The number of bytes by which to extend the length of the final linked buffer.
* @param[in] aFlags Flags specifying options for this operation (see enumeration above).
*
* @retval kErrorNone Successfully added data to the send buffer.
* @retval kErrorFailed Failed to add data to the send buffer.
*
*/
Error SendByExtension(size_t aNumBytes, uint32_t aFlags);
/**
* Provides the application with a linked buffer chain referencing data
* currently in the TCP receive buffer.
*
* The linked buffer chain is valid until the "receive ready" callback is next
* invoked, or until the next call to otTcpReceiveContiguify() or
* otTcpCommitReceive().
*
* @sa otTcpReceiveByReference
*
* @param[out] aBuffer A pointer to the linked buffer chain referencing data currently in the receive
* buffer.
*
* @retval kErrorNone Successfully completed the operation.
* @retval kErrorFailed Failed to complete the operation.
*/
Error ReceiveByReference(const otLinkedBuffer *&aBuffer);
/**
* Reorganizes the receive buffer to be entirely contiguous in memory.
*
* This is optional; an application can simply traverse the linked buffer
* chain obtained by calling @p otTcpReceiveByReference. Some
* applications may wish to call this function to make the receive buffer
* contiguous to simplify their data processing, but this comes at the expense
* of CPU time to reorganize the data in the receive buffer.
*
* @sa otTcpReceiveContiguify
*
* @retval kErrorNone Successfully completed the operation.
* @retval kErrorFailed Failed to complete the operation.
*
*/
Error ReceiveContiguify(void);
/**
* Informs the TCP stack that the application has finished processing
* @p aNumBytes bytes of data at the start of the receive buffer and that the
* TCP stack need not continue maintaining those bytes in the receive buffer.
*
* @sa otTcpCommitReceive
*
* @param[in] aNumBytes The number of bytes consumed.
* @param[in] aFlags Flags specifying options for this operation (none yet).
*
* @retval kErrorNone Successfully completed the receive operation.
* @retval kErrorFailed Failed to complete the receive operation.
*
*/
Error CommitReceive(size_t aNumBytes, uint32_t aFlags);
/**
* Informs the connection peer that this TCP endpoint will not send more data.
*
* This should be used when the application has no more data to send to the
* connection peer. For this connection, future reads on the connection peer
* will result in the "end of stream" condition, and future writes on this
* connection endpoint will fail.
*
* The "end of stream" condition only applies after any data previously
* provided to the TCP stack to send out has been received by the connection
* peer.
*
* @sa otTcpSendEndOfStream
*
* @retval kErrorNone Successfully queued the "end of stream" condition for transmission.
* @retval kErrorFailed Failed to queue the "end of stream" condition for transmission.
*
*/
Error SendEndOfStream(void);
/**
* Forcibly ends the TCP connection associated with this TCP endpoint.
*
* This immediately makes the TCP endpoint free for use for another connection
* and empties the send and receive buffers, transferring ownership of any data
* provided by the application in otTcpSendByReference() calls back to
* the application. The TCP endpoint's callbacks and memory for the receive
* buffer remain associated with the TCP endpoint.
*
* @sa otTcpAbort
*
* @retval kErrorNone Successfully aborted the TCP endpoint's connection.
* @retval kErrorFailed Failed to abort the TCP endpoint's connection.
*
*/
Error Abort(void);
/**
* Deinitializes this TCP endpoint.
*
* This means that OpenThread no longer keeps track of this TCP endpoint and
* deallocates all resources it has internally allocated for this TCP endpoint.
* The application can reuse the memory backing the TCP endpoint as it sees fit.
*
* If it corresponds to a live TCP connection, the connection is terminated
* unceremoniously (as in otTcpAbort()). All resources the application has
* provided for this TCP endpoint (linked buffers for the send buffer, memory
* for the receive buffer, this Endpoint structure itself, etc.) are
* immediately returned to the application.
*
* @sa otTcpEndpointDeinitialize
*
* @retval kErrorNone Successfully deinitialized the TCP endpoint.
* @retval kErrorFailed Failed to deinitialize the TCP endpoint.
*
*/
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<Endpoint *>(&aTcb); }
/**
* Obtains a reference to this Endpoint's struct tcpcb.
*/
struct tcpcb &GetTcb(void) { return *reinterpret_cast<struct tcpcb *>(&mTcb); }
/**
* Obtains a const reference to this Endpoint's struct tcpcb.
*/
const struct tcpcb &GetTcb(void) const { return *reinterpret_cast<const struct tcpcb *>(&mTcb); }
/**
* Checks if this Endpoint is in the closed state.
*/
bool IsClosed(void) const;
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);
enum : uint8_t
{
kTimerDelack = 0,
kTimerRexmtPersist = 1,
kTimerKeep = 2,
kTimer2Msl = 3,
kNumTimers = 4,
};
static uint8_t TimerFlagToIndex(uint8_t aTimerFlag);
bool IsTimerActive(uint8_t aTimerIndex);
void SetTimer(uint8_t aTimerFlag, uint32_t aDelay);
void CancelTimer(uint8_t aTimerFlag);
bool FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, TimeMilli &aEarliestFutureExpiry);
void PostCallbacksAfterSend(size_t aSent, size_t aBacklogBefore);
bool FirePendingCallbacks(void);
size_t GetSendBufferBytes(void) const;
size_t GetInFlightBytes(void) const;
size_t GetBacklogBytes(void) const;
Address & GetLocalIp6Address(void);
const Address &GetLocalIp6Address(void) const;
Address & GetForeignIp6Address(void);
const Address &GetForeignIp6Address(void) const;
bool Matches(const MessageInfo &aMessageInfo) const;
};
/**
* This class represents a TCP/IPv6 listener.
*
*/
class Listener : public otTcpListener, public LinkedListEntry<Listener>, public GetProvider<Listener>
{
friend class LinkedList<Listener>;
public:
/**
* Initializes a TCP listener.
*
* Calling this function causes OpenThread to keep track of the TCP listener
* and store and retrieve TCP data inside this Listener. The application should
* refrain from directly accessing or modifying the fields in this Listener. If
* the application needs to reclaim the memory backing this Listener, it should
* call otTcpListenerDeinitialize().
*
* @sa otTcpListenerInitialize
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aArgs A pointer to a structure of arguments.
*
* @retval kErrorNone Successfully opened the TCP listener.
* @retval kErrorFailed Failed to open the TCP listener.
*
*/
Error Initialize(Instance &aInstance, const otTcpListenerInitializeArgs &aArgs);
/**
* Obtains the otInstance that was associated with this Listener upon
* initialization.
*
* @sa otTcpListenerGetInstance
*
* @returns The otInstance pointer associated with this Listener.
*
*/
Instance &GetInstance(void) const;
/**
* Obtains the context pointer that was associated with this Listener upon
* initialization.
*
* @sa otTcpListenerGetContext
*
* @returns The context pointer associated with this Listener.
*
*/
void *GetContext(void) { return mContext; }
/**
* Causes incoming TCP connections that match the specified IP address and port
* to trigger this TCP listener's callbacks.
*
* @sa otTcpListen
*
* @param[in] aSockName The address and port on which to listen for incoming connections.
*
* @retval kErrorNone Successfully initiated listening on the TCP listener.
* @retval kErrorFailed Failed to initiate listening on the TCP listener.
*
*/
Error Listen(const SockAddr &aSockName);
/**
* Causes this TCP listener to stop listening for incoming connections.
*
* @sa otTcpStopListening
*
* @retval kErrorNone Successfully stopped listening on the TCP listener.
* @retval kErrorFailed Failed to stop listening on the TCP listener.
*
*/
Error StopListening(void);
/**
* Deinitializes this TCP listener.
*
* This means that OpenThread no longer keeps track of this TCP listener and
* deallocates all resources it has internally allocated for this TCP endpoint.
* The application can reuse the memory backing the TCP listener as it sees
* fit.
*
* If the TCP listener is currently listening, it stops listening.
*
* @sa otTcpListenerDeinitialize
*
* @retval kErrorNone Successfully deinitialized the TCP listener.
* @retval kErrorFailed Failed to deinitialize the TCP listener.
*
*/
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<Listener *>(&aTcbListen);
}
/**
* Obtains a reference to this Listener's struct tcpcb_listen.
*/
struct tcpcb_listen &GetTcbListen(void) { return *reinterpret_cast<struct tcpcb_listen *>(&mTcbListen); }
/**
* Obtains a const reference to this Listener's struct tcpcb_listen.
*/
const struct tcpcb_listen &GetTcbListen(void) const
{
return *reinterpret_cast<const struct tcpcb_listen *>(&mTcbListen);
}
/**
* Checks if this Listener is in the closed state.
*/
bool IsClosed(void) const;
private:
Address & GetLocalIp6Address(void);
const Address &GetLocalIp6Address(void) const;
bool Matches(const MessageInfo &aMessageInfo) const;
};
/**
* This class implements TCP header parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class Header : public Clearable<Header>
{
public:
static constexpr uint8_t kChecksumFieldOffset = 16; ///< Byte offset of the Checksum field in the TCP header.
/**
* This method returns the TCP Source Port.
*
* @returns The TCP Source Port.
*
*/
uint16_t GetSourcePort(void) const { return HostSwap16(mSource); }
/**
* This method returns the TCP Destination Port.
*
* @returns The TCP Destination Port.
*
*/
uint16_t GetDestinationPort(void) const { return HostSwap16(mDestination); }
/**
* This method returns the TCP Sequence Number.
*
* @returns The TCP Sequence Number.
*
*/
uint32_t GetSequenceNumber(void) const { return HostSwap32(mSequenceNumber); }
/**
* This method returns the TCP Acknowledgment Sequence Number.
*
* @returns The TCP Acknowledgment Sequence Number.
*
*/
uint32_t GetAcknowledgmentNumber(void) const { return HostSwap32(mAckNumber); }
/**
* This method returns the TCP Flags.
*
* @returns The TCP Flags.
*
*/
uint16_t GetFlags(void) const { return HostSwap16(mFlags); }
/**
* This method returns the TCP Window.
*
* @returns The TCP Window.
*
*/
uint16_t GetWindow(void) const { return HostSwap16(mWindow); }
/**
* This method returns the TCP Checksum.
*
* @returns The TCP Checksum.
*
*/
uint16_t GetChecksum(void) const { return HostSwap16(mChecksum); }
/**
* This method returns the TCP Urgent Pointer.
*
* @returns The TCP Urgent Pointer.
*
*/
uint16_t GetUrgentPointer(void) const { return HostSwap16(mUrgentPointer); }
private:
uint16_t mSource;
uint16_t mDestination;
uint32_t mSequenceNumber;
uint32_t mAckNumber;
uint16_t mFlags;
uint16_t mWindow;
uint16_t mChecksum;
uint16_t mUrgentPointer;
} OT_TOOL_PACKED_END;
/**
* This constructor initializes the object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit Tcp(Instance &aInstance);
/**
* Processes a received TCP segment.
*
* @param[in] aIp6Header A reference to a structure containing the segment's IPv6 header.
* @param[in] aMessage A reference to the message containing the TCP segment.
* @param[in] aMessageInfo A reference to the message info associated with @p aMessage.
*
* @retval kErrorNone Successfully processed the TCP segment.
* @retval kErrorDrop Dropped the TCP segment due to an invalid checksum.
*
*/
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
{
kDynamicPortMin = 49152, ///< Service Name and Transport Protocol Port Number Registry
kDynamicPortMax = 65535, ///< Service Name and Transport Protocol Port Number Registry
};
static constexpr uint8_t kEstablishedCallbackFlag = (1 << 0);
static constexpr uint8_t kSendDoneCallbackFlag = (1 << 1);
static constexpr uint8_t kForwardProgressCallbackFlag = (1 << 2);
static constexpr uint8_t kReceiveAvailableCallbackFlag = (1 << 3);
static constexpr uint8_t kDisconnectedCallbackFlag = (1 << 4);
void ProcessSignals(Endpoint & aEndpoint,
otLinkedBuffer * aPriorHead,
size_t aPriorBacklog,
struct tcplp_signals &aSignals);
static Error BsdErrorToOtError(int aBsdError);
bool CanBind(const SockAddr &aSockName);
static void HandleTimer(Timer &aTimer);
void ProcessTimers(void);
static void HandleTasklet(Tasklet &aTasklet);
void ProcessCallbacks(void);
TimerMilli mTimer;
Tasklet mTasklet;
LinkedList<Endpoint> mEndpoints;
LinkedList<Listener> mListeners;
uint16_t mEphemeralPort;
};
} // namespace Ip6
DefineCoreType(otTcpEndpoint, Ip6::Tcp::Endpoint);
DefineCoreType(otTcpListener, Ip6::Tcp::Listener);
} // namespace ot
#endif // TCP6_HPP_