Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2025 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * This file defines the CHIP Connection object that maintains TCP connections.
22 : * It binds to any available local addr and port and begins listening.
23 : */
24 :
25 : #pragma once
26 :
27 : #include <algorithm>
28 : #include <new>
29 : #include <utility>
30 :
31 : #include <inet/IPAddress.h>
32 : #include <inet/InetInterface.h>
33 : #include <inet/TCPEndPoint.h>
34 : #include <lib/core/CHIPCore.h>
35 : #include <lib/support/CodeUtils.h>
36 : #include <lib/support/PoolWrapper.h>
37 : #include <transport/raw/ActiveTCPConnectionState.h>
38 : #include <transport/raw/Base.h>
39 : #include <transport/raw/TCPConfig.h>
40 :
41 : namespace chip {
42 : namespace Transport {
43 :
44 : /** Defines listening parameters for setting up a TCP transport */
45 : class TcpListenParameters
46 : {
47 : public:
48 23 : explicit TcpListenParameters(Inet::EndPointManager<Inet::TCPEndPoint> * endPointManager) : mEndPointManager(endPointManager) {}
49 1 : TcpListenParameters(const TcpListenParameters &) = default;
50 : TcpListenParameters(TcpListenParameters &&) = default;
51 :
52 23 : Inet::EndPointManager<Inet::TCPEndPoint> * GetEndPointManager() { return mEndPointManager; }
53 :
54 45 : Inet::IPAddressType GetAddressType() const { return mAddressType; }
55 23 : TcpListenParameters & SetAddressType(Inet::IPAddressType type)
56 : {
57 23 : mAddressType = type;
58 :
59 23 : return *this;
60 : }
61 :
62 44 : uint16_t GetListenPort() const { return mListenPort; }
63 23 : TcpListenParameters & SetListenPort(uint16_t port)
64 : {
65 23 : mListenPort = port;
66 :
67 23 : return *this;
68 : }
69 :
70 22 : Inet::InterfaceId GetInterfaceId() const { return mInterfaceId; }
71 : TcpListenParameters & SetInterfaceId(Inet::InterfaceId id)
72 : {
73 : mInterfaceId = id;
74 :
75 : return *this;
76 : }
77 :
78 26 : bool IsServerListenEnabled() const { return mServerListenEnabled; }
79 1 : TcpListenParameters & SetServerListenEnabled(bool listenEnable)
80 : {
81 1 : mServerListenEnabled = listenEnable;
82 :
83 1 : return *this;
84 : }
85 :
86 : private:
87 : Inet::EndPointManager<Inet::TCPEndPoint> * mEndPointManager; ///< Associated endpoint factory
88 : Inet::IPAddressType mAddressType = Inet::IPAddressType::kIPv6; ///< type of listening socket
89 : uint16_t mListenPort = CHIP_PORT; ///< TCP listen port
90 : Inet::InterfaceId mInterfaceId = Inet::InterfaceId::Null(); ///< Interface to listen on
91 : bool mServerListenEnabled = true; ///< TCP Server mode enabled
92 : };
93 :
94 : /**
95 : * Packets scheduled for sending once a connection has been established.
96 : */
97 : struct PendingPacket
98 : {
99 5 : PendingPacket(const PeerAddress & peerAddress, System::PacketBufferHandle && packetBuffer) :
100 5 : mPeerAddress(peerAddress), mPacketBuffer(std::move(packetBuffer))
101 5 : {}
102 :
103 : PeerAddress mPeerAddress; // where the packet is being sent to
104 : System::PacketBufferHandle mPacketBuffer; // what data needs to be sent
105 : };
106 :
107 : /** Implements a transport using TCP. */
108 : class DLL_EXPORT TCPBase : public Base
109 : {
110 :
111 : protected:
112 : enum class ShouldAbort : uint8_t
113 : {
114 : Yes,
115 : No
116 : };
117 :
118 : enum class SuppressCallback : uint8_t
119 : {
120 : Yes,
121 : No
122 : };
123 :
124 : public:
125 : using PendingPacketPoolType = PoolInterface<PendingPacket, const PeerAddress &, System::PacketBufferHandle &&>;
126 53 : TCPBase(ActiveTCPConnectionState * activeConnectionsBuffer, size_t bufferSize, PendingPacketPoolType & packetBuffers) :
127 53 : mActiveConnections(activeConnectionsBuffer), mActiveConnectionsSize(bufferSize), mPendingPackets(packetBuffers)
128 : {
129 : // activeConnectionsBuffer must be initialized by the caller.
130 53 : }
131 : ~TCPBase() override;
132 :
133 : /**
134 : * Initialize a TCP transport on a given port.
135 : *
136 : * @param params TCP configuration parameters for this transport
137 : *
138 : * @details
139 : * Generally send and receive ports should be the same and equal to CHIP_PORT.
140 : * The class allows separate definitions to allow local execution of several
141 : * Nodes.
142 : */
143 : CHIP_ERROR Init(TcpListenParameters & params);
144 :
145 : /**
146 : * Set the timeout (in milliseconds) for the node to wait for the TCP
147 : * connection attempt to complete.
148 : *
149 : */
150 : void SetConnectTimeout(const uint32_t connTimeoutMsecs) { mConnectTimeout = connTimeoutMsecs; }
151 :
152 : /**
153 : * Close the open endpoint without destroying the object
154 : */
155 : void Close() override;
156 :
157 : CHIP_ERROR SendMessage(const PeerAddress & address, System::PacketBufferHandle && msgBuf) override;
158 : CHIP_ERROR SendMessage(const ActiveTCPConnectionHandle & connection, System::PacketBufferHandle && msgBuf);
159 :
160 : /*
161 : * Connect to the given peerAddress over TCP.
162 : *
163 : * @param address The address of the peer.
164 : *
165 : * @param appState Context passed in by the application to be sent back
166 : * via the connection attempt complete callback when
167 : * connection attempt with peer completes.
168 : *
169 : * @param outPeerConnState Pointer to pointer to the active TCP connection state. This is
170 : * an output parameter that is allocated by the
171 : * transport layer and held by the caller object.
172 : * This allows the caller object to abort the
173 : * connection attempt if the caller object dies
174 : * before the attempt completes.
175 : *
176 : */
177 : CHIP_ERROR TCPConnect(const PeerAddress & address, Transport::AppTCPConnectionCallbackCtxt * appState,
178 : Transport::ActiveTCPConnectionHandle & outPeerConnState) override;
179 :
180 0 : bool CanSendToPeer(const PeerAddress & address) override
181 : {
182 0 : return (mState == TCPState::kInitialized) && (address.GetTransportType() == Type::kTcp) &&
183 0 : (address.GetIPAddress().Type() == mEndpointType);
184 : }
185 :
186 : const Optional<PeerAddress> GetConnectionPeerAddress(const Inet::TCPEndPointHandle & con)
187 : {
188 : ActiveTCPConnectionHandle activeConState = FindActiveConnection(con);
189 :
190 : return !activeConState.IsNull() ? MakeOptional<PeerAddress>(activeConState->mPeerAddr) : Optional<PeerAddress>::Missing();
191 : }
192 :
193 : /**
194 : * Helper method to determine if IO processing is still required for a TCP transport
195 : * before everything is cleaned up (socket closing is async, so after calling 'Close' on
196 : * the transport, some time may be needed to actually be able to close.)
197 : */
198 : bool HasActiveConnections() const;
199 :
200 : /**
201 : * Close all active connections.
202 : */
203 : void CloseActiveConnections();
204 :
205 : #if INET_CONFIG_TEST
206 : static bool sForceFailureInDoHandleIncomingConnection;
207 : #endif
208 :
209 : private:
210 : // Allow tests to access private members.
211 : template <size_t kActiveConnectionsSize, size_t kPendingPacketSize>
212 : friend class TCPBaseTestAccess;
213 :
214 : /**
215 : * Allocate and initialize a connection from the pool.
216 : */
217 : ActiveTCPConnectionState * AllocateConnection(const Inet::TCPEndPointHandle & endpoint, const PeerAddress & address);
218 : /**
219 : * Find an active connection to the given peer or return nullptr if
220 : * no active connection exists.
221 : */
222 : ActiveTCPConnectionHandle FindInUseConnection(const PeerAddress & addr);
223 : ActiveTCPConnectionState * FindActiveConnection(const Inet::TCPEndPointHandle & endPoint);
224 :
225 : /**
226 : * Find an allocated connection that matches the corresponding TCPEndPoint.
227 : */
228 : ActiveTCPConnectionHandle FindInUseConnection(const Inet::TCPEndPoint & endPoint);
229 :
230 : /**
231 : * Sends the specified message once a connection has been established.
232 : *
233 : * @param existing - an already-existing connection; must not be empty
234 : * @param msg - what buffer to send once a connection has been established.
235 : *
236 : * Ownership of msg is taken over and will be freed at some unspecified time
237 : * in the future (once connection succeeds/fails).
238 : */
239 : CHIP_ERROR SendAfterConnect(const ActiveTCPConnectionHandle & existing, System::PacketBufferHandle && msg);
240 :
241 : /**
242 : * Process a single received buffer from the specified peer address.
243 : *
244 : * @param endPoint the source end point from which the data comes from
245 : * @param peerAddress the peer the data is coming from
246 : * @param buffer the actual data
247 : *
248 : * Ownership of buffer is taken over and will be freed (or re-enqueued to the endPoint receive queue)
249 : * as needed during processing.
250 : */
251 : CHIP_ERROR ProcessReceivedBuffer(const Inet::TCPEndPointHandle & endPoint, const PeerAddress & peerAddress,
252 : System::PacketBufferHandle && buffer);
253 :
254 : /**
255 : * Process a single message of the specified size from a buffer.
256 : *
257 : * @param[in] peerAddress The peer the data is coming from.
258 : * @param[in,out] state The connection state, which contains the message. On entry, the payload points to the message
259 : * body (after the length). On exit, it points after the message (or the queue is null, if there
260 : * is no other data).
261 : * @param[in] messageSize Size of the single message.
262 : */
263 : CHIP_ERROR ProcessSingleMessage(const PeerAddress & peerAddress, ActiveTCPConnectionState & state, size_t messageSize);
264 :
265 : /**
266 : * Initiate a connection to the given peer. On connection completion,
267 : * HandleTCPConnectComplete callback would be called.
268 : *
269 : */
270 : CHIP_ERROR StartConnect(const PeerAddress & addr, AppTCPConnectionCallbackCtxt * appState,
271 : ActiveTCPConnectionHandle & outPeerConnState);
272 :
273 : // Close an active connection (corresponding to the passed
274 : // ActiveTCPConnectionState object)
275 : // and release from the pool.
276 : void TCPDisconnect(ActiveTCPConnectionState & conn, bool shouldAbort = false);
277 :
278 : /**
279 : * Gracefully Close or Abort a given connection.
280 : *
281 : */
282 : void CloseConnectionInternal(ActiveTCPConnectionState & connection, CHIP_ERROR err, SuppressCallback suppressCallback);
283 :
284 : // Close the listening socket endpoint
285 : void CloseListeningSocket();
286 :
287 : // Callback handler for TCPEndPoint. TCP message receive handler.
288 : // @see TCPEndpoint::OnDataReceivedFunct
289 : static CHIP_ERROR HandleTCPEndPointDataReceived(const Inet::TCPEndPointHandle & endPoint, System::PacketBufferHandle && buffer);
290 :
291 : // Callback handler for TCPEndPoint. Called when a connection has been completed.
292 : // @see TCPEndpoint::OnConnectCompleteFunct
293 : static void HandleTCPEndPointConnectComplete(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR err);
294 :
295 : // Callback handler for TCPEndPoint. Called when a connection has been closed.
296 : // @see TCPEndpoint::OnConnectionClosedFunct
297 : static void HandleTCPEndPointConnectionClosed(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR err);
298 :
299 : // Callback handler for TCPEndPoint. Called when a connection is received on the listening port.
300 : // @see TCPEndpoint::OnConnectionReceivedFunct
301 : static void HandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint, const Inet::TCPEndPointHandle & endPoint,
302 : const Inet::IPAddress & peerAddress, uint16_t peerPort);
303 :
304 : CHIP_ERROR DoHandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint, const Inet::TCPEndPointHandle & endPoint,
305 : const Inet::IPAddress & peerAddress, uint16_t peerPort);
306 :
307 : // Callback handler for handling accept error
308 : // @see TCPEndpoint::OnAcceptErrorFunct
309 : static void HandleAcceptError(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR err);
310 :
311 : void InitEndpoint(const Inet::TCPEndPointHandle & endpoint);
312 : CHIP_ERROR PrepareBuffer(System::PacketBufferHandle & msgBuf);
313 :
314 : Inet::TCPEndPointHandle mListenSocket; ///< TCP socket used by the transport
315 : Inet::IPAddressType mEndpointType = Inet::IPAddressType::kUnknown; ///< Socket listening type
316 : TCPState mState = TCPState::kNotReady; ///< State of the TCP transport
317 :
318 : // The configured timeout for the connection attempt to the peer, before
319 : // giving up.
320 : uint32_t mConnectTimeout = CHIP_CONFIG_TCP_CONNECT_TIMEOUT_MSECS;
321 :
322 : // Number of active and 'pending connection' endpoints
323 : size_t mUsedEndPointCount = 0;
324 :
325 : // Currently active connections
326 : ActiveTCPConnectionState * mActiveConnections;
327 : const size_t mActiveConnectionsSize;
328 :
329 : // Data to be sent when connections succeed
330 : PendingPacketPoolType & mPendingPackets;
331 : };
332 :
333 : template <size_t kActiveConnectionsSize, size_t kPendingPacketSize>
334 : class TCP : public TCPBase
335 : {
336 : public:
337 265 : TCP() : TCPBase(mConnectionsBuffer, kActiveConnectionsSize, mPendingPackets)
338 : {
339 265 : for (size_t i = 0; i < kActiveConnectionsSize; ++i)
340 : {
341 212 : mConnectionsBuffer[i].Init(nullptr, PeerAddress::Uninitialized(), [](auto &) {});
342 : }
343 53 : }
344 :
345 265 : ~TCP() override { mPendingPackets.ReleaseAll(); }
346 :
347 : private:
348 : ActiveTCPConnectionState mConnectionsBuffer[kActiveConnectionsSize];
349 : PoolImpl<PendingPacket, kPendingPacketSize, ObjectPoolMem::kInline, PendingPacketPoolType::Interface> mPendingPackets;
350 : };
351 :
352 : } // namespace Transport
353 : } // namespace chip
|