Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 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 17 : explicit TcpListenParameters(Inet::EndPointManager<Inet::TCPEndPoint> * endPointManager) : mEndPointManager(endPointManager) {}
49 1 : TcpListenParameters(const TcpListenParameters &) = default;
50 : TcpListenParameters(TcpListenParameters &&) = default;
51 :
52 17 : Inet::EndPointManager<Inet::TCPEndPoint> * GetEndPointManager() { return mEndPointManager; }
53 :
54 33 : Inet::IPAddressType GetAddressType() const { return mAddressType; }
55 17 : TcpListenParameters & SetAddressType(Inet::IPAddressType type)
56 : {
57 17 : mAddressType = type;
58 :
59 17 : return *this;
60 : }
61 :
62 32 : uint16_t GetListenPort() const { return mListenPort; }
63 17 : TcpListenParameters & SetListenPort(uint16_t port)
64 : {
65 17 : mListenPort = port;
66 :
67 17 : return *this;
68 : }
69 :
70 16 : Inet::InterfaceId GetInterfaceId() const { return mInterfaceId; }
71 : TcpListenParameters & SetInterfaceId(Inet::InterfaceId id)
72 : {
73 : mInterfaceId = id;
74 :
75 : return *this;
76 : }
77 :
78 20 : 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 27 : TCPBase(ActiveTCPConnectionState * activeConnectionsBuffer, size_t bufferSize, PendingPacketPoolType & packetBuffers) :
127 27 : mActiveConnections(activeConnectionsBuffer), mActiveConnectionsSize(bufferSize), mPendingPackets(packetBuffers)
128 : {
129 : // activeConnectionsBuffer must be initialized by the caller.
130 27 : }
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 ActiveTCPConnectionHolder & 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::ActiveTCPConnectionHolder & 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::TCPEndPoint * con)
187 : {
188 : ActiveTCPConnectionHolder 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 : private:
206 : // Allow tests to access private members.
207 : template <size_t kActiveConnectionsSize, size_t kPendingPacketSize>
208 : friend class TCPBaseTestAccess;
209 :
210 : /**
211 : * Allocate and initialize a connection from the pool.
212 : */
213 : ActiveTCPConnectionState * AllocateConnection(Inet::TCPEndPoint * endpoint, const PeerAddress & address);
214 : /**
215 : * Find an active connection to the given peer or return nullptr if
216 : * no active connection exists.
217 : */
218 : ActiveTCPConnectionHolder FindInUseConnection(const PeerAddress & addr);
219 : ActiveTCPConnectionState * FindActiveConnection(const Inet::TCPEndPoint * endPoint);
220 :
221 : /**
222 : * Find an allocated connection that matches the corresponding TCPEndPoint.
223 : */
224 : ActiveTCPConnectionHolder FindInUseConnection(const Inet::TCPEndPoint * endPoint);
225 :
226 : /**
227 : * Sends the specified message once a connection has been established.
228 : *
229 : * @param existing - an already-existing connection; must not be empty
230 : * @param msg - what buffer to send once a connection has been established.
231 : *
232 : * Ownership of msg is taken over and will be freed at some unspecified time
233 : * in the future (once connection succeeds/fails).
234 : */
235 : CHIP_ERROR SendAfterConnect(const ActiveTCPConnectionHolder & existing, System::PacketBufferHandle && msg);
236 :
237 : /**
238 : * Process a single received buffer from the specified peer address.
239 : *
240 : * @param endPoint the source end point from which the data comes from
241 : * @param peerAddress the peer the data is coming from
242 : * @param buffer the actual data
243 : *
244 : * Ownership of buffer is taken over and will be freed (or re-enqueued to the endPoint receive queue)
245 : * as needed during processing.
246 : */
247 : CHIP_ERROR ProcessReceivedBuffer(Inet::TCPEndPoint * endPoint, const PeerAddress & peerAddress,
248 : System::PacketBufferHandle && buffer);
249 :
250 : /**
251 : * Process a single message of the specified size from a buffer.
252 : *
253 : * @param[in] peerAddress The peer the data is coming from.
254 : * @param[in,out] state The connection state, which contains the message. On entry, the payload points to the message
255 : * body (after the length). On exit, it points after the message (or the queue is null, if there
256 : * is no other data).
257 : * @param[in] messageSize Size of the single message.
258 : */
259 : CHIP_ERROR ProcessSingleMessage(const PeerAddress & peerAddress, ActiveTCPConnectionState & state, size_t messageSize);
260 :
261 : /**
262 : * Initiate a connection to the given peer. On connection completion,
263 : * HandleTCPConnectComplete callback would be called.
264 : *
265 : */
266 : CHIP_ERROR StartConnect(const PeerAddress & addr, AppTCPConnectionCallbackCtxt * appState,
267 : ActiveTCPConnectionHolder & outPeerConnState);
268 :
269 : // Close an active connection (corresponding to the passed
270 : // ActiveTCPConnectionState object)
271 : // and release from the pool.
272 : void TCPDisconnect(ActiveTCPConnectionState & conn, bool shouldAbort = false);
273 :
274 : /**
275 : * Gracefully Close or Abort a given connection.
276 : *
277 : */
278 : void CloseConnectionInternal(ActiveTCPConnectionState & connection, CHIP_ERROR err, SuppressCallback suppressCallback);
279 :
280 : // Close the listening socket endpoint
281 : void CloseListeningSocket();
282 :
283 : // Callback handler for TCPEndPoint. TCP message receive handler.
284 : // @see TCPEndpoint::OnDataReceivedFunct
285 : static CHIP_ERROR HandleTCPEndPointDataReceived(Inet::TCPEndPoint * endPoint, System::PacketBufferHandle && buffer);
286 :
287 : // Callback handler for TCPEndPoint. Called when a connection has been completed.
288 : // @see TCPEndpoint::OnConnectCompleteFunct
289 : static void HandleTCPEndPointConnectComplete(Inet::TCPEndPoint * endPoint, CHIP_ERROR err);
290 :
291 : // Callback handler for TCPEndPoint. Called when a connection has been closed.
292 : // @see TCPEndpoint::OnConnectionClosedFunct
293 : static void HandleTCPEndPointConnectionClosed(Inet::TCPEndPoint * endPoint, CHIP_ERROR err);
294 :
295 : // Callback handler for TCPEndPoint. Called when a connection is received on the listening port.
296 : // @see TCPEndpoint::OnConnectionReceivedFunct
297 : static void HandleIncomingConnection(Inet::TCPEndPoint * listenEndPoint, Inet::TCPEndPoint * endPoint,
298 : const Inet::IPAddress & peerAddress, uint16_t peerPort);
299 :
300 : // Callback handler for handling accept error
301 : // @see TCPEndpoint::OnAcceptErrorFunct
302 : static void HandleAcceptError(Inet::TCPEndPoint * endPoint, CHIP_ERROR err);
303 :
304 : void InitEndpoint(Inet::TCPEndPoint * endpoint);
305 : CHIP_ERROR TryResetConnection(ActiveTCPConnectionState & connection);
306 : CHIP_ERROR PrepareBuffer(System::PacketBufferHandle & msgBuf);
307 :
308 : Inet::TCPEndPoint * mListenSocket = nullptr; ///< TCP socket used by the transport
309 : Inet::IPAddressType mEndpointType = Inet::IPAddressType::kUnknown; ///< Socket listening type
310 : TCPState mState = TCPState::kNotReady; ///< State of the TCP transport
311 :
312 : // The configured timeout for the connection attempt to the peer, before
313 : // giving up.
314 : uint32_t mConnectTimeout = CHIP_CONFIG_TCP_CONNECT_TIMEOUT_MSECS;
315 :
316 : // Number of active and 'pending connection' endpoints
317 : size_t mUsedEndPointCount = 0;
318 :
319 : // Currently active connections
320 : ActiveTCPConnectionState * mActiveConnections;
321 : const size_t mActiveConnectionsSize;
322 :
323 : // Data to be sent when connections succeed
324 : PendingPacketPoolType & mPendingPackets;
325 : };
326 :
327 : template <size_t kActiveConnectionsSize, size_t kPendingPacketSize>
328 : class TCP : public TCPBase
329 : {
330 : public:
331 135 : TCP() : TCPBase(mConnectionsBuffer, kActiveConnectionsSize, mPendingPackets)
332 : {
333 135 : for (size_t i = 0; i < kActiveConnectionsSize; ++i)
334 : {
335 108 : mConnectionsBuffer[i].Init(nullptr, PeerAddress::Uninitialized(), [](auto &) {});
336 : }
337 27 : }
338 :
339 135 : ~TCP() override { mPendingPackets.ReleaseAll(); }
340 :
341 : private:
342 : ActiveTCPConnectionState mConnectionsBuffer[kActiveConnectionsSize];
343 : PoolImpl<PendingPacket, kPendingPacketSize, ObjectPoolMem::kInline, PendingPacketPoolType::Interface> mPendingPackets;
344 : };
345 :
346 : } // namespace Transport
347 : } // namespace chip
|