Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2025 Project CHIP Authors
4 : * Copyright (c) 2013-2017 Nest Labs, Inc.
5 : * All rights reserved.
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : /**
21 : * @file
22 : * This file implements the CHIP Transport object that maintains TCP connections
23 : * to peers. Handles both establishing new connections and accepting peer connection
24 : * requests.
25 : */
26 : #include <transport/raw/TCP.h>
27 :
28 : #include <lib/core/CHIPEncoding.h>
29 : #include <lib/support/CodeUtils.h>
30 : #include <lib/support/logging/CHIPLogging.h>
31 : #include <transport/raw/MessageHeader.h>
32 :
33 : #include <inttypes.h>
34 : #include <limits>
35 :
36 : namespace chip {
37 : namespace Transport {
38 : namespace {
39 :
40 : using namespace chip::Encoding;
41 :
42 : // Packets start with a 32-bit size field.
43 : constexpr size_t kPacketSizeBytes = 4;
44 :
45 : static_assert(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve <= UINT32_MAX, "Cast below could truncate the value");
46 : static_assert(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve >= kPacketSizeBytes,
47 : "Large buffer allocation should be large enough to hold the length field");
48 :
49 : constexpr uint32_t kMaxTCPMessageSize =
50 : static_cast<uint32_t>(System::PacketBuffer::kLargeBufMaxSizeWithoutReserve - kPacketSizeBytes);
51 :
52 : constexpr int kListenBacklogSize = 2;
53 :
54 25 : CHIP_ERROR GetPeerAddress(Inet::TCPEndPoint & endPoint, PeerAddress & outAddr)
55 : {
56 : Inet::IPAddress ipAddress;
57 : uint16_t port;
58 25 : Inet::InterfaceId interfaceId;
59 25 : ReturnErrorOnFailure(endPoint.GetPeerInfo(&ipAddress, &port));
60 25 : ReturnErrorOnFailure(endPoint.GetInterfaceId(&interfaceId));
61 25 : outAddr = PeerAddress::TCP(ipAddress, port, interfaceId);
62 :
63 25 : return CHIP_NO_ERROR;
64 : }
65 :
66 : } // namespace
67 :
68 290 : TCPBase::~TCPBase() = default;
69 :
70 295 : void TCPBase::CloseActiveConnections()
71 : {
72 : // Nothing to do; we can't release as long as references are being held
73 1467 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
74 : {
75 1172 : if (mActiveConnections[i].InUse())
76 : {
77 3 : CloseConnectionInternal(mActiveConnections[i], CHIP_NO_ERROR, SuppressCallback::Yes);
78 : }
79 : }
80 295 : }
81 :
82 26 : CHIP_ERROR TCPBase::Init(TcpListenParameters & params)
83 : {
84 26 : CHIP_ERROR err = CHIP_NO_ERROR;
85 :
86 26 : VerifyOrExit(mState == TCPState::kNotReady, err = CHIP_ERROR_INCORRECT_STATE);
87 :
88 26 : mEndpointType = params.GetAddressType();
89 :
90 : // Primary socket endpoint created to help get EndPointManager handle for creating multiple
91 : // connection endpoints at runtime.
92 26 : err = params.GetEndPointManager()->NewEndPoint(mListenSocket);
93 26 : SuccessOrExit(err);
94 :
95 26 : if (params.IsServerListenEnabled())
96 : {
97 48 : err = mListenSocket->Bind(params.GetAddressType(), Inet::IPAddress::Any, params.GetListenPort(),
98 24 : params.GetInterfaceId().IsPresent());
99 24 : SuccessOrExit(err);
100 :
101 24 : mListenSocket->mAppState = reinterpret_cast<void *>(this);
102 24 : mListenSocket->OnConnectionReceived = HandleIncomingConnection;
103 24 : mListenSocket->OnAcceptError = HandleAcceptError;
104 :
105 24 : err = mListenSocket->Listen(kListenBacklogSize);
106 24 : SuccessOrExit(err);
107 24 : ChipLogProgress(Inet, "TCP server listening on port %d for incoming connections", params.GetListenPort());
108 : }
109 :
110 26 : mState = TCPState::kInitialized;
111 :
112 26 : exit:
113 52 : if (err != CHIP_NO_ERROR)
114 : {
115 0 : ChipLogError(Inet, "Failed to initialize TCP transport: %" CHIP_ERROR_FORMAT, err.Format());
116 0 : mListenSocket.Release();
117 : }
118 :
119 26 : return err;
120 : }
121 :
122 295 : void TCPBase::Close()
123 : {
124 295 : mListenSocket.Release();
125 :
126 295 : CloseActiveConnections();
127 :
128 295 : mState = TCPState::kNotReady;
129 295 : }
130 :
131 49 : ActiveTCPConnectionState * TCPBase::AllocateConnection(const Inet::TCPEndPointHandle & endpoint, const PeerAddress & address)
132 : {
133 : // If a peer initiates a connection through HandleIncomingConnection but the connection is never claimed
134 : // in ProcessSingleMessage, we'll be left with a dangling ActiveTCPConnectionState which can be
135 : // reclaimed. Don't try to reclaim these connections unless we're out of space
136 49 : for (int reclaim = 0; reclaim < 2; reclaim++)
137 : {
138 65 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
139 : {
140 65 : ActiveTCPConnectionState * activeConnection = &mActiveConnections[i];
141 65 : if (!activeConnection->InUse() && (activeConnection->GetReferenceCount() == 0))
142 : {
143 : // Update state for the active connection
144 85 : activeConnection->Init(endpoint, address, [this](auto & conn) { TCPDisconnect(conn, true); });
145 49 : return activeConnection;
146 : }
147 : }
148 :
149 : // Out of space; reclaim connections that were never claimed by ProcessSingleMessage
150 : // (i.e. that have a ref count of 0)
151 0 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
152 : {
153 0 : ActiveTCPConnectionState * activeConnection = &mActiveConnections[i];
154 0 : if (!activeConnection->InUse() && (activeConnection->GetReferenceCount() != 0))
155 : {
156 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
157 0 : activeConnection->mPeerAddr.ToString(addrStr);
158 0 : ChipLogError(Inet, "Leaked TCP connection %p to %s.", activeConnection, addrStr);
159 : // Try to notify callbacks in the hope that they release; the connection is no good
160 0 : CloseConnectionInternal(*activeConnection, CHIP_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY, SuppressCallback::No);
161 : }
162 0 : ActiveTCPConnectionHandle releaseUnclaimed(activeConnection);
163 0 : }
164 : }
165 0 : return nullptr;
166 : }
167 :
168 : // Find an ActiveTCPConnectionState corresponding to a peer address
169 48 : ActiveTCPConnectionHandle TCPBase::FindInUseConnection(const PeerAddress & address)
170 : {
171 48 : if (address.GetTransportType() != Type::kTcp)
172 : {
173 0 : return nullptr;
174 : }
175 :
176 172 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
177 : {
178 139 : auto & conn = mActiveConnections[i];
179 139 : if (!conn.InUse())
180 : {
181 124 : continue;
182 : }
183 :
184 15 : if (conn.mPeerAddr == address)
185 : {
186 : Inet::IPAddress addr;
187 : uint16_t port;
188 15 : if (conn.IsConnected())
189 : {
190 : // Failure to get peer information means the connection is bad; close it
191 6 : CHIP_ERROR err = conn.mEndPoint->GetPeerInfo(&addr, &port);
192 12 : if (err != CHIP_NO_ERROR)
193 : {
194 0 : CloseConnectionInternal(conn, err, SuppressCallback::No);
195 0 : return nullptr;
196 : }
197 : }
198 :
199 15 : return ActiveTCPConnectionHandle(&conn);
200 : }
201 : }
202 :
203 33 : return nullptr;
204 : }
205 :
206 : // Find the ActiveTCPConnectionState for a given TCPEndPoint
207 16 : ActiveTCPConnectionState * TCPBase::FindActiveConnection(const Inet::TCPEndPointHandle & endPoint)
208 : {
209 23 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
210 : {
211 23 : if (mActiveConnections[i].mEndPoint == endPoint && mActiveConnections[i].IsConnected())
212 : {
213 16 : return &mActiveConnections[i];
214 : }
215 : }
216 0 : return nullptr;
217 : }
218 :
219 29 : ActiveTCPConnectionHandle TCPBase::FindInUseConnection(const Inet::TCPEndPoint & endPoint)
220 : {
221 36 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
222 : {
223 36 : if (mActiveConnections[i].mEndPoint == endPoint)
224 : {
225 29 : return ActiveTCPConnectionHandle(&mActiveConnections[i]);
226 : }
227 : }
228 0 : return nullptr;
229 : }
230 :
231 13 : CHIP_ERROR TCPBase::PrepareBuffer(System::PacketBufferHandle & msgBuf)
232 : {
233 : // Sent buffer data format is:
234 : // - packet size as a uint32_t
235 : // - actual data
236 :
237 13 : VerifyOrReturnError(mState == TCPState::kInitialized, CHIP_ERROR_INCORRECT_STATE);
238 13 : VerifyOrReturnError(kPacketSizeBytes + msgBuf->DataLength() <= System::PacketBuffer::kLargeBufMaxSizeWithoutReserve,
239 : CHIP_ERROR_INVALID_ARGUMENT);
240 :
241 : static_assert(kPacketSizeBytes <= UINT16_MAX);
242 13 : VerifyOrReturnError(msgBuf->EnsureReservedSize(static_cast<uint16_t>(kPacketSizeBytes)), CHIP_ERROR_NO_MEMORY);
243 :
244 13 : msgBuf->SetStart(msgBuf->Start() - kPacketSizeBytes);
245 :
246 13 : uint8_t * output = msgBuf->Start();
247 13 : LittleEndian::Write32(output, static_cast<uint32_t>(msgBuf->DataLength() - kPacketSizeBytes));
248 :
249 13 : return CHIP_NO_ERROR;
250 : }
251 :
252 9 : CHIP_ERROR TCPBase::SendMessage(const Transport::PeerAddress & address, System::PacketBufferHandle && msgBuf)
253 : {
254 9 : VerifyOrReturnError(address.GetTransportType() == Type::kTcp, CHIP_ERROR_INVALID_ARGUMENT);
255 9 : ReturnErrorOnFailure(PrepareBuffer(msgBuf));
256 :
257 : // Must find a previously-established connection with an owning reference
258 9 : auto connection = FindInUseConnection(address);
259 9 : VerifyOrReturnError(!connection.IsNull(), CHIP_ERROR_INCORRECT_STATE);
260 9 : if (connection->IsConnected())
261 : {
262 2 : return connection->mEndPoint->Send(std::move(msgBuf));
263 : }
264 :
265 7 : return SendAfterConnect(connection, std::move(msgBuf));
266 9 : }
267 :
268 4 : CHIP_ERROR TCPBase::SendMessage(const ActiveTCPConnectionHandle & connection, System::PacketBufferHandle && msgBuf)
269 : {
270 4 : VerifyOrReturnError(!connection.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
271 4 : ReturnErrorOnFailure(PrepareBuffer(msgBuf));
272 :
273 4 : if (connection->IsConnected())
274 : {
275 4 : return connection->mEndPoint->Send(std::move(msgBuf));
276 : }
277 :
278 0 : return SendAfterConnect(connection, std::move(msgBuf));
279 : }
280 :
281 37 : CHIP_ERROR TCPBase::StartConnect(const PeerAddress & addr, Transport::AppTCPConnectionCallbackCtxt * appState,
282 : ActiveTCPConnectionHandle & outPeerConnState)
283 : {
284 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
285 37 : Inet::TCPEndPointHandle endPoint;
286 37 : outPeerConnState.Release();
287 37 : ReturnErrorOnFailure(mListenSocket->GetEndPointManager().NewEndPoint(endPoint));
288 :
289 37 : InitEndpoint(endPoint);
290 :
291 37 : ActiveTCPConnectionHandle activeConnection = FindInUseConnection(addr);
292 : // Re-use existing connection to peer if already connected
293 37 : if (!activeConnection.IsNull())
294 : {
295 4 : if (appState != nullptr)
296 : {
297 : // We do not support parallel attempts to connect to peer when setting appState
298 0 : VerifyOrReturnError(activeConnection->mConnectionState == TCPState::kConnected &&
299 : activeConnection->mAppState == nullptr,
300 : CHIP_ERROR_INCORRECT_STATE);
301 0 : activeConnection->mAppState = appState;
302 : }
303 4 : outPeerConnState = activeConnection;
304 :
305 4 : if (activeConnection->mConnectionState == TCPState::kConnected)
306 : {
307 2 : HandleConnectionAttemptComplete(activeConnection, CHIP_NO_ERROR);
308 : }
309 :
310 4 : return CHIP_NO_ERROR;
311 : }
312 :
313 33 : activeConnection = AllocateConnection(endPoint, addr);
314 33 : VerifyOrReturnError(!activeConnection.IsNull(), CHIP_ERROR_NO_MEMORY);
315 33 : activeConnection->mAppState = appState;
316 33 : activeConnection->mConnectionState = TCPState::kConnecting;
317 :
318 33 : mUsedEndPointCount++;
319 :
320 33 : ReturnErrorOnFailure(endPoint->Connect(addr.GetIPAddress(), addr.GetPort(), addr.GetInterface()));
321 :
322 : // Set the return value of the peer connection state to the allocated
323 : // connection.
324 21 : outPeerConnState = activeConnection;
325 :
326 21 : return CHIP_NO_ERROR;
327 : #else
328 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
329 : #endif
330 37 : }
331 :
332 7 : CHIP_ERROR TCPBase::SendAfterConnect(const ActiveTCPConnectionHandle & existing, System::PacketBufferHandle && msg)
333 : {
334 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
335 7 : VerifyOrReturnError(!existing.IsNull(), CHIP_ERROR_INCORRECT_STATE);
336 7 : const PeerAddress & addr = existing->mPeerAddr;
337 :
338 : // This will initiate a connection to the specified peer
339 7 : bool alreadyConnecting = false;
340 :
341 : // Iterate through the ENTIRE array. If a pending packet for
342 : // the address already exists, this means a connection is pending and
343 : // does NOT need to be re-established.
344 7 : mPendingPackets.ForEachActiveObject([&](PendingPacket * pending) {
345 2 : if (pending->mPeerAddress == addr)
346 : {
347 : // same destination exists.
348 2 : alreadyConnecting = true;
349 2 : pending->mPacketBuffer->AddToEnd(std::move(msg));
350 2 : return Loop::Break;
351 : }
352 0 : return Loop::Continue;
353 : });
354 :
355 : // If already connecting, buffer was just enqueued for more sending
356 7 : if (alreadyConnecting)
357 : {
358 2 : return CHIP_NO_ERROR;
359 : }
360 :
361 : // enqueue the packet once the connection succeeds
362 5 : VerifyOrReturnError(mPendingPackets.CreateObject(addr, std::move(msg)) != nullptr, CHIP_ERROR_NO_MEMORY);
363 :
364 5 : return CHIP_NO_ERROR;
365 : #else
366 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
367 : #endif
368 : }
369 :
370 16 : CHIP_ERROR TCPBase::ProcessReceivedBuffer(const Inet::TCPEndPointHandle & endPoint, const PeerAddress & peerAddress,
371 : System::PacketBufferHandle && buffer)
372 : {
373 16 : ActiveTCPConnectionState * state = FindActiveConnection(endPoint);
374 : // There must be a preceding TCPConnect to hold a reference to connection
375 16 : VerifyOrReturnError(state != nullptr, CHIP_ERROR_INTERNAL);
376 16 : state->mReceived.AddToEnd(std::move(buffer));
377 :
378 36 : while (!state->mReceived.IsNull())
379 : {
380 : uint8_t messageSizeBuf[kPacketSizeBytes];
381 22 : CHIP_ERROR err = state->mReceived->Read(messageSizeBuf);
382 44 : if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
383 : {
384 : // We don't have enough data to read the message size. Wait until there's more.
385 0 : return CHIP_NO_ERROR;
386 : }
387 44 : if (err != CHIP_NO_ERROR)
388 : {
389 0 : return err;
390 : }
391 22 : uint32_t messageSize = LittleEndian::Get32(messageSizeBuf);
392 22 : if (messageSize >= kMaxTCPMessageSize)
393 : {
394 : // Message is too big for this node to process. Disconnect from peer.
395 1 : ChipLogError(Inet, "Received TCP message of length %" PRIu32 " exceeds limit.", messageSize);
396 1 : CloseConnectionInternal(*state, CHIP_ERROR_MESSAGE_TOO_LONG, SuppressCallback::No);
397 :
398 1 : return CHIP_ERROR_MESSAGE_TOO_LONG;
399 : }
400 : // The subtraction will not underflow because we successfully read kPacketSizeBytes.
401 21 : if (messageSize > (state->mReceived->TotalLength() - kPacketSizeBytes))
402 : {
403 : // We have not yet received the complete message.
404 0 : return CHIP_NO_ERROR;
405 : }
406 :
407 21 : state->mReceived.Consume(kPacketSizeBytes);
408 :
409 21 : if (messageSize == 0)
410 : {
411 : // No payload but considered a valid message. Return success to keep the connection alive.
412 1 : return CHIP_NO_ERROR;
413 : }
414 :
415 20 : ReturnErrorOnFailure(ProcessSingleMessage(peerAddress, *state, messageSize));
416 : }
417 :
418 14 : return CHIP_NO_ERROR;
419 : }
420 :
421 20 : CHIP_ERROR TCPBase::ProcessSingleMessage(const PeerAddress & peerAddress, ActiveTCPConnectionState & state, size_t messageSize)
422 : {
423 : // We enter with `state->mReceived` containing at least one full message, perhaps in a chain.
424 : // `state->mReceived->Start()` currently points to the message data.
425 : // On exit, `state->mReceived` will have had `messageSize` bytes consumed, no matter what.
426 20 : System::PacketBufferHandle message;
427 :
428 20 : if (state.mReceived->DataLength() == messageSize)
429 : {
430 : // In this case, the head packet buffer contains exactly the message.
431 : // This is common because typical messages fit in a network packet, and are delivered as such.
432 : // Peel off the head to pass upstream, which effectively consumes it from `state->mReceived`.
433 13 : message = state.mReceived.PopHead();
434 : }
435 : else
436 : {
437 : // The message is either longer or shorter than the head buffer.
438 : // In either case, copy the message to a fresh linear buffer to pass upstream. We always copy, rather than provide
439 : // a shared reference to the current buffer, in case upper layers manipulate the buffer in ways that would affect
440 : // our use, e.g. chaining it elsewhere or reusing space beyond the current message.
441 7 : message = System::PacketBufferHandle::New(messageSize, 0);
442 7 : if (message.IsNull())
443 : {
444 0 : return CHIP_ERROR_NO_MEMORY;
445 : }
446 7 : CHIP_ERROR err = state.mReceived->Read(message->Start(), messageSize);
447 7 : state.mReceived.Consume(messageSize);
448 7 : ReturnErrorOnFailure(err);
449 7 : message->SetDataLength(messageSize);
450 : }
451 :
452 20 : MessageTransportContext msgContext;
453 20 : msgContext.conn = &state; // Take ownership
454 20 : HandleMessageReceived(peerAddress, std::move(message), &msgContext);
455 20 : return CHIP_NO_ERROR;
456 20 : }
457 :
458 49 : void TCPBase::CloseConnectionInternal(ActiveTCPConnectionState & connection, CHIP_ERROR err, SuppressCallback suppressCallback)
459 : {
460 49 : if (connection.mConnectionState == TCPState::kClosed || !connection.mEndPoint)
461 : {
462 0 : return;
463 : }
464 : TCPState prevState;
465 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
466 49 : connection.mPeerAddr.ToString(addrStr);
467 49 : ChipLogProgress(Inet, "Closing connection with peer %s.", addrStr);
468 :
469 49 : Inet::TCPEndPointHandle endpoint = connection.mEndPoint;
470 49 : connection.mEndPoint.Release();
471 98 : if (err == CHIP_NO_ERROR)
472 : {
473 3 : endpoint->Close();
474 : }
475 : else
476 : {
477 46 : endpoint->Abort();
478 : }
479 :
480 49 : prevState = connection.mConnectionState;
481 49 : connection.mConnectionState = TCPState::kClosed;
482 :
483 49 : if (suppressCallback == SuppressCallback::No)
484 : {
485 12 : if (prevState == TCPState::kConnecting)
486 : {
487 0 : ActiveTCPConnectionHandle holder(&connection);
488 : // Call upper layer connection attempt complete handler
489 0 : HandleConnectionAttemptComplete(holder, err);
490 0 : }
491 : else
492 : {
493 : // Call upper layer connection closed handler
494 12 : HandleConnectionClosed(connection, err);
495 : }
496 : }
497 :
498 49 : connection.Free();
499 49 : mUsedEndPointCount--;
500 49 : }
501 :
502 9 : CHIP_ERROR TCPBase::HandleTCPEndPointDataReceived(const Inet::TCPEndPointHandle & endPoint, System::PacketBufferHandle && buffer)
503 : {
504 9 : PeerAddress peerAddress;
505 9 : ReturnErrorOnFailure(GetPeerAddress(*endPoint, peerAddress));
506 :
507 9 : TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
508 9 : CHIP_ERROR err = tcp->ProcessReceivedBuffer(endPoint, peerAddress, std::move(buffer));
509 :
510 18 : if (err != CHIP_NO_ERROR)
511 : {
512 : // Connection could need to be closed at this point
513 0 : ChipLogError(Inet, "Failed to accept received TCP message: %" CHIP_ERROR_FORMAT, err.Format());
514 0 : return CHIP_ERROR_UNEXPECTED_EVENT;
515 : }
516 9 : return CHIP_NO_ERROR;
517 : }
518 :
519 20 : void TCPBase::HandleTCPEndPointConnectComplete(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR conErr)
520 : {
521 20 : CHIP_ERROR err = CHIP_NO_ERROR;
522 20 : bool foundPendingPacket = false;
523 20 : TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
524 20 : ActiveTCPConnectionHandle activeConnection;
525 :
526 20 : PeerAddress addr;
527 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
528 20 : activeConnection = tcp->FindInUseConnection(*endPoint);
529 20 : if (activeConnection.IsNull())
530 : {
531 0 : err = GetPeerAddress(*endPoint, addr);
532 : }
533 : else
534 : {
535 20 : addr = activeConnection->mPeerAddr;
536 : }
537 40 : if (err == CHIP_NO_ERROR)
538 : {
539 20 : addr.ToString(addrStr);
540 : }
541 :
542 60 : if (conErr != CHIP_NO_ERROR || err != CHIP_NO_ERROR)
543 : {
544 0 : auto failure = (conErr != CHIP_NO_ERROR) ? conErr : err;
545 0 : if (!activeConnection.IsNull())
546 : {
547 0 : tcp->CloseConnectionInternal(*activeConnection, failure, SuppressCallback::No);
548 : }
549 0 : ChipLogError(Inet, "Connection establishment with %s encountered an error: %" CHIP_ERROR_FORMAT, addrStr, failure.Format());
550 0 : return;
551 : }
552 : // Set the Data received handler when connection completes
553 20 : endPoint->OnDataReceived = HandleTCPEndPointDataReceived;
554 20 : endPoint->OnDataSent = nullptr;
555 20 : endPoint->OnConnectionClosed = HandleTCPEndPointConnectionClosed;
556 :
557 20 : VerifyOrDie(!activeConnection.IsNull());
558 :
559 : // Set to Connected state
560 20 : activeConnection->mConnectionState = TCPState::kConnected;
561 :
562 : // Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true.
563 : // This is to expedite transmission of payload data and not rely on the
564 : // network stack's configuration of collating enough data in the TCP
565 : // window to begin transmission.
566 20 : err = endPoint->EnableNoDelay();
567 40 : if (err != CHIP_NO_ERROR)
568 : {
569 0 : tcp->CloseConnectionInternal(*activeConnection, err, SuppressCallback::No);
570 0 : return;
571 : }
572 :
573 : // Send any pending packets that are queued for this connection
574 20 : tcp->mPendingPackets.ForEachActiveObject([&](PendingPacket * pending) {
575 5 : if (pending->mPeerAddress == addr)
576 : {
577 5 : foundPendingPacket = true;
578 5 : System::PacketBufferHandle buffer = std::move(pending->mPacketBuffer);
579 5 : tcp->mPendingPackets.ReleaseObject(pending);
580 :
581 15 : if ((conErr == CHIP_NO_ERROR) && (err == CHIP_NO_ERROR))
582 : {
583 : // TODO(gmarcosb): These errors are just swallowed; caller unaware their message is just dropped?
584 : // Likely just falls through to a timeout instead of fail-fast
585 5 : err = endPoint->Send(std::move(buffer));
586 : }
587 5 : }
588 5 : return Loop::Continue;
589 : });
590 :
591 : // Set the TCPKeepalive configurations on the established connection
592 40 : TEMPORARY_RETURN_IGNORED endPoint->EnableKeepAlive(activeConnection->mTCPKeepAliveIntervalSecs,
593 20 : activeConnection->mTCPMaxNumKeepAliveProbes);
594 :
595 20 : ChipLogProgress(Inet, "Connection established successfully with %s.", addrStr);
596 :
597 : // Let higher layer/delegate know that connection is successfully
598 : // established
599 20 : tcp->HandleConnectionAttemptComplete(activeConnection, CHIP_NO_ERROR);
600 20 : }
601 :
602 9 : void TCPBase::HandleTCPEndPointConnectionClosed(const Inet::TCPEndPointHandle & endPoint, CHIP_ERROR err)
603 : {
604 9 : TCPBase * tcp = reinterpret_cast<TCPBase *>(endPoint->mAppState);
605 9 : ActiveTCPConnectionHandle activeConnection = tcp->FindInUseConnection(*endPoint);
606 :
607 9 : if (activeConnection.IsNull())
608 : {
609 0 : return;
610 : }
611 :
612 18 : if (err == CHIP_NO_ERROR && activeConnection->IsConnected())
613 : {
614 2 : err = CHIP_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY;
615 : }
616 :
617 9 : tcp->CloseConnectionInternal(*activeConnection, err, SuppressCallback::No);
618 9 : }
619 :
620 : // Handler for incoming connection requests from peer nodes
621 18 : void TCPBase::HandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint, const Inet::TCPEndPointHandle & endPoint,
622 : const Inet::IPAddress & peerAddress, uint16_t peerPort)
623 : {
624 18 : TCPBase * tcp = reinterpret_cast<TCPBase *>(listenEndPoint->mAppState);
625 18 : ReturnAndLogOnFailure(tcp->DoHandleIncomingConnection(listenEndPoint, endPoint, peerAddress, peerPort), Inet,
626 : "Failure accepting incoming connection");
627 : }
628 :
629 18 : CHIP_ERROR TCPBase::DoHandleIncomingConnection(const Inet::TCPEndPointHandle & listenEndPoint,
630 : const Inet::TCPEndPointHandle & endPoint, const Inet::IPAddress & peerAddress,
631 : uint16_t peerPort)
632 : {
633 : #if INET_CONFIG_TEST
634 18 : if (sForceFailureInDoHandleIncomingConnection)
635 : {
636 2 : return CHIP_ERROR_INTERNAL;
637 : }
638 : #endif
639 :
640 : // GetPeerAddress may fail if the client has already closed the connection, just drop it.
641 16 : PeerAddress addr;
642 16 : ReturnErrorOnFailure(GetPeerAddress(*endPoint, addr));
643 :
644 16 : ActiveTCPConnectionState * activeConnection = AllocateConnection(endPoint, addr);
645 16 : VerifyOrReturnError(activeConnection != nullptr, CHIP_ERROR_TOO_MANY_CONNECTIONS);
646 :
647 16 : auto connectionCleanup = ScopeExit([&]() { activeConnection->Free(); });
648 :
649 16 : endPoint->mAppState = this;
650 16 : endPoint->OnDataReceived = HandleTCPEndPointDataReceived;
651 16 : endPoint->OnDataSent = nullptr;
652 16 : endPoint->OnConnectionClosed = HandleTCPEndPointConnectionClosed;
653 :
654 : // By default, disable TCP Nagle buffering by setting TCP_NODELAY socket option to true
655 : // If it fails, we can still use the connection
656 16 : RETURN_SAFELY_IGNORED endPoint->EnableNoDelay();
657 :
658 16 : mUsedEndPointCount++;
659 16 : activeConnection->mConnectionState = TCPState::kConnected;
660 :
661 : // Set the TCPKeepalive configurations on the received connection
662 : // If it fails, we can still use the connection until it dies
663 16 : RETURN_SAFELY_IGNORED endPoint->EnableKeepAlive(activeConnection->mTCPKeepAliveIntervalSecs,
664 16 : activeConnection->mTCPMaxNumKeepAliveProbes);
665 :
666 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
667 16 : peerAddress.ToString(addrStr);
668 16 : ChipLogProgress(Inet, "Incoming connection established with peer at %s.", addrStr);
669 :
670 : // Call the upper layer handler for incoming connection received.
671 16 : HandleConnectionReceived(*activeConnection);
672 :
673 16 : connectionCleanup.release();
674 :
675 16 : return CHIP_NO_ERROR;
676 16 : }
677 :
678 2 : void TCPBase::HandleAcceptError(const Inet::TCPEndPointHandle & listeningEndpoint, CHIP_ERROR err)
679 : {
680 2 : ChipLogError(Inet, "Accept error: %" CHIP_ERROR_FORMAT, err.Format());
681 2 : }
682 :
683 37 : CHIP_ERROR TCPBase::TCPConnect(const PeerAddress & address, Transport::AppTCPConnectionCallbackCtxt * appState,
684 : ActiveTCPConnectionHandle & outPeerConnState)
685 : {
686 37 : VerifyOrReturnError(mState == TCPState::kInitialized, CHIP_ERROR_INCORRECT_STATE);
687 :
688 : // Verify that PeerAddress AddressType is TCP
689 37 : VerifyOrReturnError(address.GetTransportType() == Transport::Type::kTcp, CHIP_ERROR_INVALID_ARGUMENT);
690 :
691 37 : VerifyOrReturnError(mUsedEndPointCount < mActiveConnectionsSize, CHIP_ERROR_NO_MEMORY);
692 :
693 : char addrStr[Transport::PeerAddress::kMaxToStringSize];
694 37 : address.ToString(addrStr);
695 37 : ChipLogProgress(Inet, "Connecting to peer %s.", addrStr);
696 :
697 37 : ReturnErrorOnFailure(StartConnect(address, appState, outPeerConnState));
698 :
699 25 : return CHIP_NO_ERROR;
700 : }
701 :
702 36 : void TCPBase::TCPDisconnect(ActiveTCPConnectionState & conn, bool shouldAbort)
703 : {
704 : // If there are still active references, we need to notify them of connection closure
705 36 : SuppressCallback suppressCallback = (conn.GetReferenceCount() > 0) ? SuppressCallback::No : SuppressCallback::Yes;
706 :
707 : // This call should be able to disconnect the connection either when it is
708 : // already established, or when it is being set up.
709 36 : if ((conn.IsConnected() && shouldAbort) || conn.IsConnecting())
710 : {
711 36 : CloseConnectionInternal(conn, CHIP_ERROR_CONNECTION_ABORTED, suppressCallback);
712 : }
713 :
714 36 : if (conn.IsConnected() && !shouldAbort)
715 : {
716 0 : CloseConnectionInternal(conn, CHIP_NO_ERROR, suppressCallback);
717 : }
718 36 : }
719 :
720 42 : bool TCPBase::HasActiveConnections() const
721 : {
722 178 : for (size_t i = 0; i < mActiveConnectionsSize; i++)
723 : {
724 144 : if (mActiveConnections[i].IsConnected())
725 : {
726 8 : return true;
727 : }
728 : }
729 :
730 34 : return false;
731 : }
732 :
733 37 : void TCPBase::InitEndpoint(const Inet::TCPEndPointHandle & endpoint)
734 : {
735 37 : endpoint->mAppState = reinterpret_cast<void *>(this);
736 37 : endpoint->OnConnectComplete = HandleTCPEndPointConnectComplete;
737 37 : endpoint->SetConnectTimeout(mConnectTimeout);
738 37 : }
739 :
740 : #if INET_CONFIG_TEST
741 : bool TCPBase::sForceFailureInDoHandleIncomingConnection = false;
742 : #endif
743 :
744 : } // namespace Transport
745 : } // namespace chip
|