| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2019-2020 Google LLC. |
| * Copyright (c) 2013-2017 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file implements the ChipMessageLayer class. It manages communication |
| * with other CHIP nodes by employing one of several Inetlayer endpoints |
| * to establish a communication channel with other CHIP nodes. |
| * |
| */ |
| |
| #ifndef __STDC_FORMAT_MACROS |
| #define __STDC_FORMAT_MACROS |
| #endif |
| |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| |
| #include <errno.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <core/CHIPCore.h> |
| #include <core/CHIPEncoding.h> |
| #include <crypto/CHIPCryptoPAL.h> |
| #include <message/CHIPExchangeMgr.h> |
| #include <message/CHIPMessageLayer.h> |
| #include <message/CHIPSecurityMgr.h> |
| #include <support/CHIPFaultInjection.h> |
| #include <support/CodeUtils.h> |
| #include <support/ErrorStr.h> |
| #include <support/crypto/AESBlockCipher.h> |
| #include <support/crypto/CHIPCrypto.h> |
| #include <support/crypto/CTRMode.h> |
| #include <support/crypto/HMAC.h> |
| #include <support/crypto/HashAlgos.h> |
| #include <support/logging/CHIPLogging.h> |
| |
| using namespace chip::Ble; |
| using namespace chip::Crypto; |
| using namespace chip::Encoding; |
| using namespace chip::Inet; |
| using namespace chip::System; |
| |
| namespace chip { |
| |
| /** |
| * @def CHIP_BIND_DETAIL_LOGGING |
| * |
| * @brief |
| * Use CHIP Bind detailed logging for CHIP communication. |
| * |
| */ |
| #ifndef CHIP_BIND_DETAIL_LOGGING |
| #define CHIP_BIND_DETAIL_LOGGING 1 |
| #endif |
| |
| /** |
| * @def ChipBindLog(MSG, ...) |
| * |
| * @brief |
| * Define ChipBindLogic to be the same as ChipLogProgress based on |
| * whether both #CHIP_BIND_DETAIL_LOGGING and #CHIP_DETAIL_LOGGING |
| * are set. |
| * |
| */ |
| #if CHIP_BIND_DETAIL_LOGGING && CHIP_DETAIL_LOGGING |
| #define ChipBindLog(MSG, ...) ChipLogProgress(MessageLayer, MSG, ##__VA_ARGS__) |
| #else |
| #define ChipBindLog(MSG, ...) |
| #endif |
| |
| enum |
| { |
| kKeyIdLen = 2, |
| kMinPayloadLen = 1 |
| }; |
| |
| /** |
| * The CHIP Message layer constructor. |
| * |
| * @note |
| * The class must be initialized via ChipMessageLayer::Init() |
| * prior to use. |
| * |
| */ |
| ChipMessageLayer::ChipMessageLayer() |
| { |
| State = kState_NotInitialized; |
| } |
| |
| /** |
| * Initialize the CHIP Message layer object. |
| * |
| * @param[in] context A pointer to the InitContext object. |
| * |
| * @retval #CHIP_NO_ERROR on successful initialization. |
| * @retval #CHIP_ERROR_INVALID_ARGUMENT if the passed InitContext object is NULL. |
| * @retval #CHIP_ERROR_INCORRECT_STATE if the state of the ChipMessageLayer object is incorrect. |
| * @retval other errors generated from the lower Inet layer during endpoint creation. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::Init(InitContext * context) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| VerifyOrExit(State == kState_NotInitialized, err = CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrExit(context != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| State = kState_Initializing; |
| |
| SystemLayer = context->systemLayer; |
| Inet = context->inet; |
| #if CONFIG_NETWORK_LAYER_BLE |
| mBle = context->ble; |
| #endif |
| |
| FabricState = context->fabricState; |
| FabricState->MessageLayer = this; |
| OnMessageReceived = nullptr; |
| OnReceiveError = nullptr; |
| OnConnectionReceived = nullptr; |
| OnUnsecuredConnectionReceived = nullptr; |
| OnUnsecuredConnectionCallbacksRemoved = nullptr; |
| OnAcceptError = nullptr; |
| OnMessageLayerActivityChange = nullptr; |
| memset(mConPool, 0, sizeof(mConPool)); |
| AppState = nullptr; |
| ExchangeMgr = nullptr; |
| SecurityMgr = nullptr; |
| IsListening = context->listenTCP || context->listenUDP; |
| IncomingConIdleTimeout = CHIP_CONFIG_DEFAULT_INCOMING_CONNECTION_IDLE_TIMEOUT; |
| |
| // Internal and for Debug Only; When set, Message Layer drops message and returns. |
| mDropMessage = false; |
| mFlags = 0; |
| SetTCPListenEnabled(context->listenTCP); |
| SetUDPListenEnabled(context->listenUDP); |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| SetEphemeralUDPPortEnabled(context->enableEphemeralUDPPort); |
| #endif |
| |
| mIPv6TCPListen = nullptr; |
| mIPv6UDP = nullptr; |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| mIPv4TCPListen = nullptr; |
| mIPv4UDP = nullptr; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| #if CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| mIPv6UDPMulticastRcv = nullptr; |
| #if INET_CONFIG_ENABLE_IPV4 |
| mIPv4UDPBroadcastRcv = nullptr; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| #endif // CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| mIPv6EphemeralUDP = nullptr; |
| #if INET_CONFIG_ENABLE_IPV4 |
| mIPv4EphemeralUDP = nullptr; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| #endif // CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| #if CHIP_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| mUnsecuredIPv6TCPListen = NULL; |
| #endif |
| |
| err = RefreshEndpoints(); |
| SuccessOrExit(err); |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| if (context->listenBLE && mBle != nullptr) |
| { |
| mBle->mAppState = this; |
| mBle->OnChipBleConnectReceived = HandleIncomingBleConnection; |
| ChipLogProgress(MessageLayer, "Accepting WoBLE connections"); |
| } |
| else |
| { |
| ChipLogProgress(MessageLayer, "CHIPoBLE disabled%s", (mBle != NULL) ? " by application" : " (BLE layer not initialized)"); |
| } |
| #endif // CONFIG_NETWORK_LAYER_BLE |
| |
| State = kState_Initialized; |
| |
| exit: |
| if (err != CHIP_NO_ERROR && State == kState_Initializing) |
| { |
| Shutdown(); |
| } |
| return err; |
| } |
| |
| /** |
| * Shutdown the ChipMessageLayer. |
| * |
| * Close all open Inet layer endpoints, reset all |
| * higher layer callbacks, member variables and objects. |
| * A call to Shutdown() terminates the ChipMessageLayer |
| * object. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::Shutdown() |
| { |
| CloseEndpoints(); |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| if (mBle != nullptr && mBle->mAppState == this) |
| { |
| mBle->mAppState = nullptr; |
| mBle->OnChipBleConnectReceived = nullptr; |
| } |
| #endif // CONFIG_NETWORK_LAYER_BLE |
| |
| State = kState_NotInitialized; |
| IsListening = false; |
| FabricState = nullptr; |
| OnMessageReceived = nullptr; |
| OnReceiveError = nullptr; |
| OnUnsecuredConnectionReceived = nullptr; |
| OnConnectionReceived = nullptr; |
| OnAcceptError = nullptr; |
| OnMessageLayerActivityChange = nullptr; |
| memset(mConPool, 0, sizeof(mConPool)); |
| ExchangeMgr = nullptr; |
| AppState = nullptr; |
| mFlags = 0; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * Encode a CHIP Message layer header into an PacketBuffer. |
| * |
| * @param[in] destAddr The destination IP Address. |
| * |
| * @param[in] destPort The destination port. |
| * |
| * @param[in] sendIntId The interface on which to send the CHIP message. |
| * |
| * @param[in] msgInfo A pointer to a ChipMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object that would hold the CHIP message. |
| * |
| * @retval #CHIP_NO_ERROR on successful encoding of the CHIP message. |
| * @retval #CHIP_ERROR_UNSUPPORTED_MESSAGE_VERSION if the CHIP Message version is not supported. |
| * @retval #CHIP_ERROR_INVALID_MESSAGE_LENGTH if the payload length in the message buffer is zero. |
| * @retval #CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE if the encryption type is not supported. |
| * @retval #CHIP_ERROR_MESSAGE_TOO_LONG if the encoded message would be longer than the |
| * requested maximum. |
| * @retval #CHIP_ERROR_BUFFER_TOO_SMALL if there is not enough space before or after the |
| * message payload. |
| * @retval other errors generated by the fabric state object when fetching the session state. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::EncodeMessage(const IPAddress & destAddr, uint16_t destPort, InterfaceId sendIntId, |
| ChipMessageInfo * msgInfo, PacketBuffer * payload) |
| { |
| CHIP_ERROR res = CHIP_NO_ERROR; |
| |
| // Set the source node identifier in the message header. |
| if ((msgInfo->Flags & kChipMessageFlag_ReuseSourceId) == 0) |
| msgInfo->SourceNodeId = FabricState->LocalNodeId; |
| |
| // Force inclusion of the source node identifier if the destination address is not a local fabric address. |
| // |
| // Technically it should be possible to omit the source node identifier in other situations beyond the |
| // ones allowed for here. However it is difficult to determine exactly what the source IP |
| // address will be when sending a UDP packet, so we err on the side of correctness and only omit |
| // the source identifier if we're part of a fabric and sending to another member of the same fabric. |
| if (!FabricState->IsFabricAddress(destAddr)) |
| msgInfo->Flags |= kChipMessageFlag_SourceNodeId; |
| |
| // Force the destination node identifier to be included if it doesn't match the interface identifier in |
| // the destination address. |
| if (!destAddr.IsIPv6ULA() || IPv6InterfaceIdToChipNodeId(destAddr.InterfaceId()) != msgInfo->DestNodeId) |
| msgInfo->Flags |= kChipMessageFlag_DestNodeId; |
| |
| // Encode the CHIP message. NOTE that this results in the payload buffer containing the entire encoded message. |
| res = EncodeMessage(msgInfo, payload, nullptr, UINT16_MAX, 0); |
| |
| return res; |
| } |
| |
| /** |
| * Send a CHIP message using the underlying Inetlayer UDP endpoint after encoding it. |
| * |
| * @note |
| * The destination port used is #CHIP_PORT. |
| * |
| * @param[in] msgInfo A pointer to a ChipMessageInfo object containing information |
| * about the message to be sent. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the |
| * encoded CHIP message. |
| * |
| * @retval #CHIP_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::SendMessage(ChipMessageInfo * msgInfo, PacketBuffer * payload) |
| { |
| return SendMessage(IPAddress::Any, msgInfo, payload); |
| } |
| |
| /** |
| * Send a CHIP message using the underlying Inetlayer UDP endpoint after encoding it. |
| * |
| * @note |
| * -The destination port used is #CHIP_PORT. |
| * |
| * -If the destination address has not been supplied, attempt to determine it from the node identifier in |
| * the message header. Fail if this can't be done. |
| * |
| * -If the destination address is a fabric address for the local fabric, and the caller |
| * didn't specify the destination node id, extract it from the destination address. |
| * |
| * @param[in] destAddr The destination IP Address. |
| * |
| * @param[in] msgInfo A pointer to a ChipMessageInfo object containing information |
| * about the message to be sent. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the |
| * encoded CHIP message. |
| * |
| * @retval #CHIP_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::SendMessage(const IPAddress & destAddr, ChipMessageInfo * msgInfo, PacketBuffer * payload) |
| { |
| return SendMessage(destAddr, CHIP_PORT, INET_NULL_INTERFACEID, msgInfo, payload); |
| } |
| |
| /** |
| * Send a CHIP message using the underlying Inetlayer UDP endpoint after encoding it. |
| * |
| * @note |
| * -If the destination address has not been supplied, attempt to determine it from the node identifier in |
| * the message header. Fail if this can't be done. |
| * |
| * -If the destination address is a fabric address for the local fabric, and the caller |
| * didn't specify the destination node id, extract it from the destination address. |
| * |
| * @param[in] aDestAddr The destination IP Address. |
| * |
| * @param[in] destPort The destination port. |
| * |
| * @param[in] sendIntfId The interface on which to send the CHIP message. |
| * |
| * @param[in] msgInfo A pointer to a ChipMessageInfo object containing information |
| * about the message to be sent. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the |
| * encoded CHIP message. |
| * |
| * @retval #CHIP_NO_ERROR on successfully sending the message down to the network |
| * layer. |
| * @retval #CHIP_ERROR_INVALID_ADDRESS if the destAddr is not specified or cannot be determined |
| * from destination node id. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::SendMessage(const IPAddress & aDestAddr, uint16_t destPort, InterfaceId sendIntfId, |
| ChipMessageInfo * msgInfo, PacketBuffer * payload) |
| { |
| CHIP_ERROR res = CHIP_NO_ERROR; |
| IPAddress destAddr = aDestAddr; |
| |
| // Determine the message destination address based on the destination nodeId. |
| res = SelectDestNodeIdAndAddress(msgInfo->DestNodeId, destAddr); |
| SuccessOrExit(res); |
| |
| res = EncodeMessage(destAddr, destPort, sendIntfId, msgInfo, payload); |
| SuccessOrExit(res); |
| |
| // on delay send, we do everything except actually send the |
| // message. As a result, the payload will contain the entire |
| // state required for sending it a bit later |
| if (msgInfo->Flags & kChipMessageFlag_DelaySend) |
| return CHIP_NO_ERROR; |
| |
| // Copy msg to a right-sized buffer if applicable |
| payload = PacketBuffer::RightSize(payload); |
| |
| // Send the message using the appropriate UDP endpoint(s). |
| return SendMessage(destAddr, destPort, sendIntfId, payload, msgInfo->Flags); |
| |
| exit: |
| if ((res != CHIP_NO_ERROR) && (payload != nullptr) && ((msgInfo->Flags & kChipMessageFlag_RetainBuffer) == 0)) |
| { |
| PacketBuffer::Free(payload); |
| } |
| |
| return res; |
| } |
| |
| bool ChipMessageLayer::IsIgnoredMulticastSendError(CHIP_ERROR err) |
| { |
| return err == CHIP_NO_ERROR || |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| err == System::MapErrorLwIP(ERR_RTE) |
| #else |
| err == System::MapErrorPOSIX(ENETUNREACH) || err == System::MapErrorPOSIX(EADDRNOTAVAIL) |
| #endif |
| ; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::FilterUDPSendError(CHIP_ERROR err, bool isMulticast) |
| { |
| // Don't report certain types of routing errors when they occur while sending multicast packets. |
| // These may indicate that the underlying interface doesn't support multicast (e.g. the loopback |
| // interface on linux) or that the selected interface doesn't have an appropriate source address. |
| if (isMulticast) |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| if (err == System::MapErrorLwIP(ERR_RTE)) |
| { |
| err = CHIP_NO_ERROR; |
| } |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| if (err == System::MapErrorPOSIX(ENETUNREACH) || err == System::MapErrorPOSIX(EADDRNOTAVAIL)) |
| { |
| err = CHIP_NO_ERROR; |
| } |
| #endif |
| } |
| |
| return err; |
| } |
| |
| /** |
| * Checks if error, while sending, is critical enough to report to the application. |
| * |
| * @param[in] err The #CHIP_ERROR being checked for criticality. |
| * |
| * @return true if the error is NOT critical; false otherwise. |
| * |
| */ |
| bool ChipMessageLayer::IsSendErrorNonCritical(CHIP_ERROR err) |
| { |
| return (err == INET_ERROR_NOT_IMPLEMENTED || err == INET_ERROR_OUTBOUND_MESSAGE_TRUNCATED || |
| err == INET_ERROR_MESSAGE_TOO_LONG || err == INET_ERROR_NO_MEMORY || CHIP_CONFIG_IsPlatformErrorNonCritical(err)); |
| } |
| |
| /** |
| * Set the 'ForceRefreshUDPEndpoints' flag if needed. |
| * |
| * Based on the error returned when sending a UDP message, set a flag in the ChipMessageLayer |
| * that will force a complete refresh of all UDPEndPoints the next time \c RefreshEndPoints is |
| * called. |
| */ |
| void ChipMessageLayer::CheckForceRefreshUDPEndPointsNeeded(CHIP_ERROR err) |
| { |
| // On some sockets-based systems, the OS will invalidate bound UDP endpoints when certain |
| // network transitions occur. This is known to occur on Android, although the precise |
| // conditions are unclear. When that happens, set the ForceRefreshUDPEndPoints flag to |
| // force all UDPEndPoints to be closed and re-opened on the next call to RefreshEndPoints(). |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| if (err == System::MapErrorPOSIX(EPIPE)) |
| { |
| SetFlag(mFlags, kFlag_ForceRefreshUDPEndPoints); |
| } |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| } |
| |
| /** |
| * Send an encoded CHIP message using the appropriate underlying Inetlayer UDPEndPoint (or EndPoints). |
| * |
| * @param[in] destAddr The destination IP Address. |
| * |
| * @param[in] destPort The destination port. |
| * |
| * @param[in] sendIntfId The interface on which to send the CHIP message. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded CHIP message. |
| * |
| * @param[in] msgSendFlags Send flags containing metadata about the message for the lower Inet layer. |
| * |
| * @retval #CHIP_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::SendMessage(const IPAddress & destAddr, uint16_t destPort, InterfaceId sendIntfId, |
| PacketBuffer * payload, uint32_t msgFlags) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| UDPEndPoint * ep; |
| enum |
| { |
| kUnicast, |
| kMulticast_OneInterface, |
| kMulticast_AllInterfaces, |
| kMulticast_AllFabricAddrs, |
| } sendAction; |
| uint16_t udpSendFlags; |
| |
| IPPacketInfo pktInfo; |
| pktInfo.Clear(); |
| pktInfo.DestAddress = destAddr; |
| pktInfo.DestPort = destPort; |
| pktInfo.Interface = sendIntfId; |
| |
| // Check if drop flag is set; If so, do not send message; return CHIP_NO_ERROR; |
| VerifyOrExit(!mDropMessage, err = CHIP_NO_ERROR); |
| |
| // Drop the message and return. Free the buffer if it does not need to be |
| // retained(e.g., for RMP retransmissions). |
| CHIP_FAULT_INJECT(FaultInjection::kFault_DropOutgoingUDPMsg, ExitNow(err = CHIP_NO_ERROR);); |
| |
| // Select a UDP endpoint object for sending a message based on the destination address type |
| // and the message send flags. |
| err = SelectOutboundUDPEndPoint(destAddr, msgFlags, ep); |
| SuccessOrExit(err); |
| |
| // Select an appropriate send action for the message. |
| // |
| // For unicast messages, send the message once to the given address. If a target interface |
| // is given, the message will be sent over that interface. |
| // |
| // For multicast/broadcast messages... |
| // |
| // If the local node is bound to a specific address (IPv4 or IPv6) send the multicast |
| // message once over the bound interface. |
| // |
| // Otherwise, if the destination is an IPv6 multicast address, and the local node is |
| // a member of a CHIP fabric, AND MulticastFromLinkLocal has NOT been specified, send |
| // the message once for each CHIP Fabric ULA assigned to a local interface that supports |
| // multicast. If a target interface is given, only consider ULAs on that interface. |
| // |
| // Otherwise, if a target interface is given, send the multicast message over that |
| // interface only. |
| // |
| // Otherwise, send the message over each local interface that supports multicast. |
| // |
| if (!destAddr.IsMulticast() && !destAddr.IsIPv4Broadcast()) |
| { |
| sendAction = kUnicast; |
| } |
| #if CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| else if (destAddr.IsIPv4() ? IsBoundToLocalIPv4Address() : IsBoundToLocalIPv6Address()) |
| { |
| sendAction = kMulticast_OneInterface; |
| } |
| #endif // CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| else if (destAddr.IsIPv6() && FabricState->FabricId != kFabricIdNotSpecified && |
| !GetFlag(msgFlags, kChipMessageFlag_DefaultMulticastSourceAddress)) |
| { |
| sendAction = kMulticast_AllFabricAddrs; |
| } |
| else if (sendIntfId != INET_NULL_INTERFACEID) |
| { |
| sendAction = kMulticast_OneInterface; |
| } |
| else |
| { |
| sendAction = kMulticast_AllInterfaces; |
| } |
| |
| // Send the message... |
| switch (sendAction) |
| { |
| case kUnicast: |
| case kMulticast_OneInterface: |
| |
| // Send the message once. If requested by the caller, instruct the end point code to not free the |
| // message buffer. If a send interface was specified, the message is sent over that interface. |
| udpSendFlags = GetFlag(msgFlags, kChipMessageFlag_RetainBuffer) ? UDPEndPoint::kSendFlag_RetainBuffer : 0; |
| err = ep->SendMsg(&pktInfo, payload, udpSendFlags); |
| payload = nullptr; // Prevent call to Free() in exit code |
| CheckForceRefreshUDPEndPointsNeeded(err); |
| err = FilterUDPSendError(err, sendAction == kMulticast_OneInterface); |
| break; |
| |
| case kMulticast_AllInterfaces: |
| |
| // Send the message over each local interface that supports multicast. |
| for (InterfaceIterator intfIter; intfIter.HasCurrent(); intfIter.Next()) |
| { |
| if (intfIter.SupportsMulticast()) |
| { |
| pktInfo.Interface = intfIter.GetInterface(); |
| CHIP_ERROR sendErr = ep->SendMsg(&pktInfo, payload, UDPEndPoint::kSendFlag_RetainBuffer); |
| CheckForceRefreshUDPEndPointsNeeded(sendErr); |
| if (err == CHIP_NO_ERROR) |
| { |
| err = FilterUDPSendError(sendErr, true); |
| } |
| } |
| } |
| |
| break; |
| |
| case kMulticast_AllFabricAddrs: |
| |
| // Send the message once for each CHIP Fabric ULA assigned to a local interface that supports |
| // multicast/broadcast. If the caller has specified a particular interface, only send over the |
| // specified interface. For each message sent, arrange for the source address to be the Fabric ULA. |
| for (InterfaceAddressIterator addrIter; addrIter.HasCurrent(); addrIter.Next()) |
| { |
| pktInfo.SrcAddress = addrIter.GetAddress(); |
| pktInfo.Interface = addrIter.GetInterface(); |
| if (addrIter.SupportsMulticast() && FabricState->IsLocalFabricAddress(pktInfo.SrcAddress) && |
| (sendIntfId == INET_NULL_INTERFACEID || pktInfo.Interface == sendIntfId)) |
| { |
| CHIP_ERROR sendErr = ep->SendMsg(&pktInfo, payload, UDPEndPoint::kSendFlag_RetainBuffer); |
| CheckForceRefreshUDPEndPointsNeeded(sendErr); |
| if (err == CHIP_NO_ERROR) |
| { |
| err = FilterUDPSendError(sendErr, true); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| exit: |
| if (payload != nullptr && !GetFlag(msgFlags, kChipMessageFlag_RetainBuffer)) |
| PacketBuffer::Free(payload); |
| return err; |
| } |
| |
| /** |
| * Select an appropriate UDP endpoint for sending a CHIP message. |
| */ |
| CHIP_ERROR ChipMessageLayer::SelectOutboundUDPEndPoint(const IPAddress & destAddr, uint32_t msgFlags, UDPEndPoint *& ep) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| // Select a UDP endpoint object for sending a message based on the destination address type |
| // and the message send flags. |
| // |
| // If the CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT option is set, select the ephemeral UDP |
| // endpoint if the caller has specified the 'ViaEphemeralUDPPort' flag. This will result in |
| // the source port field of the UDP message being set to the currently active ephemeral |
| // port. Otherwise, select the CHIP UDP endpoint. This will result in the source port |
| // field being set to the well-known CHIP port. |
| // |
| switch (destAddr.Type()) |
| { |
| #if INET_CONFIG_ENABLE_IPV4 |
| case kIPAddressType_IPv4: |
| if (GetFlag(msgFlags, kChipMessageFlag_ViaEphemeralUDPPort)) |
| { |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| ep = mIPv4EphemeralUDP; |
| #else |
| ep = NULL; |
| #endif |
| } |
| else |
| { |
| ep = mIPv4UDP; |
| } |
| break; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| case kIPAddressType_IPv6: |
| if (GetFlag(msgFlags, kChipMessageFlag_ViaEphemeralUDPPort)) |
| { |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| ep = mIPv6EphemeralUDP; |
| #else |
| ep = NULL; |
| #endif |
| } |
| else |
| { |
| ep = mIPv6UDP; |
| } |
| break; |
| |
| default: |
| ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| |
| VerifyOrExit(ep != nullptr, err = CHIP_ERROR_NO_ENDPOINT); |
| |
| exit: |
| return err; |
| } |
| |
| /** |
| * Resend an encoded CHIP message using the underlying Inetlayer UDP endpoint. |
| * |
| * @param[in] msgInfo A pointer to the ChipMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded CHIP message. |
| * |
| * @retval #CHIP_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::ResendMessage(ChipMessageInfo * msgInfo, PacketBuffer * payload) |
| { |
| IPAddress destAddr = IPAddress::Any; |
| return ResendMessage(destAddr, msgInfo, payload); |
| } |
| |
| /** |
| * Resend an encoded CHIP message using the underlying Inetlayer UDP endpoint. |
| * |
| * @note |
| * The destination port used is #CHIP_PORT. |
| * |
| * @param[in] destAddr The destination IP Address. |
| * |
| * @param[in] msgInfo A pointer to the ChipMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded CHIP message. |
| * |
| * @retval #CHIP_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::ResendMessage(const IPAddress & destAddr, ChipMessageInfo * msgInfo, PacketBuffer * payload) |
| { |
| return ResendMessage(destAddr, CHIP_PORT, msgInfo, payload); |
| } |
| |
| /** |
| * Resend an encoded CHIP message using the underlying Inetlayer UDP endpoint. |
| * |
| * @param[in] destAddr The destination IP Address. |
| * |
| * @param[in] destPort The destination port. |
| * |
| * @param[in] msgInfo A pointer to the ChipMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded CHIP message. |
| * |
| * @retval #CHIP_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::ResendMessage(const IPAddress & destAddr, uint16_t destPort, ChipMessageInfo * msgInfo, |
| PacketBuffer * payload) |
| { |
| return ResendMessage(destAddr, CHIP_PORT, INET_NULL_INTERFACEID, msgInfo, payload); |
| } |
| |
| /** |
| * Resend an encoded CHIP message using the underlying Inetlayer UDP endpoint. |
| * |
| * @note |
| * -If the destination address has not been supplied, attempt to determine it from the node identifier in |
| * the message header. Fail if this can't be done. |
| * |
| * -If the destination address is a fabric address for the local fabric, and the caller |
| * didn't specify the destination node id, extract it from the destination address. |
| * |
| * @param[in] aDestAddr The destination IP Address. |
| * |
| * @param[in] destPort The destination port. |
| * |
| * @param[in] interfaceId The interface on which to send the CHIP message. |
| * |
| * @param[in] msgInfo A pointer to the ChipMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded CHIP message. |
| * |
| * @retval #CHIP_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::ResendMessage(const IPAddress & aDestAddr, uint16_t destPort, InterfaceId interfaceId, |
| ChipMessageInfo * msgInfo, PacketBuffer * payload) |
| { |
| CHIP_ERROR res = CHIP_NO_ERROR; |
| IPAddress destAddr = aDestAddr; |
| |
| res = SelectDestNodeIdAndAddress(msgInfo->DestNodeId, destAddr); |
| SuccessOrExit(res); |
| |
| return SendMessage(destAddr, destPort, interfaceId, payload, msgInfo->Flags); |
| exit: |
| if ((res != CHIP_NO_ERROR) && (payload != nullptr) && ((msgInfo->Flags & kChipMessageFlag_RetainBuffer) == 0)) |
| { |
| PacketBuffer::Free(payload); |
| } |
| return res; |
| } |
| |
| /** |
| * Get the number of ChipConnections in use and the size of the pool |
| * |
| * @param[out] aOutInUse Reference to size_t, in which the number of |
| * connections in use is stored. |
| * |
| */ |
| void ChipMessageLayer::GetConnectionPoolStats(chip::System::Stats::count_t & aOutInUse) const |
| { |
| aOutInUse = 0; |
| |
| const ChipConnection * con = mConPool; |
| for (int i = 0; i < CHIP_CONFIG_MAX_CONNECTIONS; i++, con++) |
| { |
| if (con->mRefCount != 0) |
| { |
| aOutInUse++; |
| } |
| } |
| } |
| |
| /** |
| * Create a new ChipConnection object from a pool. |
| * |
| * @return a pointer to the newly created ChipConnection object if successful, otherwise |
| * NULL. |
| * |
| */ |
| ChipConnection * ChipMessageLayer::NewConnection() |
| { |
| ChipConnection * con = mConPool; |
| for (int i = 0; i < CHIP_CONFIG_MAX_CONNECTIONS; i++, con++) |
| { |
| if (con->mRefCount == 0) |
| { |
| con->Init(this); |
| return con; |
| } |
| } |
| |
| ChipLogError(ExchangeManager, "New con FAILED"); |
| return nullptr; |
| } |
| |
| void ChipMessageLayer::GetIncomingTCPConCount(const IPAddress & peerAddr, uint16_t & count, uint16_t & countFromIP) |
| { |
| count = 0; |
| countFromIP = 0; |
| |
| ChipConnection * con = mConPool; |
| for (int i = 0; i < CHIP_CONFIG_MAX_CONNECTIONS; i++, con++) |
| { |
| if (con->mRefCount > 0 && con->NetworkType == ChipConnection::kNetworkType_IP && con->IsIncoming()) |
| { |
| count++; |
| if (con->PeerAddr == peerAddr) |
| { |
| countFromIP++; |
| } |
| } |
| } |
| } |
| |
| CHIP_ERROR ChipMessageLayer::SetUnsecuredConnectionListener(ConnectionReceiveFunct newOnUnsecuredConnectionReceived, |
| CallbackRemovedFunct newOnUnsecuredConnectionCallbacksRemoved, |
| bool force, void * listenerState) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| ChipLogProgress(ExchangeManager, "Entered SetUnsecuredConnectionReceived, cb = %p, %p", newOnUnsecuredConnectionReceived, |
| newOnUnsecuredConnectionCallbacksRemoved); |
| |
| if (!UnsecuredListenEnabled()) |
| { |
| err = EnableUnsecuredListen(); |
| SuccessOrExit(err); |
| } |
| |
| // New OnUnsecuredConnectionReceived cannot be null. To clear, use ClearOnUnsecuredConnectionReceived(). |
| VerifyOrExit(newOnUnsecuredConnectionReceived != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| if (OnUnsecuredConnectionReceived != nullptr) |
| { |
| if (!force) |
| { |
| err = CHIP_ERROR_INCORRECT_STATE; |
| ExitNow(); |
| } |
| else if (OnUnsecuredConnectionCallbacksRemoved != nullptr) |
| { |
| // Notify application that its previous OnUnsecuredConnectionReceived callback has been removed. |
| OnUnsecuredConnectionCallbacksRemoved(UnsecuredConnectionReceivedAppState); |
| } |
| } |
| |
| OnUnsecuredConnectionReceived = newOnUnsecuredConnectionReceived; |
| OnUnsecuredConnectionCallbacksRemoved = newOnUnsecuredConnectionCallbacksRemoved; |
| UnsecuredConnectionReceivedAppState = listenerState; |
| |
| exit: |
| return err; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::ClearUnsecuredConnectionListener(ConnectionReceiveFunct oldOnUnsecuredConnectionReceived, |
| CallbackRemovedFunct oldOnUnsecuredConnectionCallbacksRemoved) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| ChipLogProgress(ExchangeManager, "Entered ClearUnsecuredConnectionListener, cbs = %p, %p", oldOnUnsecuredConnectionReceived, |
| oldOnUnsecuredConnectionCallbacksRemoved); |
| |
| // Only clear callbacks and suppress OnUnsecuredConnectionCallbacksRemoved if caller can prove it owns current |
| // callbacks. For proof of identification, we accept copies of callback function pointers. |
| if (oldOnUnsecuredConnectionReceived != OnUnsecuredConnectionReceived || |
| oldOnUnsecuredConnectionCallbacksRemoved != OnUnsecuredConnectionCallbacksRemoved) |
| { |
| if (oldOnUnsecuredConnectionReceived != OnUnsecuredConnectionReceived) |
| ChipLogError(ExchangeManager, "bad arg: OnUnsecuredConnectionReceived"); |
| else |
| ChipLogError(ExchangeManager, "bad arg: OnUnsecuredConnectionCallbacksRemoved"); |
| err = CHIP_ERROR_INVALID_ARGUMENT; |
| ExitNow(); |
| } |
| |
| if (UnsecuredListenEnabled()) |
| { |
| err = DisableUnsecuredListen(); |
| SuccessOrExit(err); |
| } |
| |
| OnUnsecuredConnectionReceived = nullptr; |
| OnUnsecuredConnectionCallbacksRemoved = nullptr; |
| UnsecuredConnectionReceivedAppState = nullptr; |
| |
| exit: |
| return err; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::SelectDestNodeIdAndAddress(uint64_t & destNodeId, IPAddress & destAddr) |
| { |
| // If the destination address has not been supplied, attempt to determine it from the node id. |
| // Fail if this can't be done. |
| if (destAddr == IPAddress::Any) |
| { |
| destAddr = FabricState->SelectNodeAddress(destNodeId); |
| if (destAddr == IPAddress::Any) |
| return CHIP_ERROR_INVALID_ADDRESS; |
| } |
| |
| // If the destination address is a fabric address for the local fabric, and the caller |
| // didn't specify the destination node id, extract it from the destination address. |
| if (FabricState->IsFabricAddress(destAddr) && destNodeId == kNodeIdNotSpecified) |
| destNodeId = IPv6InterfaceIdToChipNodeId(destAddr.InterfaceId()); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| // Encode and return message header field value. |
| static uint16_t EncodeHeaderField(const ChipMessageInfo * msgInfo) |
| { |
| return (((static_cast<uint16_t>(msgInfo->Flags)) << kMsgHeaderField_FlagsShift) & kMsgHeaderField_FlagsMask) | |
| (((static_cast<uint16_t>(msgInfo->EncryptionType)) << kMsgHeaderField_EncryptionTypeShift) & |
| kMsgHeaderField_EncryptionTypeMask) | |
| (((static_cast<uint16_t>(msgInfo->MessageVersion)) << kMsgHeaderField_MessageVersionShift) & |
| kMsgHeaderField_MessageVersionMask); |
| } |
| |
| // Decode message header field value. |
| static void DecodeHeaderField(const uint16_t headerField, ChipMessageInfo * msgInfo) |
| { |
| msgInfo->Flags = static_cast<uint16_t>((headerField & kMsgHeaderField_FlagsMask) >> kMsgHeaderField_FlagsShift); |
| msgInfo->EncryptionType = |
| static_cast<uint8_t>((headerField & kMsgHeaderField_EncryptionTypeMask) >> kMsgHeaderField_EncryptionTypeShift); |
| msgInfo->MessageVersion = |
| static_cast<uint8_t>((headerField & kMsgHeaderField_MessageVersionMask) >> kMsgHeaderField_MessageVersionShift); |
| } |
| |
| /** |
| * Decode a CHIP Message layer header from a received CHIP message. |
| * |
| * @param[in] msgBuf A pointer to the PacketBuffer object holding the CHIP message. |
| * |
| * @param[in] msgInfo A pointer to a ChipMessageInfo object which will receive information |
| * about the message. |
| * |
| * @param[out] payloadStart A pointer to a pointer to the position in the message buffer after |
| * decoding is complete. |
| * |
| * @retval #CHIP_NO_ERROR On successful decoding of the message header. |
| * @retval #CHIP_ERROR_INVALID_MESSAGE_LENGTH |
| * If the message buffer passed is of invalid length. |
| * @retval #CHIP_ERROR_UNSUPPORTED_MESSAGE_VERSION |
| * If the CHIP Message header format version is not supported. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::DecodeHeader(PacketBuffer * msgBuf, ChipMessageInfo * msgInfo, uint8_t ** payloadStart) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| uint8_t * msgStart = msgBuf->Start(); |
| uint16_t msgLen = msgBuf->DataLength(); |
| uint8_t * msgEnd = msgStart + msgLen; |
| uint8_t * p = msgStart; |
| uint16_t headerField; |
| |
| if (msgLen < 6) |
| { |
| ExitNow(err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| } |
| |
| // Read and verify the header field. |
| headerField = LittleEndian::Read16(p); |
| VerifyOrExit((headerField & kMsgHeaderField_ReservedFlagsMask) == 0, err = CHIP_ERROR_INVALID_MESSAGE_FLAG); |
| |
| // Decode the header field. |
| DecodeHeaderField(headerField, msgInfo); |
| |
| // Error if the message version is unsupported. |
| if (msgInfo->MessageVersion != kChipMessageVersion_V1) |
| { |
| ExitNow(err = CHIP_ERROR_UNSUPPORTED_MESSAGE_VERSION); |
| } |
| |
| // Decode the message id. |
| msgInfo->MessageId = LittleEndian::Read32(p); |
| |
| // Decode the source node identifier if included in the message. |
| if (msgInfo->Flags & kChipMessageFlag_SourceNodeId) |
| { |
| if ((p + 8) > msgEnd) |
| { |
| ExitNow(err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| } |
| msgInfo->SourceNodeId = LittleEndian::Read64(p); |
| } |
| |
| // Decode the destination node identifier if included in the message. |
| if (msgInfo->Flags & kChipMessageFlag_DestNodeId) |
| { |
| if ((p + 8) > msgEnd) |
| { |
| ExitNow(err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| } |
| msgInfo->DestNodeId = LittleEndian::Read64(p); |
| } |
| else |
| // TODO: This is wrong. If not specified in the message, the destination node identifier must be |
| // derived from destination IPv6 address to which the message was sent. This is relatively |
| // easy to determine for messages received over TCP (specifically by the inspecting the local |
| // address of the connection). However it is much harder for UDP (no support in LwIP; requires |
| // use of IP_PKTINFO socket option in sockets). For now we just assume the intended destination |
| // is the local node. |
| msgInfo->DestNodeId = FabricState->LocalNodeId; |
| // Decode the encryption key identifier if present. |
| if (msgInfo->EncryptionType != kChipEncryptionType_None) |
| { |
| if ((p + kKeyIdLen) > msgEnd) |
| { |
| ExitNow(err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| } |
| msgInfo->KeyId = LittleEndian::Read16(p); |
| } |
| else |
| { |
| // Clear flag, which could have been accidentally set in the older version of code only for unencrypted messages. |
| msgInfo->Flags &= ~kChipMessageFlag_MsgCounterSyncReq; |
| |
| msgInfo->KeyId = ChipKeyId::kNone; |
| } |
| |
| if (payloadStart != nullptr) |
| { |
| *payloadStart = p; |
| } |
| |
| exit: |
| return err; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::ReEncodeMessage(PacketBuffer * msgBuf) |
| { |
| ChipMessageInfo msgInfo; |
| CHIP_ERROR err; |
| uint8_t * p; |
| ChipSessionState sessionState; |
| uint16_t msgLen = msgBuf->DataLength(); |
| uint8_t * msgStart = msgBuf->Start(); |
| uint16_t encryptionLen; |
| |
| msgInfo.Clear(); |
| msgInfo.SourceNodeId = kNodeIdNotSpecified; |
| |
| err = DecodeHeader(msgBuf, &msgInfo, &p); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| encryptionLen = msgLen - (p - msgStart); |
| |
| err = FabricState->GetSessionState(msgInfo.SourceNodeId, msgInfo.KeyId, msgInfo.EncryptionType, nullptr, sessionState); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| switch (msgInfo.EncryptionType) |
| { |
| case kChipEncryptionType_None: |
| break; |
| |
| case kChipEncryptionType_AES128CTRSHA1: { |
| // TODO: re-validate MIC to ensure that no part of the message has been altered since the time it was received. |
| |
| // Re-encrypt the payload. |
| AES128CTRMode aes128CTR; |
| aes128CTR.SetKey(sessionState.MsgEncKey->EncKey.AES128CTRSHA1.DataKey); |
| aes128CTR.SetChipMessageCounter(msgInfo.SourceNodeId, msgInfo.MessageId); |
| aes128CTR.EncryptData(p, encryptionLen, p); |
| } |
| break; |
| default: |
| return CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE; |
| } |
| |
| // signature remains untouched -- we have not modified it. |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * Encode a ChipMessageLayer header into an PacketBuffer. |
| * |
| * @param[in] msgInfo A pointer to a ChipMessageInfo object containing information |
| * about the message to be encoded. |
| * |
| * @param[in] msgBuf A pointer to the PacketBuffer object that would hold the CHIP message. |
| * |
| * @param[in] con A pointer to the ChipConnection object. |
| * |
| * @param[in] maxLen The maximum length of the encoded CHIP message. |
| * |
| * @param[in] reserve The reserved space before the payload to hold the CHIP message header. |
| * |
| * @retval #CHIP_NO_ERROR on successful encoding of the message. |
| * @retval #CHIP_ERROR_UNSUPPORTED_MESSAGE_VERSION if the CHIP Message header format version is |
| * not supported. |
| * @retval #CHIP_ERROR_INVALID_MESSAGE_LENGTH if the payload length in the message buffer is zero. |
| * @retval #CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE if the encryption type in the message header is not |
| * supported. |
| * @retval #CHIP_ERROR_MESSAGE_TOO_LONG if the encoded message would be longer than the |
| * requested maximum. |
| * @retval #CHIP_ERROR_BUFFER_TOO_SMALL if there is not enough space before or after the |
| * message payload. |
| * @retval other errors generated by the fabric state object when fetching the session state. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::EncodeMessage(ChipMessageInfo * msgInfo, PacketBuffer * msgBuf, ChipConnection * con, uint16_t maxLen, |
| uint16_t reserve) |
| { |
| CHIP_ERROR err; |
| uint8_t * p1; |
| // Error if an unsupported message version requested. |
| if (msgInfo->MessageVersion != kChipMessageVersion_V1) |
| return CHIP_ERROR_UNSUPPORTED_MESSAGE_VERSION; |
| |
| // Message already encoded, don't do anything |
| if (msgInfo->Flags & kChipMessageFlag_MessageEncoded) |
| { |
| ChipMessageInfo existingMsgInfo; |
| existingMsgInfo.Clear(); |
| err = DecodeHeader(msgBuf, &existingMsgInfo, &p1); |
| if (err != CHIP_NO_ERROR) |
| { |
| return err; |
| } |
| msgInfo->DestNodeId = existingMsgInfo.DestNodeId; |
| return CHIP_NO_ERROR; |
| } |
| |
| // Compute the number of bytes that will appear before and after the message payload |
| // in the final encoded message. |
| uint16_t headLen = 6; |
| uint16_t tailLen = 0; |
| uint16_t payloadLen = msgBuf->DataLength(); |
| if (msgInfo->Flags & kChipMessageFlag_SourceNodeId) |
| headLen += 8; |
| if (msgInfo->Flags & kChipMessageFlag_DestNodeId) |
| headLen += 8; |
| switch (msgInfo->EncryptionType) |
| { |
| case kChipEncryptionType_None: |
| break; |
| case kChipEncryptionType_AES128CTRSHA1: |
| // Can only encrypt non-zero length payloads. |
| if (payloadLen == 0) |
| return CHIP_ERROR_INVALID_MESSAGE_LENGTH; |
| headLen += 2; |
| tailLen += HMACSHA1::kDigestLength; |
| break; |
| default: |
| return CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE; |
| } |
| |
| // Error if the encoded message would be longer than the requested maximum. |
| if ((headLen + msgBuf->DataLength() + tailLen) > maxLen) |
| return CHIP_ERROR_MESSAGE_TOO_LONG; |
| |
| // Ensure there's enough room before the payload to hold the message header. |
| // Return an error if there's not enough room in the buffer. |
| if (!msgBuf->EnsureReservedSize(headLen + reserve)) |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| |
| // Error if not enough space after the message payload. |
| if ((msgBuf->DataLength() + tailLen) > msgBuf->MaxDataLength()) |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| |
| uint8_t * payloadStart = msgBuf->Start(); |
| |
| // Get the session state for the given destination node and encryption key. |
| ChipSessionState sessionState; |
| |
| if (msgInfo->DestNodeId == kAnyNodeId) |
| { |
| err = FabricState->GetSessionState(msgInfo->SourceNodeId, msgInfo->KeyId, msgInfo->EncryptionType, con, sessionState); |
| } |
| else |
| { |
| err = FabricState->GetSessionState(msgInfo->DestNodeId, msgInfo->KeyId, msgInfo->EncryptionType, con, sessionState); |
| } |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| // Starting encoding at the appropriate point in the buffer before the payload data. |
| uint8_t * p = payloadStart - headLen; |
| |
| // Allocate a new message identifier and write the message identifier field. |
| msgInfo->MessageId = sessionState.NewMessageId(); |
| |
| #if CHIP_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| // Request message counter synchronization if peer group key counter is not synchronized. |
| if (sessionState.MessageIdNotSynchronized() && ChipKeyId::IsAppGroupKey(msgInfo->KeyId)) |
| { |
| // Set the flag. |
| msgInfo->Flags |= kChipMessageFlag_MsgCounterSyncReq; |
| |
| // Update fabric state. |
| FabricState->OnMsgCounterSyncReqSent(msgInfo->MessageId); |
| } |
| #endif |
| |
| // Adjust the buffer so that the start points to the start of the encoded message. |
| msgBuf->SetStart(p); |
| |
| // Encode and verify the header field. |
| uint16_t headerField = EncodeHeaderField(msgInfo); |
| if ((headerField & kMsgHeaderField_ReservedFlagsMask) != 0) |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| |
| // Write the header field. |
| LittleEndian::Write16(p, headerField); |
| |
| if (msgInfo->DestNodeId == kAnyNodeId) |
| { |
| sessionState.IsDuplicateMessage(msgInfo->MessageId); |
| } |
| |
| LittleEndian::Write32(p, msgInfo->MessageId); |
| |
| // If specified, encode the source node id. |
| if (msgInfo->Flags & kChipMessageFlag_SourceNodeId) |
| { |
| LittleEndian::Write64(p, msgInfo->SourceNodeId); |
| } |
| |
| // If specified, encode the destination node id. |
| if (msgInfo->Flags & kChipMessageFlag_DestNodeId) |
| { |
| LittleEndian::Write64(p, msgInfo->DestNodeId); |
| } |
| |
| switch (msgInfo->EncryptionType) |
| { |
| case kChipEncryptionType_None: |
| // If no encryption requested, skip over the payload in the message buffer. |
| p += payloadLen; |
| break; |
| |
| case kChipEncryptionType_AES128CTRSHA1: |
| // Encode the key id. |
| LittleEndian::Write16(p, msgInfo->KeyId); |
| |
| // At this point we've completed encoding the head of the message (and therefore p == payloadStart), |
| // so skip over the payload data. |
| p += payloadLen; |
| |
| // Compute the integrity check value and store it immediately after the payload data. |
| ComputeIntegrityCheck_AES128CTRSHA1(msgInfo, sessionState.MsgEncKey->EncKey.AES128CTRSHA1.IntegrityKey, payloadStart, |
| payloadLen, p); |
| p += HMACSHA1::kDigestLength; |
| |
| // Encrypt the message payload and the integrity check value that follows it, in place, in the message buffer. |
| Encrypt_AES128CTRSHA1(msgInfo, sessionState.MsgEncKey->EncKey.AES128CTRSHA1.DataKey, payloadStart, |
| payloadLen + HMACSHA1::kDigestLength, payloadStart); |
| |
| break; |
| } |
| |
| msgInfo->Flags |= kChipMessageFlag_MessageEncoded; |
| // Update the buffer length to reflect the entire encoded message. |
| msgBuf->SetDataLength(headLen + payloadLen + tailLen); |
| |
| // We update the cursor (p) out of good hygiene, |
| // such that if the code is extended in the future such that the cursor is used, |
| // it will be in the correct position for such code. |
| IgnoreUnusedVariable(p); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::DecodeMessage(PacketBuffer * msgBuf, uint64_t sourceNodeId, ChipConnection * con, |
| ChipMessageInfo * msgInfo, uint8_t ** rPayload, |
| uint16_t * rPayloadLen) // TODO: use references |
| { |
| CHIP_ERROR err; |
| uint8_t * msgStart = msgBuf->Start(); |
| uint16_t msgLen = msgBuf->DataLength(); |
| uint8_t * msgEnd = msgStart + msgLen; |
| uint8_t * p = msgStart; |
| msgInfo->SourceNodeId = sourceNodeId; |
| err = DecodeHeader(msgBuf, msgInfo, &p); |
| sourceNodeId = msgInfo->SourceNodeId; |
| |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| // Get the session state for the given source node and encryption key. |
| ChipSessionState sessionState; |
| |
| err = FabricState->GetSessionState(sourceNodeId, msgInfo->KeyId, msgInfo->EncryptionType, con, sessionState); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| switch (msgInfo->EncryptionType) |
| { |
| case kChipEncryptionType_None: |
| // Return the position and length of the payload within the message. |
| *rPayloadLen = msgLen - (p - msgStart); |
| *rPayload = p; |
| |
| // Skip over the payload. |
| p += *rPayloadLen; |
| break; |
| |
| case kChipEncryptionType_AES128CTRSHA1: { |
| // Error if the message is short given the expected fields. |
| if ((p + kMinPayloadLen + HMACSHA1::kDigestLength) > msgEnd) |
| return CHIP_ERROR_INVALID_MESSAGE_LENGTH; |
| |
| // Return the position and length of the payload within the message. |
| uint16_t payloadLen = msgLen - ((p - msgStart) + HMACSHA1::kDigestLength); |
| *rPayloadLen = payloadLen; |
| *rPayload = p; |
| |
| // Decrypt the message payload and the integrity check value that follows it, in place, in the message buffer. |
| Encrypt_AES128CTRSHA1(msgInfo, sessionState.MsgEncKey->EncKey.AES128CTRSHA1.DataKey, p, |
| payloadLen + HMACSHA1::kDigestLength, p); |
| |
| // Compute the expected integrity check value from the decrypted payload. |
| uint8_t expectedIntegrityCheck[HMACSHA1::kDigestLength]; |
| ComputeIntegrityCheck_AES128CTRSHA1(msgInfo, sessionState.MsgEncKey->EncKey.AES128CTRSHA1.IntegrityKey, p, payloadLen, |
| expectedIntegrityCheck); |
| // Error if the expected integrity check doesn't match the integrity check in the message. |
| if (!ConstantTimeCompare(p + payloadLen, expectedIntegrityCheck, HMACSHA1::kDigestLength)) |
| return CHIP_ERROR_INTEGRITY_CHECK_FAILED; |
| // Skip past the payload and the integrity check value. |
| p += payloadLen + HMACSHA1::kDigestLength; |
| |
| break; |
| } |
| |
| default: |
| return CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE; |
| } |
| |
| // Set flag in the message header indicating that the message is a duplicate if: |
| // - A message with the same message identifier has already been received from that peer. |
| // - This is the first message from that peer encrypted with application keys. |
| if (sessionState.IsDuplicateMessage(msgInfo->MessageId)) |
| msgInfo->Flags |= kChipMessageFlag_DuplicateMessage; |
| |
| #if CHIP_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| // Set flag if peer group key message counter is not synchronized. |
| if (sessionState.MessageIdNotSynchronized() && ChipKeyId::IsAppGroupKey(msgInfo->KeyId)) |
| msgInfo->Flags |= kChipMessageFlag_PeerGroupMsgIdNotSynchronized; |
| #endif |
| |
| // Pass the peer authentication mode back to the application via the CHIP message header structure. |
| msgInfo->PeerAuthMode = sessionState.AuthMode; |
| |
| return err; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::EncodeMessageWithLength(ChipMessageInfo * msgInfo, PacketBuffer * msgBuf, ChipConnection * con, |
| uint16_t maxLen) |
| { |
| // Encode the message, reserving 2 bytes for the length. |
| CHIP_ERROR err = EncodeMessage(msgInfo, msgBuf, con, maxLen - 2, 2); |
| if (err != CHIP_NO_ERROR) |
| return err; |
| |
| // Prepend the message length to the beginning of the message. |
| uint8_t * newMsgStart = msgBuf->Start() - 2; |
| uint16_t msgLen = msgBuf->DataLength(); |
| msgBuf->SetStart(newMsgStart); |
| LittleEndian::Put16(newMsgStart, msgLen); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::DecodeMessageWithLength(PacketBuffer * msgBuf, uint64_t sourceNodeId, ChipConnection * con, |
| ChipMessageInfo * msgInfo, uint8_t ** rPayload, uint16_t * rPayloadLen, |
| uint32_t * rFrameLen) |
| { |
| uint8_t * dataStart = msgBuf->Start(); |
| uint16_t dataLen = msgBuf->DataLength(); |
| |
| // Error if the message buffer doesn't contain the entire message length field. |
| if (dataLen < 2) |
| { |
| *rFrameLen = 8; // Assume absolute minimum frame length. |
| return CHIP_ERROR_MESSAGE_INCOMPLETE; |
| } |
| |
| // Read the message length. |
| uint16_t msgLen = LittleEndian::Get16(dataStart); |
| |
| // The frame length is the length of the message plus the length of the length field. |
| *rFrameLen = static_cast<uint32_t>(msgLen) + 2; |
| |
| // Error if the message buffer doesn't contain the entire message, or is too |
| // long to ever fit in the buffer. |
| if (dataLen < *rFrameLen) |
| { |
| if (*rFrameLen > msgBuf->MaxDataLength() + msgBuf->ReservedSize()) |
| return CHIP_ERROR_MESSAGE_TOO_LONG; |
| return CHIP_ERROR_MESSAGE_INCOMPLETE; |
| } |
| |
| // Adjust the message buffer to point at the message, not including the message length field that precedes it, |
| // and not including any data that may follow it. |
| msgBuf->SetStart(dataStart + 2); |
| msgBuf->SetDataLength(msgLen); |
| |
| // Decode the message. |
| CHIP_ERROR err = DecodeMessage(msgBuf, sourceNodeId, con, msgInfo, rPayload, rPayloadLen); |
| |
| // If successful, adjust the message buffer to point at any remaining data beyond the end of the message. |
| // (This may in fact represent another message). |
| if (err == CHIP_NO_ERROR) |
| { |
| msgBuf->SetStart(dataStart + msgLen + 2); |
| msgBuf->SetDataLength(dataLen - (msgLen + 2)); |
| } |
| |
| // Otherwise, reset the buffer to its original position/length. |
| else |
| { |
| msgBuf->SetStart(dataStart); |
| msgBuf->SetDataLength(dataLen); |
| } |
| |
| return err; |
| } |
| |
| void ChipMessageLayer::HandleUDPMessage(UDPEndPoint * endPoint, PacketBuffer * msg, const IPPacketInfo * pktInfo) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| ChipMessageLayer * msgLayer = static_cast<ChipMessageLayer *>(endPoint->AppState); |
| ChipMessageInfo msgInfo; |
| uint64_t sourceNodeId; |
| uint8_t * payload; |
| uint16_t payloadLen; |
| |
| CHIP_FAULT_INJECT(FaultInjection::kFault_DropIncomingUDPMsg, PacketBuffer::Free(msg); ExitNow(err = CHIP_NO_ERROR)); |
| |
| msgInfo.Clear(); |
| msgInfo.InPacketInfo = pktInfo; |
| |
| // If the message was sent to an IPv6 multicast address, verify that the sending address matches |
| // one of the prefixes assigned to a local interface. If not, ignore the message and report a |
| // receive error to the application. |
| // |
| // Because the message was multicast, we will receive it regardless of what the sender's address is. |
| // However, if we don't have a local address in the same prefix, it won't be possible for us to |
| // respond. Furthermore, if we accept the message and then the sender retransmits it using a source |
| // prefix that DOES match one of our address, the latter message will be discarded as a duplicate, |
| // because we already accepted it when it was sent from the original address. |
| // |
| if (pktInfo->DestAddress.IsMulticast() && !msgLayer->Inet->MatchLocalIPv6Subnet(pktInfo->SrcAddress)) |
| err = CHIP_ERROR_INVALID_ADDRESS; |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| // If the source address is a ULA, derive a node identifier from it. Depending on what's in the |
| // message header, this may in fact be the node identifier of the sending node. |
| sourceNodeId = (pktInfo->SrcAddress.IsIPv6ULA()) ? IPv6InterfaceIdToChipNodeId(pktInfo->SrcAddress.InterfaceId()) |
| : kNodeIdNotSpecified; |
| |
| // Attempt to decode the message. |
| err = msgLayer->DecodeMessage(msg, sourceNodeId, nullptr, &msgInfo, &payload, &payloadLen); |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| // Set the message buffer to point at the payload data. |
| msg->SetStart(payload); |
| msg->SetDataLength(payloadLen); |
| } |
| } |
| |
| // Verify that destination node identifier refers to the local node. |
| if (err == CHIP_NO_ERROR) |
| { |
| if (msgInfo.DestNodeId != msgLayer->FabricState->LocalNodeId && msgInfo.DestNodeId != kAnyNodeId) |
| err = CHIP_ERROR_INVALID_DESTINATION_NODE_ID; |
| } |
| |
| // If an error occurred, discard the message and call the on receive error handler. |
| SuccessOrExit(err); |
| |
| // Record whether the message was sent to the local node's ephemeral port. |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| SetFlag(msgInfo.Flags, kChipMessageFlag_ViaEphemeralUDPPort, |
| (endPoint == msgLayer->mIPv6EphemeralUDP |
| #if INET_CONFIG_ENABLE_IPV4 |
| || endPoint == msgLayer->mIPv4EphemeralUDP |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| )); |
| #endif // CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| // Call the supplied OnMessageReceived callback. |
| if (msgLayer->OnMessageReceived != nullptr) |
| { |
| msgLayer->OnMessageReceived(msgLayer, &msgInfo, msg); |
| } |
| else |
| { |
| ExitNow(err = CHIP_ERROR_NO_MESSAGE_HANDLER); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(MessageLayer, "HandleUDPMessage Error %s", ErrorStr(err)); |
| |
| PacketBuffer::Free(msg); |
| |
| // Send key error response to the peer if required. |
| // Key error response is sent only if the received message is not a multicast. |
| if (!pktInfo->DestAddress.IsMulticast() && msgLayer->SecurityMgr->IsKeyError(err)) |
| msgLayer->SecurityMgr->SendKeyErrorMsg(&msgInfo, pktInfo, nullptr, err); |
| |
| if (msgLayer->OnReceiveError != nullptr) |
| msgLayer->OnReceiveError(msgLayer, err, pktInfo); |
| } |
| |
| return; |
| } |
| |
| void ChipMessageLayer::HandleUDPReceiveError(UDPEndPoint * endPoint, INET_ERROR err, const IPPacketInfo * pktInfo) |
| { |
| ChipLogError(MessageLayer, "HandleUDPReceiveError Error %s", ErrorStr(err)); |
| |
| ChipMessageLayer * msgLayer = static_cast<ChipMessageLayer *>(endPoint->AppState); |
| if (msgLayer->OnReceiveError != nullptr) |
| msgLayer->OnReceiveError(msgLayer, err, pktInfo); |
| } |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| void ChipMessageLayer::HandleIncomingBleConnection(BLEEndPoint * bleEP) |
| { |
| ChipMessageLayer * msgLayer = static_cast<ChipMessageLayer *>(bleEP->mAppState); |
| |
| // Immediately close the connection if there's no callback registered. |
| if (msgLayer->OnConnectionReceived == nullptr && msgLayer->ExchangeMgr == nullptr) |
| { |
| bleEP->Close(); |
| if (msgLayer->OnAcceptError != nullptr) |
| msgLayer->OnAcceptError(msgLayer, CHIP_ERROR_NO_CONNECTION_HANDLER); |
| return; |
| } |
| |
| // Attempt to allocate a connection object. Fail if too many connections. |
| ChipConnection * con = msgLayer->NewConnection(); |
| if (con == nullptr) |
| { |
| bleEP->Close(); |
| if (msgLayer->OnAcceptError != nullptr) |
| msgLayer->OnAcceptError(msgLayer, CHIP_ERROR_TOO_MANY_CONNECTIONS); |
| return; |
| } |
| |
| // Setup the connection object. |
| con->MakeConnectedBle(bleEP); |
| |
| #if CHIP_PROGRESS_LOGGING |
| { |
| ChipLogProgress(MessageLayer, "WoBle con rcvd"); |
| } |
| #endif |
| |
| // Set the default idle timeout. |
| con->SetIdleTimeout(msgLayer->IncomingConIdleTimeout); |
| |
| // Set incoming connection flag. |
| con->SetIncoming(true); |
| |
| // If the exchange manager has been initialized, call its callback. |
| if (msgLayer->ExchangeMgr != nullptr) |
| msgLayer->ExchangeMgr->HandleConnectionReceived(con); |
| |
| // Call the app's OnConnectionReceived callback. |
| if (msgLayer->OnConnectionReceived != nullptr) |
| msgLayer->OnConnectionReceived(msgLayer, con); |
| } |
| #endif /* CONFIG_NETWORK_LAYER_BLE */ |
| |
| void ChipMessageLayer::HandleIncomingTcpConnection(TCPEndPoint * listeningEP, TCPEndPoint * conEP, const IPAddress & peerAddr, |
| uint16_t peerPort) |
| { |
| INET_ERROR err; |
| IPAddress localAddr; |
| uint16_t localPort; |
| uint16_t incomingTCPConCount; |
| uint16_t incomingTCPConCountFromIP; |
| ChipMessageLayer * msgLayer = static_cast<ChipMessageLayer *>(listeningEP->AppState); |
| |
| // Immediately close the connection if there's no callback registered. |
| if (msgLayer->OnConnectionReceived == nullptr && msgLayer->ExchangeMgr == nullptr) |
| { |
| conEP->Free(); |
| if (msgLayer->OnAcceptError != nullptr) |
| msgLayer->OnAcceptError(msgLayer, CHIP_ERROR_NO_CONNECTION_HANDLER); |
| return; |
| } |
| |
| // Fail if too many incoming TCP connections. |
| msgLayer->GetIncomingTCPConCount(peerAddr, incomingTCPConCount, incomingTCPConCountFromIP); |
| if (incomingTCPConCount == CHIP_CONFIG_MAX_INCOMING_TCP_CONNECTIONS || |
| incomingTCPConCountFromIP == CHIP_CONFIG_MAX_INCOMING_TCP_CON_FROM_SINGLE_IP) |
| { |
| conEP->Free(); |
| if (msgLayer->OnAcceptError != nullptr) |
| msgLayer->OnAcceptError(msgLayer, CHIP_ERROR_TOO_MANY_CONNECTIONS); |
| return; |
| } |
| |
| // Attempt to allocate a connection object. Fail if too many connections. |
| ChipConnection * con = msgLayer->NewConnection(); |
| if (con == nullptr) |
| { |
| conEP->Free(); |
| if (msgLayer->OnAcceptError != nullptr) |
| msgLayer->OnAcceptError(msgLayer, CHIP_ERROR_TOO_MANY_CONNECTIONS); |
| return; |
| } |
| |
| // Get the local address that was used for the connection. |
| err = conEP->GetLocalInfo(&localAddr, &localPort); |
| if (err != INET_NO_ERROR) |
| { |
| conEP->Free(); |
| if (msgLayer->OnAcceptError != nullptr) |
| msgLayer->OnAcceptError(msgLayer, err); |
| return; |
| } |
| |
| // Setup the connection object. |
| con->MakeConnectedTcp(conEP, localAddr, peerAddr); |
| |
| #if CHIP_PROGRESS_LOGGING |
| { |
| char ipAddrStr[64]; |
| peerAddr.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| ChipLogProgress(MessageLayer, "Con %s %04" PRIX16 " %s %d", "rcvd", con->LogId(), ipAddrStr, (int) peerPort); |
| } |
| #endif |
| |
| // Set the default idle timeout. |
| con->SetIdleTimeout(msgLayer->IncomingConIdleTimeout); |
| |
| // Set incoming connection flag. |
| con->SetIncoming(true); |
| |
| // If the exchange manager has been initialized, call its callback. |
| if (msgLayer->ExchangeMgr != nullptr) |
| msgLayer->ExchangeMgr->HandleConnectionReceived(con); |
| |
| // Call the app's OnConnectionReceived callback. |
| if (msgLayer->OnConnectionReceived != nullptr) |
| msgLayer->OnConnectionReceived(msgLayer, con); |
| |
| // If connection was received on unsecured port, call the app's OnUnsecuredConnectionReceived callback. |
| if (msgLayer->OnUnsecuredConnectionReceived != nullptr && conEP->GetLocalInfo(&localAddr, &localPort) == CHIP_NO_ERROR && |
| localPort == CHIP_UNSECURED_PORT) |
| msgLayer->OnUnsecuredConnectionReceived(msgLayer, con); |
| } |
| |
| void ChipMessageLayer::HandleAcceptError(TCPEndPoint * ep, INET_ERROR err) |
| { |
| ChipMessageLayer * msgLayer = static_cast<ChipMessageLayer *>(ep->AppState); |
| if (msgLayer->OnAcceptError != nullptr) |
| msgLayer->OnAcceptError(msgLayer, err); |
| } |
| |
| /** |
| * Refresh the InetLayer endpoints based on the current state of the system's network interfaces. |
| * |
| * @note |
| * This function is designed to be called multiple times. The first call will setup all the |
| * TCP / UDP endpoints needed for the messaging layer to communicate, based on the specified |
| * configuration (i.e. IPv4 listen enabled, IPv6 listen enabled, etc.). Subsequent calls will |
| * re-initialize the active endpoints based on the current state of the system's network |
| * interfaces. |
| * |
| * @retval #CHIP_NO_ERROR on successful refreshing of endpoints. |
| * @retval InetLayer errors based on calls to create TCP/UDP endpoints. |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::RefreshEndpoints() |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| const bool listenIPv6 = IPv6ListenEnabled(); |
| #if INET_CONFIG_ENABLE_IPV4 |
| const bool listenIPv4 = IPv4ListenEnabled(); |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| #if CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| IPAddress & listenIPv6Addr = FabricState->ListenIPv6Addr; |
| InterfaceId listenIPv6Intf = INET_NULL_INTERFACEID; |
| #if INET_CONFIG_ENABLE_IPV4 |
| IPAddress & listenIPv4Addr = FabricState->ListenIPv4Addr; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| // If configured to use a specific IPv6 address, determine the interface associated |
| // with that address. Store it as the only interface in the interface list. |
| if (IsBoundToLocalIPv6Address()) |
| { |
| err = Inet->GetInterfaceFromAddr(listenIPv6Addr, listenIPv6Intf); |
| SuccessOrExit(err); |
| } |
| |
| #else // CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| IPAddress & listenIPv6Addr = IPAddress::Any; |
| InterfaceId listenIPv6Intf = INET_NULL_INTERFACEID; |
| #if INET_CONFIG_ENABLE_IPV4 |
| IPAddress & listenIPv4Addr = IPAddress::Any; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| #endif // CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| // ================================================================================ |
| // Enable / disable TCP listening endpoints... |
| // ================================================================================ |
| |
| { |
| const bool listenTCP = TCPListenEnabled(); |
| const bool listenIPv6TCP = (listenTCP && listenIPv6); |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| |
| const bool listenIPv4TCP = (listenTCP && listenIPv4); |
| |
| // Enable / disable the CHIP IPv4 TCP listening endpoint |
| // |
| // The CHIP IPv4 TCP listening endpoint is use to listen for incoming IPv4 TCP connections |
| // to the local node's CHIP port. |
| // |
| err = |
| RefreshEndpoint(mIPv4TCPListen, listenIPv4TCP, "CHIP IPv4 TCP listen", kIPAddressType_IPv4, listenIPv4Addr, CHIP_PORT); |
| SuccessOrExit(err); |
| |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| // Enable / disable the CHIP IPv6 TCP listening endpoint |
| // |
| // The CHIP IPv6 TCP listening endpoint is use to listen for incoming IPv6 TCP connections |
| // to the local node's CHIP port. |
| // |
| err = |
| RefreshEndpoint(mIPv6TCPListen, listenIPv6TCP, "CHIP IPv6 TCP listen", kIPAddressType_IPv6, listenIPv6Addr, CHIP_PORT); |
| SuccessOrExit(err); |
| |
| #if CHIP_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| |
| // Enable / disable the Unsecured IPv6 TCP listening endpoint |
| // |
| // The Unsecured IPv6 TCP listening endpoint is use to listen for incoming IPv6 TCP connections |
| // to the local node's unsecured CHIP port. This endpoint is only enabled if the unsecured TCP |
| // listen feature has been enabled. |
| // |
| const bool listenUnsecuredIPv6TCP = (listenIPv6TCP && UnsecuredListenEnabled()); |
| err = RefreshEndpoint(mUnsecuredIPv6TCPListen, listenUnsecuredIPv6TCP, "unsecured IPv6 TCP listen", kIPAddressType_IPv6, |
| listenIPv6Addr, CHIP_UNSECURED_PORT); |
| SuccessOrExit(err); |
| |
| #endif // CHIP_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| } |
| |
| // ================================================================================ |
| // Enable / disable UDP endpoints... |
| // ================================================================================ |
| |
| { |
| const bool listenUDP = UDPListenEnabled(); |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| |
| // Enabled / disable the CHIP IPv4 UDP endpoint as necessary. |
| // |
| // The CHIP IPv4 UDP endpoint is used to listen for unsolicited IPv4 UDP CHIP messages sent |
| // to the local node's CHIP port. Is is also used by the local node to send IPv4 UDP CHIP |
| // messages to other nodes, and to receive their replies, unless the outbound ephemeral UDP port |
| // feature has been enabled. |
| // |
| const bool listenIPv4UDP = (listenIPv4 && listenUDP); |
| err = RefreshEndpoint(mIPv4UDP, listenIPv4UDP, "CHIP IPv4 UDP", kIPAddressType_IPv4, listenIPv4Addr, CHIP_PORT, |
| INET_NULL_INTERFACEID); |
| SuccessOrExit(err); |
| |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| // Enabled / disable the ephemeral IPv4 UDP endpoint as necessary. |
| // |
| // The ephemeral IPv4 UDP endpoint is used to send IPv4 UDP CHIP messages to other nodes and to |
| // receive their replies. It is only enabled when the outbound ephemeral UDP port feature has been |
| // enabled. |
| // |
| const bool listenIPv4EphemeralUDP = (listenIPv4UDP && EphemeralUDPPortEnabled()); |
| err = RefreshEndpoint(mIPv4EphemeralUDP, listenIPv4EphemeralUDP, "ephemeral IPv4 UDP", kIPAddressType_IPv4, listenIPv4Addr, |
| 0, INET_NULL_INTERFACEID); |
| SuccessOrExit(err); |
| |
| #endif // CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| // Enabled / disable the CHIP IPv6 UDP endpoint as necessary. |
| // |
| // The CHIP IPv6 UDP endpoint is used to listen for unsolicited IPv6 UDP CHIP messages sent |
| // to the local node's CHIP port. Is is also used by the local node to send IPv6 UDP CHIP |
| // messages to other nodes, and to receive their replies, unless the outbound ephemeral UDP port |
| // feature has been enabled. |
| // |
| const bool listenIPv6UDP = (listenIPv6 && listenUDP); |
| err = RefreshEndpoint(mIPv6UDP, listenIPv6UDP, "CHIP IPv6 UDP", kIPAddressType_IPv6, listenIPv6Addr, CHIP_PORT, |
| listenIPv6Intf); |
| SuccessOrExit(err); |
| |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| // Enabled / disable the ephemeral IPv6 UDP endpoint as necessary. |
| // |
| // The ephemeral IPv6 UDP endpoint is used to send IPv6 UDP CHIP messages to other nodes and to |
| // receive their replies. It is only enabled when the outbound ephemeral UDP port feature has been |
| // enabled. |
| // |
| const bool listenIPv6EphemeralUDP = (listenIPv6UDP && EphemeralUDPPortEnabled()); |
| err = RefreshEndpoint(mIPv6EphemeralUDP, listenIPv6EphemeralUDP, "ephemeral IPv6 UDP", kIPAddressType_IPv6, listenIPv6Addr, |
| 0, listenIPv6Intf); |
| SuccessOrExit(err); |
| |
| #endif // CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| #if CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| // Enable / disable the CHIP IPv6 UDP multicast endpoint. |
| // |
| // The CHIP IPv6 UDP multicast endpoint is used to listen for unsolicited IPv6 UDP CHIP messages sent |
| // to the all-nodes, link-local multicast address. It is only enabled when the message layer has been bound |
| // to a specific IPv6 address. This is required because the CHIP IPv6 UDP endpoint will not receive multicast |
| // messages in this configuration. |
| // |
| IPAddress ipv6LinkLocalAllNodes = |
| IPAddress::MakeIPv6WellKnownMulticast(kIPv6MulticastScope_Link, kIPV6MulticastGroup_AllNodes); |
| const bool listenChipIPv6UDPMulticastEP = (listenIPv6UDP && IsBoundToLocalIPv6Address()); |
| err = RefreshEndpoint(mIPv6UDPMulticastRcv, listenChipIPv6UDPMulticastEP, "CHIP IPv6 UDP multicast", kIPAddressType_IPv6, |
| ipv6LinkLocalAllNodes, CHIP_PORT, listenIPv6Intf); |
| SuccessOrExit(err); |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| |
| // Enable / disable the CHIP IPv4 UDP broadcast endpoint. |
| // |
| // Similar to the IPv6 UDP multicast endpoint, this endpoint is used to receive IPv4 broadcast messages |
| // when the message layer has been bound to a specific IPv4 address. |
| // |
| IPAddress ipv4Broadcast = IPAddress::MakeIPv4Broadcast(); |
| const bool listenChipIPv4UDPBroadcastEP = (listenIPv4UDP && IsBoundToLocalIPv4Address()); |
| err = RefreshEndpoint(mIPv4UDPBroadcastRcv, listenChipIPv4UDPBroadcastEP, "CHIP IPv4 UDP broadcast", kIPAddressType_IPv4, |
| ipv4Broadcast, CHIP_PORT, INET_NULL_INTERFACEID); |
| SuccessOrExit(err); |
| |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| #endif // CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| ChipBindLog("RefreshEndpoints failed: %s", ErrorStr(err)); |
| return err; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::RefreshEndpoint(TCPEndPoint *& endPoint, bool enable, const char * name, IPAddressType addrType, |
| IPAddress addr, uint16_t port) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| // Release any existing endpoint as needed. |
| if (endPoint != nullptr && !enable) |
| { |
| endPoint->Free(); |
| endPoint = nullptr; |
| } |
| |
| // If needed, create and bind a new endpoint... |
| if (endPoint == nullptr && enable) |
| { |
| // Create a new TCP endpoint object. |
| err = Inet->NewTCPEndPoint(&endPoint); |
| SuccessOrExit(err); |
| |
| // Bind the endpoint to the given address, port and interface. |
| err = endPoint->Bind(addrType, addr, port, true); |
| SuccessOrExit(err); |
| |
| // Accept incoming TCP connections. |
| endPoint->AppState = this; |
| endPoint->OnConnectionReceived = HandleIncomingTcpConnection; |
| endPoint->OnAcceptError = HandleAcceptError; |
| err = endPoint->Listen(1); |
| SuccessOrExit(err); |
| |
| #if CHIP_BIND_DETAIL_LOGGING && CHIP_DETAIL_LOGGING |
| { |
| char ipAddrStr[64]; |
| addr.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| ChipBindLog("Listening on %s endpoint ([%s]:%" PRIu16 ")", name, ipAddrStr, port); |
| } |
| #endif // CHIP_BIND_DETAIL_LOGGING && CHIP_DETAIL_LOGGING |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| if (endPoint != nullptr) |
| { |
| endPoint->Free(); |
| endPoint = nullptr; |
| } |
| ChipLogError(MessageLayer, "Error initializing %s endpoint: %s", name, ErrorStr(err)); |
| } |
| return err; |
| } |
| |
| CHIP_ERROR ChipMessageLayer::RefreshEndpoint(UDPEndPoint *& endPoint, bool enable, const char * name, IPAddressType addrType, |
| IPAddress addr, uint16_t port, InterfaceId intfId) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| // Release any existing endpoint as needed. |
| if (endPoint != nullptr && (!enable || GetFlag(mFlags, kFlag_ForceRefreshUDPEndPoints))) |
| { |
| endPoint->Free(); |
| endPoint = nullptr; |
| } |
| |
| // If needed, create and bind a new endpoint... |
| if (endPoint == nullptr && enable) |
| { |
| // Create a new UDP endpoint object. |
| err = Inet->NewUDPEndPoint(&endPoint); |
| SuccessOrExit(err); |
| |
| // Bind the endpoint to the given address, port and interface. |
| err = endPoint->Bind(addrType, addr, port, intfId); |
| SuccessOrExit(err); |
| |
| // Accept incoming packets on the endpoint. |
| endPoint->AppState = this; |
| endPoint->OnMessageReceived = reinterpret_cast<IPEndPointBasis::OnMessageReceivedFunct>(HandleUDPMessage); |
| endPoint->OnReceiveError = reinterpret_cast<IPEndPointBasis::OnReceiveErrorFunct>(HandleUDPReceiveError); |
| err = endPoint->Listen(); |
| SuccessOrExit(err); |
| |
| #if CHIP_BIND_DETAIL_LOGGING && CHIP_DETAIL_LOGGING |
| { |
| char ipAddrStr[64]; |
| char intfStr[64]; |
| addr.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| if (intfId != INET_NULL_INTERFACEID) |
| { |
| intfStr[0] = '%'; |
| GetInterfaceName(intfId, intfStr + 1, sizeof(intfStr) - 1); |
| } |
| else |
| { |
| intfStr[0] = '\0'; |
| } |
| ChipBindLog("Listening on %s endpoint ([%s]:%" PRIu16 "%s)", name, ipAddrStr, endPoint->GetBoundPort(), intfStr); |
| } |
| #endif // CHIP_BIND_DETAIL_LOGGING && CHIP_DETAIL_LOGGING |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| if (endPoint != nullptr) |
| { |
| endPoint->Free(); |
| endPoint = nullptr; |
| } |
| ChipLogError(MessageLayer, "Error initializing %s endpoint: %s", name, ErrorStr(err)); |
| } |
| return err; |
| } |
| |
| void ChipMessageLayer::Encrypt_AES128CTRSHA1(const ChipMessageInfo * msgInfo, const uint8_t * key, const uint8_t * inData, |
| uint16_t inLen, uint8_t * outBuf) |
| { |
| AES128CTRMode aes128CTR; |
| aes128CTR.SetKey(key); |
| aes128CTR.SetChipMessageCounter(msgInfo->SourceNodeId, msgInfo->MessageId); |
| aes128CTR.EncryptData(inData, inLen, outBuf); |
| } |
| |
| void ChipMessageLayer::ComputeIntegrityCheck_AES128CTRSHA1(const ChipMessageInfo * msgInfo, const uint8_t * key, |
| const uint8_t * inData, uint16_t inLen, uint8_t * outBuf) |
| { |
| // TODO(#2093):: reimplement with chip crypto lib |
| #if 0 |
| HMACSHA1 hmacSHA1; |
| uint8_t encodedBuf[2 * sizeof(uint64_t) + sizeof(uint16_t) + sizeof(uint32_t)]; |
| uint8_t * p = encodedBuf; |
| |
| // Initialize HMAC Key. |
| hmacSHA1.Begin(key, ChipEncryptionKey_AES128CTRSHA1::IntegrityKeySize); |
| |
| // Encode the source and destination node identifiers in a little-endian format. |
| Encoding::LittleEndian::Write64(p, msgInfo->SourceNodeId); |
| Encoding::LittleEndian::Write64(p, msgInfo->DestNodeId); |
| |
| // Encode message header field value. |
| uint16_t headerField = EncodeHeaderField(msgInfo); |
| |
| // Mask destination and source node Id flags. |
| headerField &= kMsgHeaderField_MessageHMACMask; |
| |
| // Encode the message header field and the message Id in a little-endian format. |
| Encoding::LittleEndian::Write16(p, headerField); |
| Encoding::LittleEndian::Write32(p, msgInfo->MessageId); |
| |
| // Hash encoded message header fields. |
| hmacSHA1.AddData(encodedBuf, p - encodedBuf); |
| |
| // Handle payload data. |
| hmacSHA1.AddData(inData, inLen); |
| |
| // Generate the MAC. |
| hmacSHA1.Finish(outBuf); |
| #endif |
| } |
| |
| /** |
| * Close all open TCP and UDP endpoints. Then abort any |
| * open ChipConnections |
| * |
| * @note |
| * A call to CloseEndpoints() terminates all communication |
| * channels within the ChipMessageLayer but does not terminate |
| * the ChipMessageLayer object. |
| * |
| * @sa Shutdown(). |
| * |
| */ |
| CHIP_ERROR ChipMessageLayer::CloseEndpoints() |
| { |
| // Close all endpoints used for listening. |
| CloseListeningEndpoints(); |
| |
| // Abort any open connections. |
| ChipConnection * con = static_cast<ChipConnection *>(mConPool); |
| for (int i = 0; i < CHIP_CONFIG_MAX_CONNECTIONS; i++, con++) |
| if (con->mRefCount > 0) |
| con->Abort(); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void ChipMessageLayer::CloseListeningEndpoints() |
| { |
| ChipBindLog("Closing endpoints"); |
| |
| if (mIPv6TCPListen != nullptr) |
| { |
| mIPv6TCPListen->Free(); |
| mIPv6TCPListen = nullptr; |
| } |
| |
| if (mIPv6UDP != nullptr) |
| { |
| mIPv6UDP->Free(); |
| mIPv6UDP = nullptr; |
| } |
| |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| if (mIPv6EphemeralUDP != nullptr) |
| { |
| mIPv6EphemeralUDP->Free(); |
| mIPv6EphemeralUDP = nullptr; |
| } |
| |
| #endif // CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| #if CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| if (mIPv6UDPMulticastRcv != nullptr) |
| { |
| mIPv6UDPMulticastRcv->Free(); |
| mIPv6UDPMulticastRcv = nullptr; |
| } |
| |
| #endif // CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| #if CHIP_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| |
| if (mUnsecuredIPv6TCPListen != NULL) |
| { |
| mUnsecuredIPv6TCPListen->Free(); |
| mUnsecuredIPv6TCPListen = NULL; |
| } |
| |
| #endif // CHIP_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| |
| if (mIPv4TCPListen != nullptr) |
| { |
| mIPv4TCPListen->Free(); |
| mIPv4TCPListen = nullptr; |
| } |
| |
| if (mIPv4UDP != nullptr) |
| { |
| mIPv4UDP->Free(); |
| mIPv4UDP = nullptr; |
| } |
| |
| #if CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| if (mIPv4EphemeralUDP != nullptr) |
| { |
| mIPv4EphemeralUDP->Free(); |
| mIPv4EphemeralUDP = nullptr; |
| } |
| |
| #endif // CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT |
| |
| #if CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| if (mIPv4UDPBroadcastRcv != nullptr) |
| { |
| mIPv4UDPBroadcastRcv->Free(); |
| mIPv4UDPBroadcastRcv = nullptr; |
| } |
| |
| #endif // CHIP_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| } |
| |
| CHIP_ERROR ChipMessageLayer::EnableUnsecuredListen() |
| { |
| // Enable reception of connections on the unsecured CHIP port. This allows devices to establish |
| // a connection while provisionally connected (i.e. without security) at the network layer. |
| SetFlag(mFlags, kFlag_ListenUnsecured); |
| return RefreshEndpoints(); |
| } |
| |
| CHIP_ERROR ChipMessageLayer::DisableUnsecuredListen() |
| { |
| ClearFlag(mFlags, kFlag_ListenUnsecured); |
| return RefreshEndpoints(); |
| } |
| |
| /** |
| * Set an application handler that will get called every time the |
| * activity of the message layer changes. |
| * Specifically, application will be notified every time: |
| * - the number of opened exchanges changes. |
| * - the number of pending message counter synchronization requests |
| * changes from zero to at least one and back to zero. |
| * The handler is served as general signal indicating whether there |
| * are any ongoing CHIP conversations or pending responses. |
| * The handler must be set after the ChipMessageLayer has been initialized; |
| * shutting down the ChipMessageLayer will clear out the current handler. |
| * |
| * @param[in] messageLayerActivityChangeHandler A pointer to a function to |
| * be called whenever the message layer activity changes. |
| * |
| * @retval None. |
| */ |
| void ChipMessageLayer::SetSignalMessageLayerActivityChanged( |
| MessageLayerActivityChangeHandlerFunct messageLayerActivityChangeHandler) |
| { |
| OnMessageLayerActivityChange = messageLayerActivityChangeHandler; |
| } |
| |
| bool ChipMessageLayer::IsMessageLayerActive() |
| { |
| return (ExchangeMgr->mContextsInUse != 0) |
| #if CHIP_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| || FabricState->IsMsgCounterSyncReqInProgress() |
| #endif |
| ; |
| } |
| |
| /** |
| * This method is called every time the message layer activity changes. |
| * Specifically, it will be called every time: |
| * - the number of opened exchanges changes. |
| * - the number of pending message counter synchronization requests |
| * changes from zero to at least one and back to zero. |
| * New events can be added to this list in the future as needed. |
| * |
| * @retval None. |
| */ |
| void ChipMessageLayer::SignalMessageLayerActivityChanged() |
| { |
| if (OnMessageLayerActivityChange) |
| { |
| bool messageLayerIsActive = IsMessageLayerActive(); |
| OnMessageLayerActivityChange(messageLayerIsActive); |
| } |
| } |
| |
| /** |
| * Get the max CHIP payload size for a message configuration and supplied |
| * PacketBuffer. |
| * |
| * The maximum payload size returned will not exceed the available space |
| * for a payload inside the supplied PacketBuffer. |
| * |
| * If the message is UDP, the maximum payload size returned will not result in |
| * a CHIP message that will not overflow the specified UDP MTU. |
| * |
| * Finally, the maximum payload size returned will not result in a CHIP |
| * message that will overflow the max CHIP message size. |
| * |
| * @param[in] msgBuf A pointer to the PacketBuffer to which the message |
| * payload will be written. |
| * |
| * @param[in] isUDP True if message is a UDP message. |
| * |
| * @param[in] udpMTU The size of the UDP MTU. Ignored if isUDP is false. |
| * |
| * @return the max CHIP payload size. |
| */ |
| uint32_t ChipMessageLayer::GetMaxChipPayloadSize(const PacketBuffer * msgBuf, bool isUDP, uint32_t udpMTU) |
| { |
| uint32_t maxChipMessageSize = isUDP ? udpMTU - INET_CONFIG_MAX_IP_AND_UDP_HEADER_SIZE : UINT16_MAX; |
| uint32_t maxChipPayloadSize = maxChipMessageSize - CHIP_HEADER_RESERVE_SIZE - CHIP_TRAILER_RESERVE_SIZE; |
| uint32_t maxBufferablePayloadSize = msgBuf->AvailableDataLength() - CHIP_TRAILER_RESERVE_SIZE; |
| |
| return maxBufferablePayloadSize < maxChipPayloadSize ? maxBufferablePayloadSize : maxChipPayloadSize; |
| } |
| |
| /** |
| * Constructs a string describing a peer node and its associated address / connection information. |
| * |
| * The generated string has the following format: |
| * |
| * <node-id> ([<ip-address>]:<port>%<interface>, con <con-id>) |
| * |
| * @param[in] buf A pointer to a buffer into which the string should be written. The supplied |
| * buffer should be at least as big as kChipPeerDescription_MaxLength. If a |
| * smaller buffer is given the string will be truncated to fit. The output |
| * will include a NUL termination character in all cases. |
| * @param[in] bufSize The size of the buffer pointed at by buf. |
| * @param[in] nodeId The node id to be printed. |
| * @param[in] addr A pointer to an IP address to be printed; or NULL if no IP address should |
| * be printed. |
| * @param[in] port An IP port number to be printed. No port number will be printed if addr |
| * is NULL. |
| * @param[in] interfaceId An InterfaceId identifying the interface to be printed. The output string |
| * will contain the name of the interface as known to the underlying network |
| * stack. No interface name will be printed if interfaceId is INET_NULL_INTERFACEID |
| * or if addr is NULL. |
| * @param[in] con A pointer to a ChipConnection object whose logging id should be printed; |
| * or NULL if no connection id should be printed. |
| */ |
| void ChipMessageLayer::GetPeerDescription(char * buf, size_t bufSize, uint64_t nodeId, const IPAddress * addr, uint16_t port, |
| InterfaceId interfaceId, const ChipConnection * con) |
| { |
| enum |
| { |
| kMaxInterfaceNameLength = 20 |
| }; // Arbitrarily capped at 20 characters so long interface |
| // names do not blow out the available space. |
| |
| uint32_t len; |
| const char * sep = ""; |
| |
| if (nodeId != kNodeIdNotSpecified) |
| { |
| len = snprintf(buf, bufSize, "%" PRIX64 " (", nodeId); |
| } |
| else |
| { |
| len = snprintf(buf, bufSize, "unknown ("); |
| } |
| VerifyOrExit(len < bufSize, /* no-op */); |
| |
| if (addr != nullptr) |
| { |
| buf[len++] = '['; |
| VerifyOrExit(len < bufSize, /* no-op */); |
| |
| addr->ToString(buf + len, bufSize - len); |
| len = strlen(buf); |
| |
| if (port > 0) |
| { |
| len += snprintf(buf + len, bufSize - len, "]:%" PRIu16, port); |
| } |
| else |
| { |
| len += snprintf(buf + len, bufSize - len, "]"); |
| } |
| VerifyOrExit(len < bufSize, /* no-op */); |
| |
| if (interfaceId != INET_NULL_INTERFACEID) |
| { |
| char interfaceName[kMaxInterfaceNameLength + 1]; |
| Inet::GetInterfaceName(interfaceId, interfaceName, sizeof(interfaceName)); |
| interfaceName[kMaxInterfaceNameLength] = 0; |
| len += snprintf(buf + len, bufSize - len, "%%%s", interfaceName); |
| VerifyOrExit(len < bufSize, /* no-op */); |
| } |
| |
| sep = ", "; |
| } |
| |
| if (con != nullptr) |
| { |
| const char * conType; |
| switch (con->NetworkType) |
| { |
| case ChipConnection::kNetworkType_BLE: |
| conType = "ble "; |
| break; |
| case ChipConnection::kNetworkType_IP: |
| default: |
| conType = ""; |
| break; |
| } |
| |
| len += snprintf(buf + len, bufSize - len, "%s%scon %04" PRIX16, sep, conType, con->LogId()); |
| VerifyOrExit(len < bufSize, /* no-op */); |
| } |
| |
| snprintf(buf + len, bufSize - len, ")"); |
| |
| exit: |
| if (bufSize > 0) |
| { |
| buf[bufSize - 1] = 0; |
| } |
| return; |
| } |
| |
| /** |
| * Constructs a string describing a peer node based on the information associated with a message received from the peer. |
| * |
| * @param[in] buf A pointer to a buffer into which the string should be written. The supplied |
| * buffer should be at least as big as kChipPeerDescription_MaxLength. If a |
| * smaller buffer is given the string will be truncated to fit. The output |
| * will include a NUL termination character in all cases. |
| * @param[in] bufSize The size of the buffer pointed at by buf. |
| * @param[in] msgInfo A pointer to a ChipMessageInfo structure containing information about the message. |
| * |
| */ |
| void ChipMessageLayer::GetPeerDescription(char * buf, size_t bufSize, const ChipMessageInfo * msgInfo) |
| { |
| GetPeerDescription( |
| buf, bufSize, msgInfo->SourceNodeId, (msgInfo->InPacketInfo != nullptr) ? &msgInfo->InPacketInfo->SrcAddress : nullptr, |
| (msgInfo->InPacketInfo != nullptr) ? msgInfo->InPacketInfo->SrcPort : 0, |
| (msgInfo->InPacketInfo != nullptr) ? msgInfo->InPacketInfo->Interface : INET_NULL_INTERFACEID, msgInfo->InCon); |
| } |
| |
| /** |
| * @brief |
| * Generate random CHIP node Id. |
| * |
| * @details |
| * This function generates 64-bit locally unique CHIP node Id. This function uses cryptographically strong |
| * random data source to guarantee uniqueness of generated value. Note that bit 57 of the generated CHIP |
| * node Id is set to 1 to indicate that generated CHIP node Id is locally (not globally) unique. |
| * |
| * @param nodeId A reference to the 64-bit CHIP node Id. |
| * |
| * @retval #CHIP_NO_ERROR If CHIP node Id was successfully generated. |
| */ |
| DLL_EXPORT CHIP_ERROR GenerateChipNodeId(uint64_t & nodeId) |
| { |
| CHIP_ERROR err; |
| uint64_t id = 0; |
| |
| while (id <= kMaxAlwaysLocalChipNodeId) |
| { |
| err = chip::Platform::Security::GetSecureRandomData(reinterpret_cast<uint8_t *>(&id), sizeof(id)); |
| SuccessOrExit(err); |
| |
| id &= ~kEUI64_UL_Local; |
| } |
| |
| nodeId = id | kEUI64_UL_Local; |
| |
| exit: |
| return err; |
| } |
| |
| } // namespace chip |