/src/connectedhomeip/src/transport/raw/TCP.h
Line | Count | Source |
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 | 0 | explicit TcpListenParameters(Inet::EndPointManager<Inet::TCPEndPoint> * endPointManager) : mEndPointManager(endPointManager) {} |
49 | 0 | TcpListenParameters(const TcpListenParameters &) = default; |
50 | | TcpListenParameters(TcpListenParameters &&) = default; |
51 | | |
52 | 0 | Inet::EndPointManager<Inet::TCPEndPoint> * GetEndPointManager() { return mEndPointManager; } |
53 | | |
54 | 0 | Inet::IPAddressType GetAddressType() const { return mAddressType; } |
55 | | TcpListenParameters & SetAddressType(Inet::IPAddressType type) |
56 | 0 | { |
57 | 0 | mAddressType = type; |
58 | 0 |
|
59 | 0 | return *this; |
60 | 0 | } |
61 | | |
62 | 0 | uint16_t GetListenPort() const { return mListenPort; } |
63 | | TcpListenParameters & SetListenPort(uint16_t port) |
64 | 0 | { |
65 | 0 | mListenPort = port; |
66 | 0 |
|
67 | 0 | return *this; |
68 | 0 | } |
69 | | |
70 | 0 | Inet::InterfaceId GetInterfaceId() const { return mInterfaceId; } |
71 | | TcpListenParameters & SetInterfaceId(Inet::InterfaceId id) |
72 | 0 | { |
73 | 0 | mInterfaceId = id; |
74 | 0 |
|
75 | 0 | return *this; |
76 | 0 | } |
77 | | |
78 | 0 | bool IsServerListenEnabled() const { return mServerListenEnabled; } |
79 | | TcpListenParameters & SetServerListenEnabled(bool listenEnable) |
80 | 0 | { |
81 | 0 | mServerListenEnabled = listenEnable; |
82 | 0 |
|
83 | 0 | return *this; |
84 | 0 | } |
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 | | PendingPacket(const PeerAddress & peerAddress, System::PacketBufferHandle && packetBuffer) : |
100 | | mPeerAddress(peerAddress), mPacketBuffer(std::move(packetBuffer)) |
101 | 0 | {} |
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 | | TCPBase(ActiveTCPConnectionState * activeConnectionsBuffer, size_t bufferSize, PendingPacketPoolType & packetBuffers) : |
127 | | mActiveConnections(activeConnectionsBuffer), mActiveConnectionsSize(bufferSize), mPendingPackets(packetBuffers) |
128 | 0 | { |
129 | 0 | // activeConnectionsBuffer must be initialized by the caller. |
130 | 0 | } |
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 | 0 | 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 | | bool CanSendToPeer(const PeerAddress & address) override |
181 | 0 | { |
182 | 0 | return (mState == TCPState::kInitialized) && (address.GetTransportType() == Type::kTcp) && |
183 | 0 | (address.GetIPAddress().Type() == mEndpointType); |
184 | 0 | } |
185 | | |
186 | | const Optional<PeerAddress> GetConnectionPeerAddress(const Inet::TCPEndPointHandle & con) |
187 | 0 | { |
188 | 0 | ActiveTCPConnectionHandle activeConState = FindActiveConnection(con); |
189 | 0 |
|
190 | 0 | return !activeConState.IsNull() ? MakeOptional<PeerAddress>(activeConState->mPeerAddr) : Optional<PeerAddress>::Missing(); |
191 | 0 | } |
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 | 0 | TCP() : TCPBase(mConnectionsBuffer, kActiveConnectionsSize, mPendingPackets) |
338 | 0 | { |
339 | 0 | for (size_t i = 0; i < kActiveConnectionsSize; ++i) |
340 | 0 | { |
341 | 0 | mConnectionsBuffer[i].Init(nullptr, PeerAddress::Uninitialized(), [](auto &) {}); |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | ~TCP() override |
346 | 0 | { |
347 | 0 | mPendingPackets.ReleaseAll(); |
348 | | |
349 | | // Call Close to free the listening socket and close all active connections. |
350 | 0 | Close(); |
351 | 0 | } |
352 | | |
353 | | private: |
354 | | ActiveTCPConnectionState mConnectionsBuffer[kActiveConnectionsSize]; |
355 | | PoolImpl<PendingPacket, kPendingPacketSize, ObjectPoolMem::kInline, PendingPacketPoolType::Interface> mPendingPackets; |
356 | | }; |
357 | | |
358 | | } // namespace Transport |
359 | | } // namespace chip |