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 5031 : ExchangeManager::ExchangeManager() : mReliableMessageMgr(mContextPool)
55 : {
56 559 : mState = State::kState_NotInitialized;
57 559 : }
58 :
59 427 : CHIP_ERROR ExchangeManager::Init(SessionManager * sessionManager)
60 : {
61 427 : CHIP_ERROR err = CHIP_NO_ERROR;
62 :
63 427 : VerifyOrReturnError(mState == State::kState_NotInitialized, err = CHIP_ERROR_INCORRECT_STATE);
64 :
65 427 : mSessionManager = sessionManager;
66 :
67 427 : mNextExchangeId = chip::Crypto::GetRandU16();
68 427 : mNextKeyId = 0;
69 :
70 3843 : 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 3416 : handler.Reset();
76 : }
77 :
78 427 : sessionManager->SetMessageDelegate(this);
79 :
80 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
81 427 : sessionManager->SetConnectionDelegate(this);
82 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
83 427 : mReliableMessageMgr.Init(sessionManager->SystemLayer());
84 :
85 427 : mState = State::kState_Initialized;
86 :
87 427 : return err;
88 : }
89 :
90 427 : void ExchangeManager::Shutdown()
91 : {
92 427 : VerifyOrReturn(mState != State::kState_NotInitialized);
93 :
94 427 : mReliableMessageMgr.Shutdown();
95 :
96 427 : if (mSessionManager != nullptr)
97 : {
98 427 : mSessionManager->SetMessageDelegate(nullptr);
99 427 : mSessionManager = nullptr;
100 : }
101 :
102 427 : mState = State::kState_NotInitialized;
103 : }
104 :
105 2396 : ExchangeContext * ExchangeManager::NewContext(const SessionHandle & session, ExchangeDelegate * delegate, bool isInitiator)
106 : {
107 2396 : if (!session->IsActiveSession())
108 : {
109 : #if CHIP_ERROR_LOGGING
110 6 : const ScopedNodeId & peer = session->GetPeer();
111 6 : ChipLogError(ExchangeManager, "NewContext failed: session %u to " ChipLogFormatScopedNodeId " is inactive",
112 : session->SessionIdForLogging(), ChipLogValueScopedNodeId(peer));
113 : #endif // CHIP_ERROR_LOGGING
114 :
115 : // Disallow creating exchange on an inactive session
116 6 : return nullptr;
117 : }
118 2390 : return mContextPool.CreateObject(this, mNextExchangeId++, session, isInitiator, delegate);
119 : }
120 :
121 489 : CHIP_ERROR ExchangeManager::RegisterUnsolicitedMessageHandlerForProtocol(Protocols::Id protocolId,
122 : UnsolicitedMessageHandler * handler)
123 : {
124 489 : return RegisterUMH(protocolId, kAnyMessageType, handler);
125 : }
126 :
127 480 : CHIP_ERROR ExchangeManager::RegisterUnsolicitedMessageHandlerForType(Protocols::Id protocolId, uint8_t msgType,
128 : UnsolicitedMessageHandler * handler)
129 : {
130 480 : return RegisterUMH(protocolId, static_cast<int16_t>(msgType), handler);
131 : }
132 :
133 362 : CHIP_ERROR ExchangeManager::UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::Id protocolId)
134 : {
135 362 : return UnregisterUMH(protocolId, kAnyMessageType);
136 : }
137 :
138 466 : CHIP_ERROR ExchangeManager::UnregisterUnsolicitedMessageHandlerForType(Protocols::Id protocolId, uint8_t msgType,
139 : Messaging::UnsolicitedMessageHandler ** outHandler)
140 : {
141 466 : return UnregisterUMH(protocolId, static_cast<int16_t>(msgType), outHandler);
142 : }
143 :
144 969 : CHIP_ERROR ExchangeManager::RegisterUMH(Protocols::Id protocolId, int16_t msgType, UnsolicitedMessageHandler * handler)
145 : {
146 969 : UnsolicitedMessageHandlerSlot * selected = nullptr;
147 :
148 7806 : for (auto & umh : UMHandlerPool)
149 : {
150 6968 : if (!umh.IsInUse())
151 : {
152 6270 : if (selected == nullptr)
153 838 : selected = &umh;
154 : }
155 698 : else if (umh.Matches(protocolId, msgType))
156 : {
157 131 : umh.Handler = handler;
158 131 : return CHIP_NO_ERROR;
159 : }
160 : }
161 :
162 838 : if (selected == nullptr)
163 0 : return CHIP_ERROR_TOO_MANY_UNSOLICITED_MESSAGE_HANDLERS;
164 :
165 838 : selected->Handler = handler;
166 838 : selected->ProtocolId = protocolId;
167 838 : selected->MessageType = msgType;
168 :
169 838 : SYSTEM_STATS_INCREMENT(chip::System::Stats::kExchangeMgr_NumUMHandlers);
170 :
171 838 : return CHIP_NO_ERROR;
172 : }
173 :
174 828 : CHIP_ERROR ExchangeManager::UnregisterUMH(Protocols::Id protocolId, int16_t msgType,
175 : Messaging::UnsolicitedMessageHandler ** outHandler)
176 : {
177 1305 : for (auto & umh : UMHandlerPool)
178 : {
179 1296 : if (umh.IsInUse() && umh.Matches(protocolId, msgType))
180 : {
181 : // Store the handler before unregistering.
182 819 : if (outHandler != nullptr)
183 : {
184 16 : *outHandler = umh.Handler;
185 : }
186 819 : umh.Reset();
187 819 : SYSTEM_STATS_DECREMENT(chip::System::Stats::kExchangeMgr_NumUMHandlers);
188 819 : return CHIP_NO_ERROR;
189 : }
190 : }
191 :
192 9 : if (outHandler != nullptr)
193 : {
194 1 : *outHandler = nullptr;
195 : }
196 :
197 9 : return CHIP_ERROR_NO_UNSOLICITED_MESSAGE_HANDLER;
198 : }
199 :
200 14995 : void ExchangeManager::OnMessageReceived(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
201 : const SessionHandle & session, DuplicateMessage isDuplicate,
202 : System::PacketBufferHandle && msgBuf)
203 : {
204 14995 : UnsolicitedMessageHandlerSlot * matchingUMH = nullptr;
205 :
206 : #if CHIP_PROGRESS_LOGGING
207 14995 : auto * protocolName = Protocols::GetProtocolName(payloadHeader.GetProtocolID());
208 14995 : auto * msgTypeName = Protocols::GetMessageTypeName(payloadHeader.GetProtocolID(), payloadHeader.GetMessageType());
209 :
210 14995 : auto destination = kUndefinedNodeId;
211 14995 : if (packetHeader.GetDestinationNodeId().HasValue())
212 : {
213 51 : destination = packetHeader.GetDestinationNodeId().Value();
214 : }
215 14944 : else if (session->IsSecureSession())
216 : {
217 14874 : destination = session->AsSecureSession()->GetLocalNodeId();
218 : }
219 :
220 : //
221 : // 32-bit value maximum = 10 chars + text preamble (6) + trailer (1) + null (1) + 2 buffer = 20
222 : //
223 : char ackBuf[20];
224 14995 : ackBuf[0] = '\0';
225 14995 : if (payloadHeader.GetAckMessageCounter().HasValue())
226 : {
227 12638 : snprintf(ackBuf, sizeof(ackBuf), " (Ack:" ChipLogFormatMessageCounter ")", payloadHeader.GetAckMessageCounter().Value());
228 : }
229 :
230 14995 : CompressedFabricId compressedFabricId = 0;
231 14995 : if (session->IsSecureSession() && mSessionManager->GetFabricTable() != nullptr)
232 : {
233 14874 : auto fabricInfo = mSessionManager->GetFabricTable()->FindFabricWithIndex(session->AsSecureSession()->GetFabricIndex());
234 14874 : if (fabricInfo)
235 : {
236 14827 : compressedFabricId = fabricInfo->GetCompressedFabricId();
237 : }
238 : }
239 :
240 : // Work around pigweed not allowing more than 14 format args in a log
241 : // message when using tokenized logs.
242 : char typeStr[4 + 1 + 2 + 1];
243 14995 : snprintf(typeStr, sizeof(typeStr), "%04X:%02X", payloadHeader.GetProtocolID().GetProtocolId(), payloadHeader.GetMessageType());
244 :
245 : // More work around pigweed not allowing more than 14 format args in a log
246 : // message when using tokenized logs.
247 : // text(5) + fabricIndex (uint16_t, at most 5 chars) + text (1) + source (16) + text (2) + compressed fabric id (4) + text (5) +
248 : // destination + null-terminator
249 : char sourceDestinationStr[5 + 5 + 1 + 16 + 2 + 4 + 5 + 16 + 1];
250 59980 : snprintf(sourceDestinationStr, sizeof(sourceDestinationStr), "from %u:" ChipLogFormatX64 " [%04X] to " ChipLogFormatX64,
251 14995 : session->GetFabricIndex(), ChipLogValueX64(session->GetPeer().GetNodeId()), static_cast<uint16_t>(compressedFabricId),
252 14995 : ChipLogValueX64(destination));
253 :
254 : //
255 : // Legend that can be used to decode this log line can be found in README.md
256 : //
257 14995 : ChipLogProgress(
258 : ExchangeManager,
259 : ">>> [E:" ChipLogFormatExchangeId " S:%u M:" ChipLogFormatMessageCounter "%s] (%s) Msg RX %s --- Type %s (%s:%s) (B:%u)",
260 : ChipLogValueExchangeIdFromReceivedHeader(payloadHeader), session->SessionIdForLogging(), packetHeader.GetMessageCounter(),
261 : ackBuf, Transport::GetSessionTypeString(session), sourceDestinationStr, typeStr, protocolName, msgTypeName,
262 : static_cast<unsigned>(msgBuf->TotalLength() + packetHeader.EncodeSizeBytes() + packetHeader.MICTagLength() +
263 : payloadHeader.EncodeSizeBytes()));
264 : #endif
265 :
266 14995 : MessageFlags msgFlags;
267 14995 : if (isDuplicate == DuplicateMessage::Yes)
268 : {
269 5 : msgFlags.Set(MessageFlagValues::kDuplicateMessage);
270 : }
271 :
272 : // Skip retrieval of exchange for group message since no exchange is stored
273 : // for group msg (optimization)
274 14995 : if (!packetHeader.IsGroupSession())
275 : {
276 : // Search for an existing exchange that the message applies to. If a match is found...
277 14995 : bool found = false;
278 14995 : mContextPool.ForEachActiveObject([&](auto * ec) {
279 52438 : if (ec->MatchExchange(session, packetHeader, payloadHeader))
280 : {
281 12611 : ChipLogDetail(ExchangeManager, "Found matching exchange: " ChipLogFormatExchange ", Delegate: %p",
282 : ChipLogValueExchange(ec), ec->GetDelegate());
283 :
284 : // Matched ExchangeContext; send to message handler.
285 12611 : TEMPORARY_RETURN_IGNORED ec->HandleMessage(packetHeader.GetMessageCounter(), payloadHeader, msgFlags,
286 12611 : std::move(msgBuf));
287 12611 : found = true;
288 12611 : return Loop::Break;
289 : }
290 39827 : return Loop::Continue;
291 : });
292 :
293 14995 : if (found)
294 : {
295 12611 : return;
296 : }
297 : }
298 : else
299 : {
300 0 : if (packetHeader.GetDestinationGroupId().HasValue())
301 : {
302 0 : ChipLogProgress(ExchangeManager, "Received Groupcast Message with GroupId 0x%04X (%d)",
303 : packetHeader.GetDestinationGroupId().Value(), packetHeader.GetDestinationGroupId().Value());
304 : }
305 : else
306 : {
307 0 : ChipLogProgress(ExchangeManager, "Received Groupcast Message without GroupId");
308 : }
309 : }
310 :
311 : // Do not handle messages that don't match an existing exchange on an
312 : // inactive session, since we should not be creating new exchanges there.
313 2384 : if (!session->IsActiveSession())
314 : {
315 0 : ChipLogProgress(ExchangeManager, "Dropping message on inactive session that does not match an existing exchange");
316 0 : return;
317 : }
318 :
319 : // If it's not a duplicate message, search for an unsolicited message handler if it is marked as being sent by an initiator.
320 : // Since we didn't find an existing exchange that matches the message, it must be an unsolicited message. However all
321 : // unsolicited messages must be marked as being from an initiator.
322 2384 : if (!msgFlags.Has(MessageFlagValues::kDuplicateMessage) && payloadHeader.IsInitiator())
323 : {
324 : // Search for an unsolicited message handler that can handle the message. Prefer handlers that can explicitly
325 : // handle the message type over handlers that handle all messages for a profile.
326 2322 : matchingUMH = nullptr;
327 :
328 20366 : for (auto & umh : UMHandlerPool)
329 : {
330 18120 : if (umh.IsInUse() && payloadHeader.HasProtocol(umh.ProtocolId))
331 : {
332 2339 : if (umh.MessageType == payloadHeader.GetMessageType())
333 : {
334 76 : matchingUMH = &umh;
335 76 : break;
336 : }
337 :
338 2263 : if (umh.MessageType == kAnyMessageType)
339 2231 : matchingUMH = &umh;
340 : }
341 : }
342 : }
343 : // Discard the message if it isn't marked as being sent by an initiator and the message does not need to send
344 : // an ack to the peer.
345 62 : else if (!payloadHeader.NeedsAck())
346 : {
347 : // We can easily get standalone acks here: any time we fail to get a
348 : // timely ack for the last message in an exchange and retransmit it,
349 : // then get acks for both the message and the retransmit, the second ack
350 : // will end up in this block. That's not really an error condition, so
351 : // there is no need to log an error in that case.
352 48 : if (!payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::StandaloneAck))
353 : {
354 : // Using same error message for all errors to reduce code size.
355 2 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT,
356 : CHIP_ERROR_UNSOLICITED_MSG_NO_ORIGINATOR.Format());
357 : }
358 48 : return;
359 : }
360 :
361 : // If we found a handler, create an exchange to handle the message.
362 2336 : if (matchingUMH != nullptr)
363 : {
364 2307 : ExchangeDelegate * delegate = nullptr;
365 :
366 : // Fetch delegate from the handler
367 2307 : CHIP_ERROR err = matchingUMH->Handler->OnUnsolicitedMessageReceived(payloadHeader, session, delegate);
368 4614 : if (err != CHIP_NO_ERROR)
369 : {
370 : // Using same error message for all errors to reduce code size.
371 0 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, err.Format());
372 0 : SendStandaloneAckIfNeeded(packetHeader, payloadHeader, session, msgFlags, std::move(msgBuf));
373 0 : return;
374 : }
375 :
376 2307 : ExchangeContext * ec = mContextPool.CreateObject(this, payloadHeader.GetExchangeID(), session, false, delegate);
377 :
378 2307 : if (ec == nullptr)
379 : {
380 0 : if (delegate != nullptr)
381 : {
382 0 : matchingUMH->Handler->OnExchangeCreationFailed(delegate);
383 : }
384 :
385 : // Using same error message for all errors to reduce code size.
386 0 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, CHIP_ERROR_NO_MEMORY.Format());
387 : // No resource for creating new exchange, SendStandaloneAckIfNeeded probably also fails, so do not try it here
388 0 : return;
389 : }
390 :
391 2307 : ChipLogDetail(ExchangeManager, "Handling via exchange: " ChipLogFormatExchange ", Delegate: %p", ChipLogValueExchange(ec),
392 : ec->GetDelegate());
393 :
394 2307 : if (ec->IsEncryptionRequired() != packetHeader.IsEncrypted())
395 : {
396 1 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT,
397 : CHIP_ERROR_INVALID_MESSAGE_TYPE.Format());
398 1 : ec->Close();
399 1 : SendStandaloneAckIfNeeded(packetHeader, payloadHeader, session, msgFlags, std::move(msgBuf));
400 1 : return;
401 : }
402 :
403 2306 : err = ec->HandleMessage(packetHeader.GetMessageCounter(), payloadHeader, msgFlags, std::move(msgBuf));
404 4612 : if (err != CHIP_NO_ERROR)
405 : {
406 : // Using same error message for all errors to reduce code size.
407 3 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, err.Format());
408 : }
409 2306 : return;
410 : }
411 :
412 29 : SendStandaloneAckIfNeeded(packetHeader, payloadHeader, session, msgFlags, std::move(msgBuf));
413 : }
414 :
415 30 : void ExchangeManager::SendStandaloneAckIfNeeded(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
416 : const SessionHandle & session, MessageFlags msgFlags,
417 : System::PacketBufferHandle && msgBuf)
418 : {
419 :
420 : // If using the MRP protocol and we need to send a StandaloneAck, create an EphemeralExchange to send
421 : // the StandaloneAck.
422 30 : if (!session->AllowsMRP() || !payloadHeader.NeedsAck())
423 8 : return;
424 :
425 : // If rcvd msg is from initiator then this exchange is created as not Initiator.
426 : // If rcvd msg is not from initiator then this exchange is created as Initiator.
427 : // Create a EphemeralExchange to generate a StandaloneAck
428 22 : ExchangeContext * ec = mContextPool.CreateObject(this, payloadHeader.GetExchangeID(), session, !payloadHeader.IsInitiator(),
429 22 : nullptr, true /* IsEphemeralExchange */);
430 :
431 22 : if (ec == nullptr)
432 : {
433 : // Using same error message for all errors to reduce code size.
434 0 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, CHIP_ERROR_NO_MEMORY.Format());
435 0 : return;
436 : }
437 :
438 22 : ChipLogDetail(ExchangeManager, "Generating StandaloneAck via exchange: " ChipLogFormatExchange, ChipLogValueExchange(ec));
439 :
440 : // No need to verify packet encryption type, the EphemeralExchange can handle both secure and insecure messages.
441 :
442 22 : CHIP_ERROR err = ec->HandleMessage(packetHeader.GetMessageCounter(), payloadHeader, msgFlags, std::move(msgBuf));
443 44 : if (err != CHIP_NO_ERROR)
444 : {
445 : // Using same error message for all errors to reduce code size.
446 0 : ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %" CHIP_ERROR_FORMAT, err.Format());
447 : }
448 :
449 : // The exchange should be closed inside HandleMessage function. So don't bother close it here.
450 : }
451 :
452 428 : void ExchangeManager::CloseAllContextsForDelegate(const ExchangeDelegate * delegate)
453 : {
454 428 : mContextPool.ForEachActiveObject([&](auto * ec) {
455 31 : if (ec->GetDelegate() == delegate)
456 : {
457 : // Make sure to null out the delegate before closing the context, so
458 : // we don't notify the delegate that the context is closing. We
459 : // have to do this, because the delegate might be partially
460 : // destroyed by this point.
461 1 : ec->SetDelegate(nullptr);
462 1 : ec->Close();
463 : }
464 31 : return Loop::Continue;
465 : });
466 428 : }
467 :
468 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
469 0 : void ExchangeManager::OnTCPConnectionClosed(const Transport::ActiveTCPConnectionState & conn, const SessionHandle & session,
470 : CHIP_ERROR conErr)
471 : {
472 0 : mContextPool.ForEachActiveObject([&](auto * ec) {
473 0 : if (ec->HasSessionHandle() && ec->GetSessionHandle() == session)
474 : {
475 0 : ec->OnSessionConnectionClosed(conn, conErr);
476 : }
477 0 : return Loop::Continue;
478 : });
479 0 : }
480 :
481 0 : bool ExchangeManager::OnTCPConnectionAttemptComplete(Transport::ActiveTCPConnectionHandle & conn, CHIP_ERROR conErr)
482 : {
483 0 : bool foundHandler = false;
484 0 : mContextPool.ForEachActiveObject([&](auto * ec) {
485 0 : if (ec->HasSessionHandle())
486 : {
487 0 : ec->OnConnectionAttemptComplete(conn, conErr);
488 0 : foundHandler = true;
489 : }
490 0 : return Loop::Continue;
491 : });
492 :
493 0 : return foundHandler;
494 : }
495 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
496 :
497 : } // namespace Messaging
498 : } // namespace chip
|