Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @file
20 : * This file implements the ExchangeManager class.
21 : *
22 : */
23 :
24 : #include <cstring>
25 : #include <inttypes.h>
26 : #include <stddef.h>
27 :
28 : #include <crypto/RandUtils.h>
29 : #include <lib/core/CHIPCore.h>
30 : #include <lib/core/CHIPEncoding.h>
31 : #include <lib/support/CHIPFaultInjection.h>
32 : #include <lib/support/CodeUtils.h>
33 : #include <lib/support/logging/CHIPLogging.h>
34 : #include <messaging/ExchangeContext.h>
35 : #include <messaging/ExchangeMgr.h>
36 : #include <protocols/Protocols.h>
37 :
38 : using namespace chip::Encoding;
39 : using namespace chip::Inet;
40 : using namespace chip::System;
41 :
42 : namespace chip {
43 : namespace Messaging {
44 :
45 : /**
46 : * Constructor for the ExchangeManager class.
47 : * It sets the state to kState_NotInitialized.
48 : *
49 : * @note
50 : * The class must be initialized via ExchangeManager::Init()
51 : * prior to use.
52 : *
53 : */
54 6003 : ExchangeManager::ExchangeManager() : mReliableMessageMgr(mContextPool)
55 : {
56 667 : mState = State::kState_NotInitialized;
57 667 : }
58 :
59 457 : CHIP_ERROR ExchangeManager::Init(SessionManager * sessionManager)
60 : {
61 457 : CHIP_ERROR err = CHIP_NO_ERROR;
62 :
63 457 : VerifyOrReturnError(mState == State::kState_NotInitialized, err = CHIP_ERROR_INCORRECT_STATE);
64 :
65 457 : mSessionManager = sessionManager;
66 :
67 457 : mNextExchangeId = chip::Crypto::GetRandU16();
68 457 : mNextKeyId = 0;
69 :
70 4113 : for (auto & handler : UMHandlerPool)
71 : {
72 : // Mark all handlers as unallocated. This handles both initial
73 : // initialization and the case when the consumer shuts us down and
74 : // then re-initializes without removing registered handlers.
75 3656 : handler.Reset();
76 : }
77 :
78 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
79 : // Start from a clean slate: a stale observer must not survive a Shutdown()/re-Init() cycle.
80 457 : mTestOnlyReceivedObserver = nullptr;
81 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
82 :
83 457 : sessionManager->SetMessageDelegate(this);
84 :
85 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
86 457 : sessionManager->SetConnectionDelegate(this);
87 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
88 457 : mReliableMessageMgr.Init(sessionManager->SystemLayer());
89 :
90 457 : mState = State::kState_Initialized;
91 :
92 457 : return err;
93 : }
94 :
95 457 : void ExchangeManager::Shutdown()
96 : {
97 457 : VerifyOrReturn(mState != State::kState_NotInitialized);
98 :
99 457 : mReliableMessageMgr.Shutdown();
100 :
101 457 : if (mSessionManager != nullptr)
102 : {
103 457 : mSessionManager->SetMessageDelegate(nullptr);
104 457 : mSessionManager = nullptr;
105 : }
106 :
107 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
108 : // Drop the test-only observer so inbound traffic after a re-Init() cannot dispatch to a now-stale observer.
109 457 : mTestOnlyReceivedObserver = nullptr;
110 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
111 :
112 457 : mState = State::kState_NotInitialized;
113 : }
114 :
115 2434 : ExchangeContext * ExchangeManager::NewContext(const SessionHandle & session, ExchangeDelegate * delegate, bool isInitiator)
116 : {
117 2434 : if (!session->IsActiveSession())
118 : {
119 : #if CHIP_ERROR_LOGGING
120 6 : const ScopedNodeId & peer = session->GetPeer();
121 6 : ChipLogError(ExchangeManager, "NewContext failed: session %u to " ChipLogFormatScopedNodeId " is inactive",
122 : session->SessionIdForLogging(), ChipLogValueScopedNodeId(peer));
123 : #endif // CHIP_ERROR_LOGGING
124 :
125 : // Disallow creating exchange on an inactive session
126 6 : return nullptr;
127 : }
128 2428 : return mContextPool.CreateObject(this, mNextExchangeId++, session, isInitiator, delegate);
129 : }
130 :
131 527 : CHIP_ERROR ExchangeManager::RegisterUnsolicitedMessageHandlerForProtocol(Protocols::Id protocolId,
132 : UnsolicitedMessageHandler * handler)
133 : {
134 527 : return RegisterUMH(protocolId, kAnyMessageType, handler);
135 : }
136 :
137 517 : CHIP_ERROR ExchangeManager::RegisterUnsolicitedMessageHandlerForType(Protocols::Id protocolId, uint8_t msgType,
138 : UnsolicitedMessageHandler * handler)
139 : {
140 517 : return RegisterUMH(protocolId, static_cast<int16_t>(msgType), handler);
141 : }
142 :
143 389 : CHIP_ERROR ExchangeManager::UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::Id protocolId)
144 : {
145 389 : return UnregisterUMH(protocolId, kAnyMessageType);
146 : }
147 :
148 501 : CHIP_ERROR ExchangeManager::UnregisterUnsolicitedMessageHandlerForType(Protocols::Id protocolId, uint8_t msgType,
149 : Messaging::UnsolicitedMessageHandler ** outHandler)
150 : {
151 501 : return UnregisterUMH(protocolId, static_cast<int16_t>(msgType), outHandler);
152 : }
153 :
154 1044 : CHIP_ERROR ExchangeManager::RegisterUMH(Protocols::Id protocolId, int16_t msgType, UnsolicitedMessageHandler * handler)
155 : {
156 1044 : UnsolicitedMessageHandlerSlot * selected = nullptr;
157 :
158 8410 : for (auto & umh : UMHandlerPool)
159 : {
160 7507 : if (!umh.IsInUse())
161 : {
162 6746 : if (selected == nullptr)
163 903 : selected = &umh;
164 : }
165 761 : else if (umh.Matches(protocolId, msgType))
166 : {
167 141 : umh.Handler = handler;
168 141 : return CHIP_NO_ERROR;
169 : }
170 : }
171 :
172 903 : if (selected == nullptr)
173 0 : return CHIP_ERROR_TOO_MANY_UNSOLICITED_MESSAGE_HANDLERS;
174 :
175 903 : selected->Handler = handler;
176 903 : selected->ProtocolId = protocolId;
177 903 : selected->MessageType = msgType;
178 :
179 903 : SYSTEM_STATS_INCREMENT(chip::System::Stats::kExchangeMgr_NumUMHandlers);
180 :
181 903 : return CHIP_NO_ERROR;
182 : }
183 :
184 890 : CHIP_ERROR ExchangeManager::UnregisterUMH(Protocols::Id protocolId, int16_t msgType,
185 : Messaging::UnsolicitedMessageHandler ** outHandler)
186 : {
187 1402 : for (auto & umh : UMHandlerPool)
188 : {
189 1393 : if (umh.IsInUse() && umh.Matches(protocolId, msgType))
190 : {
191 : // Store the handler before unregistering.
192 881 : if (outHandler != nullptr)
193 : {
194 17 : *outHandler = umh.Handler;
195 : }
196 881 : umh.Reset();
197 881 : SYSTEM_STATS_DECREMENT(chip::System::Stats::kExchangeMgr_NumUMHandlers);
198 881 : return CHIP_NO_ERROR;
199 : }
200 : }
201 :
202 9 : if (outHandler != nullptr)
203 : {
204 1 : *outHandler = nullptr;
205 : }
206 :
207 9 : return CHIP_ERROR_NO_UNSOLICITED_MESSAGE_HANDLER;
208 : }
209 :
210 15069 : void ExchangeManager::OnMessageReceived(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
211 : const SessionHandle & session, DuplicateMessage isDuplicate,
212 : System::PacketBufferHandle && msgBuf)
213 : {
214 15069 : UnsolicitedMessageHandlerSlot * matchingUMH = nullptr;
215 :
216 : #if CHIP_PROGRESS_LOGGING
217 15069 : auto * protocolName = Protocols::GetProtocolName(payloadHeader.GetProtocolID());
218 15069 : auto * msgTypeName = Protocols::GetMessageTypeName(payloadHeader.GetProtocolID(), payloadHeader.GetMessageType());
219 :
220 15069 : auto destination = kUndefinedNodeId;
221 15069 : if (packetHeader.GetDestinationNodeId().HasValue())
222 : {
223 51 : destination = packetHeader.GetDestinationNodeId().Value();
224 : }
225 15018 : else if (session->IsSecureSession())
226 : {
227 14945 : destination = session->AsSecureSession()->GetLocalNodeId();
228 : }
229 :
230 : //
231 : // 32-bit value maximum = 10 chars + text preamble (6) + trailer (1) + null (1) + 2 buffer = 20
232 : //
233 : char ackBuf[20];
234 15069 : ackBuf[0] = '\0';
235 15069 : if (payloadHeader.GetAckMessageCounter().HasValue())
236 : {
237 12682 : snprintf(ackBuf, sizeof(ackBuf), " (Ack:" ChipLogFormatMessageCounter ")", payloadHeader.GetAckMessageCounter().Value());
238 : }
239 :
240 15069 : CompressedFabricId compressedFabricId = 0;
241 15069 : if (session->IsSecureSession() && mSessionManager->GetFabricTable() != nullptr)
242 : {
243 14945 : auto fabricInfo = mSessionManager->GetFabricTable()->FindFabricWithIndex(session->AsSecureSession()->GetFabricIndex());
244 14945 : if (fabricInfo)
245 : {
246 14898 : compressedFabricId = fabricInfo->GetCompressedFabricId();
247 : }
248 : }
249 :
250 : // Work around pigweed not allowing more than 14 format args in a log
251 : // message when using tokenized logs.
252 : char typeStr[4 + 1 + 2 + 1];
253 15069 : snprintf(typeStr, sizeof(typeStr), "%04X:%02X", payloadHeader.GetProtocolID().GetProtocolId(), payloadHeader.GetMessageType());
254 :
255 : // More work around pigweed not allowing more than 14 format args in a log
256 : // message when using tokenized logs.
257 : // text(5) + fabricIndex (uint16_t, at most 5 chars) + text (1) + source (16) + text (2) + compressed fabric id (4) + text (5) +
258 : // destination + null-terminator
259 : char sourceDestinationStr[5 + 5 + 1 + 16 + 2 + 4 + 5 + 16 + 1];
260 60276 : snprintf(sourceDestinationStr, sizeof(sourceDestinationStr), "from %u:" ChipLogFormatX64 " [%04X] to " ChipLogFormatX64,
261 15069 : session->GetFabricIndex(), ChipLogValueX64(session->GetPeer().GetNodeId()), static_cast<uint16_t>(compressedFabricId),
262 15069 : ChipLogValueX64(destination));
263 :
264 : //
265 : // Legend that can be used to decode this log line can be found in README.md
266 : //
267 15069 : ChipLogProgress(
268 : ExchangeManager,
269 : ">>> [E:" ChipLogFormatExchangeId " S:%u M:" ChipLogFormatMessageCounter "%s] (%s) Msg RX %s --- Type %s (%s:%s) (B:%u)",
270 : ChipLogValueExchangeIdFromReceivedHeader(payloadHeader), session->SessionIdForLogging(), packetHeader.GetMessageCounter(),
271 : ackBuf, Transport::GetSessionTypeString(session), sourceDestinationStr, typeStr, protocolName, msgTypeName,
272 : static_cast<unsigned>(msgBuf->TotalLength() + packetHeader.EncodeSizeBytes() + packetHeader.MICTagLength() +
273 : payloadHeader.EncodeSizeBytes()));
274 : #endif
275 :
276 15069 : MessageFlags msgFlags;
277 15069 : if (isDuplicate == DuplicateMessage::Yes)
278 : {
279 5 : msgFlags.Set(MessageFlagValues::kDuplicateMessage);
280 : }
281 :
282 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
283 15069 : if (mTestOnlyReceivedObserver != nullptr)
284 : {
285 0 : mTestOnlyReceivedObserver->OnMessageReceived(packetHeader, payloadHeader, msgBuf);
286 : }
287 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
288 :
289 : // Skip retrieval of exchange for group message since no exchange is stored
290 : // for group msg (optimization)
291 15069 : if (!packetHeader.IsGroupSession())
292 : {
293 : // Search for an existing exchange that the message applies to. If a match is found...
294 15066 : bool found = false;
295 15066 : mContextPool.ForEachActiveObject([&](auto * ec) {
296 52502 : if (ec->MatchExchange(session, packetHeader, payloadHeader))
297 : {
298 12650 : ChipLogDetail(ExchangeManager, "Found matching exchange: " ChipLogFormatExchange ", Delegate: %p",
299 : ChipLogValueExchange(ec), ec->GetDelegate());
300 :
301 : // Matched ExchangeContext; send to message handler.
302 12650 : TEMPORARY_RETURN_IGNORED ec->HandleMessage(packetHeader.GetMessageCounter(), payloadHeader, msgFlags,
303 12650 : std::move(msgBuf));
304 12650 : found = true;
305 12650 : return Loop::Break;
306 : }
307 39852 : return Loop::Continue;
308 : });
309 :
310 15066 : if (found)
311 : {
312 12650 : return;
313 : }
314 : }
315 : else
316 : {
317 3 : if (packetHeader.GetDestinationGroupId().HasValue())
318 : {
319 3 : ChipLogProgress(ExchangeManager, "Received Groupcast Message with GroupId 0x%04X (%d)",
320 : packetHeader.GetDestinationGroupId().Value(), packetHeader.GetDestinationGroupId().Value());
321 : }
322 : else
323 : {
324 0 : ChipLogProgress(ExchangeManager, "Received Groupcast Message without GroupId");
325 : }
326 : }
327 :
328 : // Do not handle messages that don't match an existing exchange on an
329 : // inactive session, since we should not be creating new exchanges there.
330 2419 : if (!session->IsActiveSession())
331 : {
332 0 : ChipLogProgress(ExchangeManager, "Dropping message on inactive session that does not match an existing exchange");
333 0 : return;
334 : }
335 :
336 : // If it's not a duplicate message, search for an unsolicited message handler if it is marked as being sent by an initiator.
337 : // Since we didn't find an existing exchange that matches the message, it must be an unsolicited message. However all
338 : // unsolicited messages must be marked as being from an initiator.
339 2419 : if (!msgFlags.Has(MessageFlagValues::kDuplicateMessage) && payloadHeader.IsInitiator())
340 : {
341 : // Search for an unsolicited message handler that can handle the message. Prefer handlers that can explicitly
342 : // handle the message type over handlers that handle all messages for a profile.
343 2352 : matchingUMH = nullptr;
344 :
345 20608 : for (auto & umh : UMHandlerPool)
346 : {
347 18336 : if (umh.IsInUse() && payloadHeader.HasProtocol(umh.ProtocolId))
348 : {
349 2369 : if (umh.MessageType == payloadHeader.GetMessageType())
350 : {
351 80 : matchingUMH = &umh;
352 80 : break;
353 : }
354 :
355 2289 : if (umh.MessageType == kAnyMessageType)
356 2257 : matchingUMH = &umh;
357 : }
358 : }
359 : }
360 : // Discard the message if it isn't marked as being sent by an initiator and the message does not need to send
361 : // an ack to the peer.
362 67 : else if (!payloadHeader.NeedsAck())
363 : {
364 : // We can easily get standalone acks here: any time we fail to get a
365 : // timely ack for the last message in an exchange and retransmit it,
366 : // then get acks for both the message and the retransmit, the second ack
367 : // will end up in this block. That's not really an error condition, so
368 : // there is no need to log an error in that case.
369 52 : if (!payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::StandaloneAck))
370 : {
371 : // Using same error message for all errors to reduce code size.
372 2 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT,
373 : CHIP_ERROR_UNSOLICITED_MSG_NO_ORIGINATOR.Format());
374 : }
375 52 : return;
376 : }
377 :
378 : // If we found a handler, create an exchange to handle the message.
379 2367 : if (matchingUMH != nullptr)
380 : {
381 2337 : ExchangeDelegate * delegate = nullptr;
382 2337 : UnsolicitedMessageHandler * handler = matchingUMH->Handler;
383 :
384 : // Fetch delegate from the handler
385 2337 : CHIP_ERROR err = handler->OnUnsolicitedMessageReceived(payloadHeader, session, delegate);
386 4674 : if (err != CHIP_NO_ERROR)
387 : {
388 : // Using same error message for all errors to reduce code size.
389 0 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, err.Format());
390 0 : SendStandaloneAckIfNeeded(packetHeader, payloadHeader, session, msgFlags, std::move(msgBuf));
391 0 : return;
392 : }
393 :
394 2337 : ExchangeContext * ec = mContextPool.CreateObject(this, payloadHeader.GetExchangeID(), session, false, delegate);
395 :
396 2337 : if (ec == nullptr)
397 : {
398 0 : if (delegate != nullptr)
399 : {
400 0 : handler->OnExchangeCreationFailed(delegate);
401 : }
402 :
403 : // Using same error message for all errors to reduce code size.
404 0 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, CHIP_ERROR_NO_MEMORY.Format());
405 : // No resource for creating new exchange, SendStandaloneAckIfNeeded probably also fails, so do not try it here
406 0 : return;
407 : }
408 :
409 2337 : ChipLogDetail(ExchangeManager, "Handling via exchange: " ChipLogFormatExchange ", Delegate: %p", ChipLogValueExchange(ec),
410 : ec->GetDelegate());
411 :
412 2337 : if (ec->IsEncryptionRequired() != packetHeader.IsEncrypted())
413 : {
414 2 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT,
415 : CHIP_ERROR_INVALID_MESSAGE_TYPE.Format());
416 2 : if (delegate != nullptr)
417 : {
418 : // The OnExchangeCreationFailed contract allows the handler to deallocate the delegate.
419 : // Clear it from the exchange context first to prevent use-after-free in ec->Close().
420 2 : ec->SetDelegate(nullptr);
421 2 : handler->OnExchangeCreationFailed(delegate);
422 : }
423 2 : ec->Close();
424 2 : SendStandaloneAckIfNeeded(packetHeader, payloadHeader, session, msgFlags, std::move(msgBuf));
425 2 : return;
426 : }
427 :
428 2335 : err = ec->HandleMessage(packetHeader.GetMessageCounter(), payloadHeader, msgFlags, std::move(msgBuf));
429 4670 : if (err != CHIP_NO_ERROR)
430 : {
431 : // Using same error message for all errors to reduce code size.
432 3 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, err.Format());
433 : }
434 2335 : return;
435 : }
436 :
437 30 : SendStandaloneAckIfNeeded(packetHeader, payloadHeader, session, msgFlags, std::move(msgBuf));
438 : }
439 :
440 32 : void ExchangeManager::SendStandaloneAckIfNeeded(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
441 : const SessionHandle & session, MessageFlags msgFlags,
442 : System::PacketBufferHandle && msgBuf)
443 : {
444 :
445 : // If using the MRP protocol and we need to send a StandaloneAck, create an EphemeralExchange to send
446 : // the StandaloneAck.
447 32 : if (!session->AllowsMRP() || !payloadHeader.NeedsAck())
448 9 : return;
449 :
450 : // If rcvd msg is from initiator then this exchange is created as not Initiator.
451 : // If rcvd msg is not from initiator then this exchange is created as Initiator.
452 : // Create a EphemeralExchange to generate a StandaloneAck
453 23 : ExchangeContext * ec = mContextPool.CreateObject(this, payloadHeader.GetExchangeID(), session, !payloadHeader.IsInitiator(),
454 23 : nullptr, true /* IsEphemeralExchange */);
455 :
456 23 : if (ec == nullptr)
457 : {
458 : // Using same error message for all errors to reduce code size.
459 0 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, CHIP_ERROR_NO_MEMORY.Format());
460 0 : return;
461 : }
462 :
463 23 : ChipLogDetail(ExchangeManager, "Generating StandaloneAck via exchange: " ChipLogFormatExchange, ChipLogValueExchange(ec));
464 :
465 : // No need to verify packet encryption type, the EphemeralExchange can handle both secure and insecure messages.
466 :
467 23 : CHIP_ERROR err = ec->HandleMessage(packetHeader.GetMessageCounter(), payloadHeader, msgFlags, std::move(msgBuf));
468 46 : if (err != CHIP_NO_ERROR)
469 : {
470 : // Using same error message for all errors to reduce code size.
471 0 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, err.Format());
472 : }
473 :
474 : // The exchange should be closed inside HandleMessage function. So don't bother close it here.
475 : }
476 :
477 458 : void ExchangeManager::CloseAllContextsForDelegate(const ExchangeDelegate * delegate)
478 : {
479 458 : mContextPool.ForEachActiveObject([&](auto * ec) {
480 36 : if (ec->GetDelegate() == delegate)
481 : {
482 : // Make sure to null out the delegate before closing the context, so
483 : // we don't notify the delegate that the context is closing. We
484 : // have to do this, because the delegate might be partially
485 : // destroyed by this point.
486 1 : ec->SetDelegate(nullptr);
487 1 : ec->Close();
488 : }
489 36 : return Loop::Continue;
490 : });
491 458 : }
492 :
493 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
494 0 : void ExchangeManager::OnTCPConnectionClosed(const Transport::ActiveTCPConnectionState & conn, const SessionHandle & session,
495 : CHIP_ERROR conErr)
496 : {
497 0 : mContextPool.ForEachActiveObject([&](auto * ec) {
498 0 : if (ec->HasSessionHandle() && ec->GetSessionHandle() == session)
499 : {
500 0 : ec->OnSessionConnectionClosed(conn, conErr);
501 : }
502 0 : return Loop::Continue;
503 : });
504 0 : }
505 :
506 0 : bool ExchangeManager::OnTCPConnectionAttemptComplete(Transport::ActiveTCPConnectionHandle & conn, CHIP_ERROR conErr)
507 : {
508 0 : bool foundHandler = false;
509 0 : mContextPool.ForEachActiveObject([&](auto * ec) {
510 0 : if (ec->HasSessionHandle())
511 : {
512 0 : ec->OnConnectionAttemptComplete(conn, conErr);
513 0 : foundHandler = true;
514 : }
515 0 : return Loop::Continue;
516 : });
517 :
518 0 : return foundHandler;
519 : }
520 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
521 :
522 : } // namespace Messaging
523 : } // namespace chip
|