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