blob: d9f78be1771b0f63fa46184074b49846e4d75b8e [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file defines the CHIP Connection object that maintains TCP connections.
* It binds to any available local addr and port and begins listening.
*/
#pragma once
#include <algorithm>
#include <new>
#include <utility>
#include <inet/IPAddress.h>
#include <inet/InetInterface.h>
#include <inet/TCPEndPoint.h>
#include <lib/core/CHIPCore.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/PoolWrapper.h>
#include <transport/raw/Base.h>
namespace chip {
namespace Transport {
/** Defines listening parameters for setting up a TCP transport */
class TcpListenParameters
{
public:
explicit TcpListenParameters(Inet::EndPointManager<Inet::TCPEndPoint> * endPointManager) : mEndPointManager(endPointManager) {}
TcpListenParameters(const TcpListenParameters &) = default;
TcpListenParameters(TcpListenParameters &&) = default;
Inet::EndPointManager<Inet::TCPEndPoint> * GetEndPointManager() { return mEndPointManager; }
Inet::IPAddressType GetAddressType() const { return mAddressType; }
TcpListenParameters & SetAddressType(Inet::IPAddressType type)
{
mAddressType = type;
return *this;
}
uint16_t GetListenPort() const { return mListenPort; }
TcpListenParameters & SetListenPort(uint16_t port)
{
mListenPort = port;
return *this;
}
Inet::InterfaceId GetInterfaceId() const { return mInterfaceId; }
TcpListenParameters & SetInterfaceId(Inet::InterfaceId id)
{
mInterfaceId = id;
return *this;
}
private:
Inet::EndPointManager<Inet::TCPEndPoint> * mEndPointManager; ///< Associated endpoint factory
Inet::IPAddressType mAddressType = Inet::IPAddressType::kIPv6; ///< type of listening socket
uint16_t mListenPort = CHIP_PORT; ///< TCP listen port
Inet::InterfaceId mInterfaceId = Inet::InterfaceId::Null(); ///< Interface to listen on
};
/**
* Packets scheduled for sending once a connection has been established.
*/
struct PendingPacket
{
PendingPacket(const PeerAddress & peerAddress, System::PacketBufferHandle && packetBuffer) :
mPeerAddress(peerAddress), mPacketBuffer(std::move(packetBuffer))
{}
PeerAddress mPeerAddress; // where the packet is being sent to
System::PacketBufferHandle mPacketBuffer; // what data needs to be sent
};
/** Implements a transport using TCP. */
class DLL_EXPORT TCPBase : public Base
{
/**
* The State of the TCP connection
*/
enum class State
{
kNotReady = 0, /**< State before initialization. */
kInitialized = 1, /**< State after class is listening and ready. */
};
protected:
/**
* State for each active connection
*/
struct ActiveConnectionState
{
void Init(Inet::TCPEndPoint * endPoint)
{
mEndPoint = endPoint;
mReceived = nullptr;
}
void Free()
{
mEndPoint->Free();
mEndPoint = nullptr;
mReceived = nullptr;
}
bool InUse() const { return mEndPoint != nullptr; }
// Associated endpoint.
Inet::TCPEndPoint * mEndPoint;
// Buffers received but not yet consumed.
System::PacketBufferHandle mReceived;
};
public:
using PendingPacketPoolType = PoolInterface<PendingPacket, const PeerAddress &, System::PacketBufferHandle &&>;
TCPBase(ActiveConnectionState * activeConnectionsBuffer, size_t bufferSize, PendingPacketPoolType & packetBuffers) :
mActiveConnections(activeConnectionsBuffer), mActiveConnectionsSize(bufferSize), mPendingPackets(packetBuffers)
{
// activeConnectionsBuffer must be initialized by the caller.
}
~TCPBase() override;
/**
* Initialize a TCP transport on a given port.
*
* @param params TCP configuration parameters for this transport
*
* @details
* Generally send and receive ports should be the same and equal to CHIP_PORT.
* The class allows separate definitions to allow local execution of several
* Nodes.
*/
CHIP_ERROR Init(TcpListenParameters & params);
/**
* Close the open endpoint without destroying the object
*/
void Close() override;
CHIP_ERROR SendMessage(const PeerAddress & address, System::PacketBufferHandle && msgBuf) override;
void Disconnect(const PeerAddress & address) override;
bool CanSendToPeer(const PeerAddress & address) override
{
return (mState == State::kInitialized) && (address.GetTransportType() == Type::kTcp) &&
(address.GetIPAddress().Type() == mEndpointType);
}
/**
* Helper method to determine if IO processing is still required for a TCP transport
* before everything is cleaned up (socket closing is async, so after calling 'Close' on
* the transport, some time may be needed to actually be able to close.)
*/
bool HasActiveConnections() const;
/**
* Close all active connections.
*/
void CloseActiveConnections();
private:
friend class TCPTest;
/**
* Find an active connection to the given peer or return nullptr if
* no active connection exists.
*/
ActiveConnectionState * FindActiveConnection(const PeerAddress & addr);
ActiveConnectionState * FindActiveConnection(const Inet::TCPEndPoint * endPoint);
/**
* Sends the specified message once a connection has been established.
*
* @param addr - what peer to connect to
* @param msg - what buffer to send once a connection has been established.
*
* Ownership of msg is taken over and will be freed at some unspecified time
* in the future (once connection succeeds/fails).
*/
CHIP_ERROR SendAfterConnect(const PeerAddress & addr, System::PacketBufferHandle && msg);
/**
* Process a single received buffer from the specified peer address.
*
* @param endPoint the source end point from which the data comes from
* @param peerAddress the peer the data is coming from
* @param buffer the actual data
*
* Ownership of buffer is taken over and will be freed (or re-enqueued to the endPoint receive queue)
* as needed during processing.
*/
CHIP_ERROR ProcessReceivedBuffer(Inet::TCPEndPoint * endPoint, const PeerAddress & peerAddress,
System::PacketBufferHandle && buffer);
/**
* Process a single message of the specified size from a buffer.
*
* @param[in] peerAddress The peer the data is coming from.
* @param[in,out] state The connection state, which contains the message. On entry, the payload points to the message
* body (after the length). On exit, it points after the message (or the queue is null, if there
* is no other data).
* @param[in] messageSize Size of the single message.
*/
CHIP_ERROR ProcessSingleMessage(const PeerAddress & peerAddress, ActiveConnectionState * state, uint16_t messageSize);
// Release an active connection (corresponding to the passed TCPEndPoint)
// from the pool.
void ReleaseActiveConnection(Inet::TCPEndPoint * endPoint);
// Callback handler for TCPEndPoint. TCP message receive handler.
// @see TCPEndpoint::OnDataReceivedFunct
static CHIP_ERROR OnTcpReceive(Inet::TCPEndPoint * endPoint, System::PacketBufferHandle && buffer);
// Callback handler for TCPEndPoint. Called when a connection has been completed.
// @see TCPEndpoint::OnConnectCompleteFunct
static void OnConnectionComplete(Inet::TCPEndPoint * endPoint, CHIP_ERROR err);
// Callback handler for TCPEndPoint. Called when a connection has been closed.
// @see TCPEndpoint::OnConnectionClosedFunct
static void OnConnectionClosed(Inet::TCPEndPoint * endPoint, CHIP_ERROR err);
// Callback handler for TCPEndPoint. Callend when a peer closes the connection.
// @see TCPEndpoint::OnPeerCloseFunct
static void OnPeerClosed(Inet::TCPEndPoint * endPoint);
// Callback handler for TCPEndPoint. Called when a connection is received on the listening port.
// @see TCPEndpoint::OnConnectionReceivedFunct
static void OnConnectionReceived(Inet::TCPEndPoint * listenEndPoint, Inet::TCPEndPoint * endPoint,
const Inet::IPAddress & peerAddress, uint16_t peerPort);
// Called on accept error
// @see TCPEndpoint::OnAcceptErrorFunct
static void OnAcceptError(Inet::TCPEndPoint * endPoint, CHIP_ERROR err);
Inet::TCPEndPoint * mListenSocket = nullptr; ///< TCP socket used by the transport
Inet::IPAddressType mEndpointType = Inet::IPAddressType::kUnknown; ///< Socket listening type
State mState = State::kNotReady; ///< State of the TCP transport
// Number of active and 'pending connection' endpoints
size_t mUsedEndPointCount = 0;
// Currently active connections
ActiveConnectionState * mActiveConnections;
const size_t mActiveConnectionsSize;
// Data to be sent when connections succeed
PendingPacketPoolType & mPendingPackets;
};
template <size_t kActiveConnectionsSize, size_t kPendingPacketSize>
class TCP : public TCPBase
{
public:
TCP() : TCPBase(mConnectionsBuffer, kActiveConnectionsSize, mPendingPackets)
{
for (size_t i = 0; i < kActiveConnectionsSize; ++i)
{
mConnectionsBuffer[i].Init(nullptr);
}
}
~TCP() override { mPendingPackets.ReleaseAll(); }
private:
friend class TCPTest;
TCPBase::ActiveConnectionState mConnectionsBuffer[kActiveConnectionsSize];
PoolImpl<PendingPacket, kPendingPacketSize, ObjectPoolMem::kInline, PendingPacketPoolType::Interface> mPendingPackets;
};
} // namespace Transport
} // namespace chip