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