| /* |
| * |
| * 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 WeaveMessageLayer class. It manages communication |
| * with other Weave nodes by employing one of several Inetlayer endpoints |
| * to establish a communication channel with other Weave nodes. |
| * |
| */ |
| |
| #ifndef __STDC_FORMAT_MACROS |
| #define __STDC_FORMAT_MACROS |
| #endif |
| |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include <Weave/Core/WeaveMessageLayer.h> |
| #include <Weave/Core/WeaveExchangeMgr.h> |
| #include <Weave/Core/WeaveEncoding.h> |
| #include <Weave/Support/crypto/WeaveCrypto.h> |
| #include <Weave/Support/crypto/HashAlgos.h> |
| #include <Weave/Support/crypto/HMAC.h> |
| #include <Weave/Support/crypto/AESBlockCipher.h> |
| #include <Weave/Support/crypto/CTRMode.h> |
| #include <Weave/Support/logging/WeaveLogging.h> |
| #include <Weave/Support/ErrorStr.h> |
| #include <Weave/Support/CodeUtils.h> |
| #include <Weave/Support/WeaveFaultInjection.h> |
| |
| |
| namespace nl { |
| namespace Weave { |
| |
| using namespace nl::Weave::Crypto; |
| using namespace nl::Weave::Encoding; |
| |
| /** |
| * @def WEAVE_BIND_DETAIL_LOGGING |
| * |
| * @brief |
| * Use Weave Bind detailed logging for Weave communication. |
| * |
| */ |
| #ifndef WEAVE_BIND_DETAIL_LOGGING |
| #define WEAVE_BIND_DETAIL_LOGGING 1 |
| #endif |
| |
| /** |
| * @def WeaveBindLog(MSG, ...) |
| * |
| * @brief |
| * Define WeaveBindLogic to be the same as WeaveLogProgress based on |
| * whether both #WEAVE_BIND_DETAIL_LOGGING and #WEAVE_DETAIL_LOGGING |
| * are set. |
| * |
| */ |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| #define WeaveBindLog(MSG, ...) WeaveLogProgress(MessageLayer, MSG, ## __VA_ARGS__ ) |
| #else |
| #define WeaveBindLog(MSG, ...) |
| #endif |
| |
| |
| enum |
| { |
| kKeyIdLen = 2, |
| kMinPayloadLen = 1 |
| }; |
| |
| /** |
| * The Weave Message layer constructor. |
| * |
| * @note |
| * The class must be initialized via WeaveMessageLayer::Init() |
| * prior to use. |
| * |
| */ |
| WeaveMessageLayer::WeaveMessageLayer() |
| { |
| State = kState_NotInitialized; |
| } |
| |
| /** |
| * Initialize the Weave Message layer object. |
| * |
| * @param[in] context A pointer to the InitContext object. |
| * |
| * @retval #WEAVE_NO_ERROR on successful initialization. |
| * @retval #WEAVE_ERROR_INVALID_ARGUMENT if the passed InitContext object is NULL. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE if the state of the WeaveMessageLayer object is incorrect. |
| * @retval other errors generated from the lower Inet layer during endpoint creation. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::Init(InitContext *context) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| |
| if (context == NULL) |
| return WEAVE_ERROR_INVALID_ARGUMENT; |
| |
| if (State != kState_NotInitialized) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| SystemLayer = context->systemLayer; |
| Inet = context->inet; |
| #if CONFIG_NETWORK_LAYER_BLE |
| mBle = context->ble; |
| #endif |
| |
| #if WEAVE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES |
| if (SystemLayer == NULL) |
| { |
| SystemLayer = Inet->SystemLayer(); |
| } |
| #endif // WEAVE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES |
| |
| FabricState = context->fabricState; |
| FabricState->MessageLayer = this; |
| OnMessageReceived = NULL; |
| OnReceiveError = NULL; |
| OnConnectionReceived = NULL; |
| OnUnsecuredConnectionReceived = NULL; |
| OnUnsecuredConnectionCallbacksRemoved = NULL; |
| OnAcceptError = NULL; |
| OnMessageLayerActivityChange = NULL; |
| memset(mConPool, 0, sizeof(mConPool)); |
| memset(mTunnelPool, 0, sizeof(mTunnelPool)); |
| AppState = NULL; |
| ExchangeMgr = NULL; |
| SecurityMgr = NULL; |
| IsListening = context->listenTCP || context->listenUDP; |
| IncomingConIdleTimeout = 0; |
| |
| //Internal and for Debug Only; When set, Message Layer drops message and returns. |
| mDropMessage = false; |
| mFlags = 0; |
| if (context->listenTCP) |
| mFlags |= kFlag_ListenTCP; |
| if (context->listenUDP) |
| mFlags |= kFlag_ListenUDP; |
| #if CONFIG_NETWORK_LAYER_BLE |
| if (context->listenBLE) |
| mFlags |= kFlag_ListenBLE; |
| #endif |
| |
| #if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| #if INET_CONFIG_ENABLE_IPV4 |
| if (FabricState->ListenIPv6Addr != IPAddress::Any) |
| mFlags |= kFlag_ListenIPv6; |
| if (FabricState->ListenIPv4Addr != IPAddress::Any) |
| mFlags |= kFlag_ListenIPv4; |
| if ((mFlags & (kFlag_ListenIPv4 | kFlag_ListenIPv6)) == 0) |
| mFlags |= kFlag_ListenIPv4 | kFlag_ListenIPv6; |
| #else // !INET_CONFIG_ENABLE_IPV4 |
| mFlags |= kFlag_ListenIPv6; |
| #endif // !INET_CONFIG_ENABLE_IPV4 |
| #else // !WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| mFlags |= kFlag_ListenIPv6; |
| #if INET_CONFIG_ENABLE_IPV4 |
| mFlags |= kFlag_ListenIPv4; |
| #endif // !INET_CONFIG_ENABLE_IPV4 |
| #endif // !WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| mIPv6TCPListen = NULL; |
| mIPv6UDP = NULL; |
| #if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| mIPv6UDPMulticastRcv = NULL; |
| #endif |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| mIPv4TCPListen = NULL; |
| mIPv4UDP = NULL; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| #if WEAVE_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| mUnsecuredIPv6TCPListen = NULL; |
| #endif |
| memset(mIPv6UDPLocalAddr, 0, sizeof(mIPv6UDPLocalAddr)); |
| memset(mInterfaces, 0, sizeof(mInterfaces)); |
| |
| res = RefreshEndpoints(); |
| if (res != WEAVE_NO_ERROR) |
| goto done; |
| |
| done: |
| if (res != WEAVE_NO_ERROR) |
| Shutdown(); |
| else |
| State = kState_Initialized; |
| |
| return res; |
| } |
| |
| /** |
| * Shutdown the WeaveMessageLayer. |
| * |
| * Close all open Inet layer endpoints, reset all |
| * higher layer callbacks, member variables and objects. |
| * A call to Shutdown() terminates the WeaveMessageLayer |
| * object. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::Shutdown() |
| { |
| CloseEndpoints(); |
| |
| State = kState_NotInitialized; |
| IsListening = false; |
| FabricState = NULL; |
| OnMessageReceived = NULL; |
| OnReceiveError = NULL; |
| OnUnsecuredConnectionReceived = NULL; |
| OnConnectionReceived = NULL; |
| OnAcceptError = NULL; |
| OnMessageLayerActivityChange = NULL; |
| memset(mConPool, 0, sizeof(mConPool)); |
| memset(mTunnelPool, 0, sizeof(mTunnelPool)); |
| ExchangeMgr = NULL; |
| AppState = NULL; |
| mFlags = 0; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| #if WEAVE_CONFIG_ENABLE_TUNNELING |
| /** |
| * Send a tunneled IPv6 data message over UDP. |
| * |
| * @param[in] msgInfo A pointer to a WeaveMessageInfo object. |
| * |
| * @param[in] destAddr IPAddress of the UDP tunnel destination. |
| * |
| * @param[in] msgBuf A pointer to the PacketBuffer object holding the packet to send. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network |
| * layer. |
| * @retval #WEAVE_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. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::SendUDPTunneledMessage(const IPAddress &destAddr, WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| |
| //Set message version to V2 |
| msgInfo->MessageVersion = kWeaveMessageVersion_V2; |
| |
| //Set the tunneling flag |
| msgInfo->Flags |= kWeaveMessageFlag_TunneledData; |
| |
| res = SendMessage(destAddr, msgInfo, msgBuf); |
| msgBuf = NULL; |
| |
| return res; |
| } |
| #endif // WEAVE_CONFIG_ENABLE_TUNNELING |
| |
| /** |
| * Encode a Weave Message layer header into an PacketBuffer. |
| * |
| * @param[in] destAddr The destination IP Address. |
| * |
| * @param[in] destPort The destination port. |
| * |
| * @param[in] sendIntfId The interface on which to send the Weave message. |
| * |
| * @param[in] msgInfo A pointer to a WeaveMessageInfo object. |
| * |
| * @param[in] msgBuf A pointer to the PacketBuffer object that would hold the Weave message. |
| * |
| * @retval #WEAVE_NO_ERROR on successful encoding of the Weave message. |
| * @retval #WEAVE_ERROR_UNSUPPORTED_MESSAGE_VERSION if the Weave Message version is not supported. |
| * @retval #WEAVE_ERROR_INVALID_MESSAGE_LENGTH if the payload length in the message buffer is zero. |
| * @retval #WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE if the encryption type is not supported. |
| * @retval #WEAVE_ERROR_MESSAGE_TOO_LONG if the encoded message would be longer than the |
| * requested maximum. |
| * @retval #WEAVE_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. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::EncodeMessage(const IPAddress &destAddr, uint16_t destPort, InterfaceId sendIntId, |
| WeaveMessageInfo *msgInfo, PacketBuffer *payload) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| |
| // Set the source node identifier in the message header. |
| if ((msgInfo->Flags & kWeaveMessageFlag_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 |= kWeaveMessageFlag_SourceNodeId; |
| |
| // Force the destination node identifier to be included if it doesn't match the interface identifier in |
| // the destination address. |
| if (!destAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(destAddr.InterfaceId()) != msgInfo->DestNodeId) |
| msgInfo->Flags |= kWeaveMessageFlag_DestNodeId; |
| |
| // Encode the Weave message. NOTE that this results in the payload buffer containing the entire encoded message. |
| res = EncodeMessage(msgInfo, payload, NULL, UINT16_MAX, 0); |
| |
| return res; |
| } |
| |
| /** |
| * Send a Weave message using the underlying Inetlayer UDP endpoint after encoding it. |
| * |
| * @note |
| * The destination port used is #WEAVE_PORT. |
| * |
| * @param[in] msgInfo A pointer to a WeaveMessageInfo object containing information |
| * about the message to be sent. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the |
| * encoded Weave message. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::SendMessage(WeaveMessageInfo *msgInfo, PacketBuffer *payload) |
| { |
| return SendMessage(IPAddress::Any, msgInfo, payload); |
| } |
| |
| /** |
| * Send a Weave message using the underlying Inetlayer UDP endpoint after encoding it. |
| * |
| * @note |
| * -The destination port used is #WEAVE_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 WeaveMessageInfo object containing information |
| * about the message to be sent. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the |
| * encoded Weave message. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::SendMessage(const IPAddress &destAddr, WeaveMessageInfo *msgInfo, |
| PacketBuffer *payload) |
| { |
| return SendMessage(destAddr, WEAVE_PORT, INET_NULL_INTERFACEID, msgInfo, payload); |
| } |
| |
| /** |
| * Send a Weave 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] destAddr The destination IP Address. |
| * |
| * @param[in] destPort The destination port. |
| * |
| * @param[in] sendIntfId The interface on which to send the Weave message. |
| * |
| * @param[in] msgInfo A pointer to a WeaveMessageInfo object containing information |
| * about the message to be sent. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the |
| * encoded Weave message. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network |
| * layer. |
| * @retval #WEAVE_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. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::SendMessage(const IPAddress &aDestAddr, uint16_t destPort, InterfaceId sendIntfId, |
| WeaveMessageInfo *msgInfo, PacketBuffer *payload) |
| { |
| WEAVE_ERROR res = WEAVE_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 & kWeaveMessageFlag_DelaySend) |
| return WEAVE_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 != WEAVE_NO_ERROR) && |
| (payload != NULL) && |
| ((msgInfo->Flags & kWeaveMessageFlag_RetainBuffer) == 0)) |
| { |
| PacketBuffer::Free(payload); |
| } |
| |
| return res; |
| } |
| |
| bool WeaveMessageLayer::IsIgnoredMulticastSendError(WEAVE_ERROR err) |
| { |
| return err == WEAVE_NO_ERROR || |
| |
| // Ignore routing errors. In general, these indicate that the underly interface |
| // doesn't support multicast (e.g. the loopback interface on linux) or that the |
| // inteface isn't fully configured (e.g. we're bound to an address on the interface |
| // but the address hasn't finished DAD). |
| #if WEAVE_SYSTEM_CONFIG_USE_LWIP |
| err == System::MapErrorLwIP(ERR_RTE) |
| #else |
| err == System::MapErrorPOSIX(ENETUNREACH) || err == System::MapErrorPOSIX(EADDRNOTAVAIL) |
| #endif |
| ; |
| } |
| |
| /** |
| * Checks if error, while sending, is critical enough to report to the application. |
| * |
| * @param[in] err The #WEAVE_ERROR being checked for criticality. |
| * |
| * @return true if the error is NOT critical; false otherwise. |
| * |
| */ |
| bool WeaveMessageLayer::IsSendErrorNonCritical (WEAVE_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 || |
| WEAVE_CONFIG_IsPlatformErrorNonCritical(err)); |
| } |
| |
| /** |
| * Send an encoded Weave 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 Weave message. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded Weave message. |
| * |
| * @param[in] msgSendFlags Send flags containing metadata about the message for the lower Inet layer. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::SendMessage(const IPAddress &destAddr, uint16_t destPort, InterfaceId sendIntfId, |
| PacketBuffer *payload, uint16_t msgSendFlags) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| WEAVE_ERROR mcastSendErr = WEAVE_NO_ERROR; |
| uint16_t udpSendFlags = (msgSendFlags & kWeaveMessageFlag_RetainBuffer) ? UDPEndPoint::kSendFlag_RetainBuffer : 0; |
| UDPEndPoint* lUDP; |
| bool lIsNotIPv6Multicast; |
| |
| //Check if drop flag is set; If so, do not send message; return WEAVE_NO_ERROR; |
| VerifyOrExit(!mDropMessage, err = WEAVE_NO_ERROR); |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| if (destAddr.IsIPv4()) |
| { |
| lUDP = mIPv4UDP; |
| lIsNotIPv6Multicast = true; |
| } |
| else |
| { |
| lUDP = mIPv6UDP; |
| lIsNotIPv6Multicast = !destAddr.IsMulticast(); |
| } |
| #else // !INET_CONFIG_ENABLE_IPV4 |
| lUDP = mIPv6UDP; |
| lIsNotIPv6Multicast = !destAddr.IsMulticast(); |
| #endif // !INET_CONFIG_ENABLE_IPV4 |
| |
| VerifyOrExit(lUDP != NULL, err = WEAVE_ERROR_NO_ENDPOINT); |
| |
| // If sending to a unicast IPv6 destination or an IPv4 destination |
| if (lIsNotIPv6Multicast) |
| { |
| // Send use the general-purpose IPv6 endpoint |
| err = lUDP->SendTo(destAddr, WEAVE_PORT, sendIntfId, payload, udpSendFlags); |
| payload = NULL; |
| } |
| |
| // Otherwise we're sending to a multicast IPv6 destination... |
| else |
| { |
| // Since we will be sending over multiple endpoints, ensure that the Inet layer code makes |
| // a copy of the message when sending. We'll take care of freeing the original when we're |
| // done. |
| udpSendFlags |= UDPEndPoint::kSendFlag_RetainBuffer; |
| |
| // If requested, send the multicast message over all interfaces using the appropriate IPv6 |
| // source link-local addresses for each interface... |
| // |
| // NOTE: In the case where we are configured to use a specific listening address (i.e. |
| // FabricState->ListenIPv6Addr != IPAddress::Any) this code will actually end up sending |
| // the message using the listening address as the source address. Since specifying a |
| // listening address is primarily used for simulating multiple Weave nodes on a single |
| // host, and there's no reasonable way for multiple nodes to share a single link-local |
| // address, this limitation is deemed acceptable. |
| // |
| if ((IsBoundToLocalIPv6Address()) || (msgSendFlags & kWeaveMessageFlag_MulticastFromLinkLocal) != 0) |
| { |
| // Send the message over each local interface or the interface passed as argument |
| // using the link-local address of the interface as the src address. |
| if (sendIntfId == INET_NULL_INTERFACEID) |
| { |
| for (uint16_t i = 0; i < WEAVE_CONFIG_MAX_INTERFACES; i++) |
| { |
| if (mInterfaces[i] != INET_NULL_INTERFACEID) |
| { |
| mcastSendErr = lUDP->SendTo(destAddr, WEAVE_PORT, mInterfaces[i], payload, udpSendFlags); |
| if (!IsIgnoredMulticastSendError(mcastSendErr)) |
| { |
| err = mcastSendErr; |
| } |
| } |
| } |
| } |
| else |
| { |
| mcastSendErr = lUDP->SendTo(destAddr, WEAVE_PORT, sendIntfId, payload, udpSendFlags); |
| if (!IsIgnoredMulticastSendError(mcastSendErr)) |
| err = mcastSendErr; |
| |
| } |
| } |
| |
| // Otherwise, send the multicast message over all interfaces, generating a distinct message for each |
| // bound address assigned to the interface... |
| else |
| { |
| // Send the message over each interface or the interface passed as argument |
| // using a ULA address as the src address. |
| for (uint16_t i = 0; i < WEAVE_CONFIG_MAX_LOCAL_ADDR_UDP_ENDPOINTS; i++) |
| { |
| if (mIPv6UDPLocalAddr[i] != NULL) |
| { |
| if (sendIntfId == INET_NULL_INTERFACEID) |
| { |
| mcastSendErr = mIPv6UDPLocalAddr[i]->SendTo(destAddr, WEAVE_PORT, sendIntfId, payload, udpSendFlags); |
| if (!IsIgnoredMulticastSendError(mcastSendErr)) |
| { |
| err = mcastSendErr; |
| } |
| |
| } |
| else |
| { |
| if (sendIntfId == mIPv6UDPLocalAddr[i]->GetBoundInterface()) |
| { |
| mcastSendErr = mIPv6UDPLocalAddr[i]->SendTo(destAddr, WEAVE_PORT, sendIntfId, payload, udpSendFlags); |
| if (!IsIgnoredMulticastSendError(mcastSendErr)) |
| { |
| err = mcastSendErr; |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| exit: |
| if (payload != NULL && (msgSendFlags & kWeaveMessageFlag_RetainBuffer) == 0) |
| PacketBuffer::Free(payload); |
| return err; |
| } |
| |
| /** |
| * Resend an encoded Weave message using the underlying Inetlayer UDP endpoint. |
| * |
| * @param[in] msgInfo A pointer to the WeaveMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded Weave message. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::ResendMessage(WeaveMessageInfo *msgInfo, PacketBuffer *payload) |
| { |
| IPAddress destAddr = IPAddress::Any; |
| return ResendMessage(destAddr, msgInfo, payload); |
| } |
| |
| /** |
| * Resend an encoded Weave message using the underlying Inetlayer UDP endpoint. |
| * |
| * @note |
| * The destination port used is #WEAVE_PORT. |
| * |
| * @param[in] destAddr The destination IP Address. |
| * |
| * @param[in] msgInfo A pointer to the WeaveMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded Weave message. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::ResendMessage(const IPAddress &destAddr, WeaveMessageInfo *msgInfo, PacketBuffer *payload) |
| { |
| return ResendMessage(destAddr, WEAVE_PORT, msgInfo, payload); |
| } |
| |
| /** |
| * Resend an encoded Weave 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 WeaveMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded Weave message. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::ResendMessage(const IPAddress &destAddr, uint16_t destPort, WeaveMessageInfo *msgInfo, PacketBuffer *payload) |
| { |
| return ResendMessage(destAddr, WEAVE_PORT, INET_NULL_INTERFACEID, msgInfo, payload); |
| } |
| |
| /** |
| * Resend an encoded Weave 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] destAddr The destination IP Address. |
| * |
| * @param[in] destPort The destination port. |
| * |
| * @param[in] interfaceId The interface on which to send the Weave message. |
| * |
| * @param[in] msgInfo A pointer to the WeaveMessageInfo object. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the encoded Weave message. |
| * |
| * @retval #WEAVE_NO_ERROR on successfully sending the message down to the network layer. |
| * @retval errors generated from the lower Inet layer UDP endpoint during sending. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::ResendMessage(const IPAddress &aDestAddr, uint16_t destPort, InterfaceId interfaceId, |
| WeaveMessageInfo *msgInfo, PacketBuffer *payload) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| IPAddress destAddr = aDestAddr; |
| |
| res = SelectDestNodeIdAndAddress(msgInfo->DestNodeId, destAddr); |
| SuccessOrExit(res); |
| |
| return SendMessage(destAddr, destPort, interfaceId, payload, msgInfo->Flags); |
| exit: |
| if ((res != WEAVE_NO_ERROR) && |
| (payload != NULL) && |
| ((msgInfo->Flags & kWeaveMessageFlag_RetainBuffer) == 0)) |
| { |
| PacketBuffer::Free(payload); |
| } |
| return res; |
| } |
| |
| /** |
| * Get the number of WeaveConnections 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. |
| * @param[out] aOutTotal Reference to size_t, in which the size of |
| * pool is stored. |
| * |
| */ |
| void WeaveMessageLayer::GetConnectionPoolStats(nl::Weave::System::Stats::count_t &aOutInUse) const |
| { |
| aOutInUse = 0; |
| |
| const WeaveConnection *con = (WeaveConnection *) mConPool; |
| for (int i = 0; i < WEAVE_CONFIG_MAX_CONNECTIONS; i++, con++) |
| { |
| if (con->mRefCount != 0) |
| { |
| aOutInUse++; |
| } |
| } |
| } |
| |
| /** |
| * Create a new WeaveConnection object from a pool. |
| * |
| * @return a pointer to the newly created WeaveConnection object if successful, otherwise |
| * NULL. |
| * |
| */ |
| WeaveConnection *WeaveMessageLayer::NewConnection() |
| { |
| WeaveConnection *con = (WeaveConnection *) mConPool; |
| for (int i = 0; i < WEAVE_CONFIG_MAX_CONNECTIONS; i++, con++) |
| { |
| if (con->mRefCount == 0) |
| { |
| con->Init(this); |
| return con; |
| } |
| } |
| |
| WeaveLogError(ExchangeManager, "New con FAILED"); |
| return NULL; |
| } |
| |
| /** |
| * Create a new WeaveConnectionTunnel object from a pool. |
| * |
| * @return a pointer to the newly created WeaveConnectionTunnel object if successful, |
| * otherwise NULL. |
| * |
| */ |
| WeaveConnectionTunnel *WeaveMessageLayer::NewConnectionTunnel() |
| { |
| WeaveConnectionTunnel *tun = (WeaveConnectionTunnel *) mTunnelPool; |
| for (int i = 0; i < WEAVE_CONFIG_MAX_TUNNELS; i++, tun++) |
| { |
| if (tun->IsInUse() == false) |
| { |
| tun->Init(this); |
| return tun; |
| } |
| } |
| |
| WeaveLogError(ExchangeManager, "New tun FAILED"); |
| return NULL; |
| } |
| |
| /** |
| * Create a WeaveConnectionTunnel by coupling together two specified WeaveConnections. |
| On successful creation, the TCPEndPoints corresponding to the component WeaveConnection |
| objects are handed over to the WeaveConnectionTunnel, otherwise the WeaveConnections are |
| closed. |
| * |
| * @param[out] tunPtr A pointer to pointer of a WeaveConnectionTunnel object. |
| * |
| * @param[in] conOne A reference to the first WeaveConnection object. |
| * |
| * @param[in] conTwo A reference to the second WeaveConnection object. |
| * |
| * @param[in] inactivityTimeoutMS The maximum time in milliseconds that the Weave |
| * connection tunnel could be idle. |
| * |
| * @retval #WEAVE_NO_ERROR on successful creation of the WeaveConnectionTunnel. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE if the component WeaveConnection objects of the |
| * WeaveConnectionTunnel is not in the correct state. |
| * @retval #WEAVE_ERROR_NO_MEMORY if a new WeaveConnectionTunnel object cannot be created. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::CreateTunnel(WeaveConnectionTunnel **tunPtr, WeaveConnection &conOne, |
| WeaveConnection &conTwo, uint32_t inactivityTimeoutMS) |
| { |
| WeaveLogDetail(ExchangeManager, "Entering CreateTunnel"); |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| VerifyOrExit(conOne.State == WeaveConnection::kState_Connected && conTwo.State == |
| WeaveConnection::kState_Connected, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| *tunPtr = NewConnectionTunnel(); |
| VerifyOrExit(*tunPtr != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| // Form WeaveConnectionTunnel from former WeaveConnections' TCPEndPoints. |
| err = (*tunPtr)->MakeTunnelConnected(conOne.mTcpEndPoint, conTwo.mTcpEndPoint); |
| SuccessOrExit(err); |
| |
| WeaveLogProgress(ExchangeManager, "Created Weave tunnel from Cons (%04X, %04X) with EPs (%04X, %04X)", |
| conOne.LogId(), conTwo.LogId(), conOne.mTcpEndPoint->LogId(), conTwo.mTcpEndPoint->LogId()); |
| |
| if (inactivityTimeoutMS > 0) |
| { |
| // Set TCPEndPoint inactivity timeouts. |
| conOne.mTcpEndPoint->SetIdleTimeout(inactivityTimeoutMS); |
| conTwo.mTcpEndPoint->SetIdleTimeout(inactivityTimeoutMS); |
| } |
| |
| // Remove TCPEndPoints from WeaveConnections now that we've handed the former to our new WeaveConnectionTunnel. |
| conOne.mTcpEndPoint = NULL; |
| conTwo.mTcpEndPoint = NULL; |
| |
| exit: |
| WeaveLogDetail(ExchangeManager, "Exiting CreateTunnel"); |
| |
| // Close WeaveConnection args. |
| conOne.Close(true); |
| conTwo.Close(true); |
| |
| return err; |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::SetUnsecuredConnectionListener(ConnectionReceiveFunct |
| newOnUnsecuredConnectionReceived, CallbackRemovedFunct newOnUnsecuredConnectionCallbacksRemoved, bool force, |
| void *listenerState) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| WeaveLogProgress(ExchangeManager, "Entered SetUnsecuredConnectionReceived, cb = %p, %p", |
| newOnUnsecuredConnectionReceived, newOnUnsecuredConnectionCallbacksRemoved); |
| |
| if (IsUnsecuredListenEnabled() == false) |
| { |
| err = EnableUnsecuredListen(); |
| SuccessOrExit(err); |
| } |
| |
| // New OnUnsecuredConnectionReceived cannot be null. To clear, use ClearOnUnsecuredConnectionReceived(). |
| VerifyOrExit(newOnUnsecuredConnectionReceived != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT); |
| |
| if (OnUnsecuredConnectionReceived != NULL) |
| { |
| if (force == false) |
| { |
| err = WEAVE_ERROR_INCORRECT_STATE; |
| ExitNow(); |
| } |
| else if (OnUnsecuredConnectionCallbacksRemoved != NULL) |
| { |
| // Notify application that its previous OnUnsecuredConnectionReceived callback has been removed. |
| OnUnsecuredConnectionCallbacksRemoved(UnsecuredConnectionReceivedAppState); |
| } |
| } |
| |
| OnUnsecuredConnectionReceived = newOnUnsecuredConnectionReceived; |
| OnUnsecuredConnectionCallbacksRemoved = newOnUnsecuredConnectionCallbacksRemoved; |
| UnsecuredConnectionReceivedAppState = listenerState; |
| |
| exit: |
| return err; |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::ClearUnsecuredConnectionListener(ConnectionReceiveFunct |
| oldOnUnsecuredConnectionReceived, CallbackRemovedFunct oldOnUnsecuredConnectionCallbacksRemoved) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| WeaveLogProgress(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) |
| WeaveLogError(ExchangeManager, "bad arg: OnUnsecuredConnectionReceived"); |
| else |
| WeaveLogError(ExchangeManager, "bad arg: OnUnsecuredConnectionCallbacksRemoved"); |
| err = WEAVE_ERROR_INVALID_ARGUMENT; |
| ExitNow(); |
| } |
| |
| if (IsUnsecuredListenEnabled() == true) |
| { |
| err = DisableUnsecuredListen(); |
| SuccessOrExit(err); |
| } |
| |
| OnUnsecuredConnectionReceived = NULL; |
| OnUnsecuredConnectionCallbacksRemoved = NULL; |
| UnsecuredConnectionReceivedAppState = NULL; |
| |
| exit: |
| return err; |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::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 WEAVE_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 = IPv6InterfaceIdToWeaveNodeId(destAddr.InterfaceId()); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| // Encode and return message header field value. |
| static uint16_t EncodeHeaderField(const WeaveMessageInfo *msgInfo) |
| { |
| return ((((uint16_t)msgInfo->Flags) << kMsgHeaderField_FlagsShift) & kMsgHeaderField_FlagsMask) | |
| ((((uint16_t)msgInfo->EncryptionType) << kMsgHeaderField_EncryptionTypeShift) & kMsgHeaderField_EncryptionTypeMask) | |
| ((((uint16_t)msgInfo->MessageVersion) << kMsgHeaderField_MessageVersionShift) & kMsgHeaderField_MessageVersionMask); |
| } |
| |
| // Decode message header field value. |
| static void DecodeHeaderField(const uint16_t headerField, WeaveMessageInfo *msgInfo) |
| { |
| msgInfo->Flags = (uint16_t)((headerField & kMsgHeaderField_FlagsMask) >> kMsgHeaderField_FlagsShift); |
| msgInfo->EncryptionType = (uint8_t)((headerField & kMsgHeaderField_EncryptionTypeMask) >> kMsgHeaderField_EncryptionTypeShift); |
| msgInfo->MessageVersion = (uint8_t)((headerField & kMsgHeaderField_MessageVersionMask) >> kMsgHeaderField_MessageVersionShift); |
| } |
| |
| /** |
| * Decode a Weave Message layer header from a received Weave message. |
| * |
| * @param[in] msgBuf A pointer to the PacketBuffer object holding the Weave message. |
| * |
| * @param[in] msgInfo A pointer to a WeaveMessageInfo 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 #WEAVE_NO_ERROR On successful decoding of the message header. |
| * @retval #WEAVE_ERROR_INVALID_MESSAGE_LENGTH |
| * If the message buffer passed is of invalid length. |
| * @retval #WEAVE_ERROR_UNSUPPORTED_MESSAGE_VERSION |
| * If the Weave Message header format version is not supported. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::DecodeHeader(PacketBuffer *msgBuf, WeaveMessageInfo *msgInfo, uint8_t **payloadStart) |
| { |
| WEAVE_ERROR err = WEAVE_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 = WEAVE_ERROR_INVALID_MESSAGE_LENGTH); |
| } |
| |
| // Read and verify the header field. |
| headerField = LittleEndian::Read16(p); |
| VerifyOrExit((headerField & kMsgHeaderField_ReservedFlagsMask) == 0, err = WEAVE_ERROR_INVALID_MESSAGE_FLAG); |
| |
| // Decode the header field. |
| DecodeHeaderField(headerField, msgInfo); |
| |
| // Error if the message version is unsupported. |
| if (msgInfo->MessageVersion != kWeaveMessageVersion_V1 && |
| msgInfo->MessageVersion != kWeaveMessageVersion_V2) |
| { |
| ExitNow(err = WEAVE_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 & kWeaveMessageFlag_SourceNodeId) |
| { |
| if ((p + 8) > msgEnd) |
| { |
| ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH); |
| } |
| msgInfo->SourceNodeId = LittleEndian::Read64(p); |
| } |
| |
| // Decode the destination node identifier if included in the message. |
| if (msgInfo->Flags & kWeaveMessageFlag_DestNodeId) |
| { |
| if ((p + 8) > msgEnd) |
| { |
| ExitNow(err = WEAVE_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 != kWeaveEncryptionType_None) |
| { |
| if ((p + kKeyIdLen) > msgEnd) |
| { |
| ExitNow(err = WEAVE_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 &= ~kWeaveMessageFlag_MsgCounterSyncReq; |
| |
| msgInfo->KeyId = WeaveKeyId::kNone; |
| } |
| |
| if (payloadStart != NULL) |
| { |
| *payloadStart = p; |
| } |
| |
| exit: |
| return err; |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::ReEncodeMessage(PacketBuffer *msgBuf) |
| { |
| WeaveMessageInfo msgInfo; |
| WEAVE_ERROR err; |
| uint8_t *p; |
| WeaveSessionState 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 != WEAVE_NO_ERROR) |
| return err; |
| |
| encryptionLen = msgLen - (p - msgStart); |
| |
| err = FabricState->GetSessionState(msgInfo.SourceNodeId, msgInfo.KeyId, msgInfo.EncryptionType, NULL, sessionState); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| switch (msgInfo.EncryptionType) |
| { |
| case kWeaveEncryptionType_None: |
| break; |
| |
| case kWeaveEncryptionType_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.SetWeaveMessageCounter(msgInfo.SourceNodeId, msgInfo.MessageId); |
| aes128CTR.EncryptData(p, encryptionLen, p); |
| } |
| break; |
| default: |
| return WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE; |
| } |
| |
| // signature remains untouched -- we have not modified it. |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Encode a WeaveMessageLayer header into an PacketBuffer. |
| * |
| * @param[in] msgInfo A pointer to a WeaveMessageInfo object containing information |
| * about the message to be encoded. |
| * |
| * @param[in] msgBuf A pointer to the PacketBuffer object that would hold the Weave message. |
| * |
| * @param[in] con A pointer to the WeaveConnection object. |
| * |
| * @param[in] maxLen The maximum length of the encoded Weave message. |
| * |
| * @param[in] reserve The reserved space before the payload to hold the Weave message header. |
| * |
| * @retval #WEAVE_NO_ERROR on successful encoding of the message. |
| * @retval #WEAVE_ERROR_UNSUPPORTED_MESSAGE_VERSION if the Weave Message header format version is |
| * not supported. |
| * @retval #WEAVE_ERROR_INVALID_MESSAGE_LENGTH if the payload length in the message buffer is zero. |
| * @retval #WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE if the encryption type in the message header is not |
| * supported. |
| * @retval #WEAVE_ERROR_MESSAGE_TOO_LONG if the encoded message would be longer than the |
| * requested maximum. |
| * @retval #WEAVE_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. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::EncodeMessage(WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf, WeaveConnection *con, |
| uint16_t maxLen, uint16_t reserve) |
| { |
| WEAVE_ERROR err; |
| uint8_t *p1; |
| // Error if an unsupported message version requested. |
| if (msgInfo->MessageVersion != kWeaveMessageVersion_V1 && |
| msgInfo->MessageVersion != kWeaveMessageVersion_V2) |
| return WEAVE_ERROR_UNSUPPORTED_MESSAGE_VERSION; |
| |
| // Message already encoded, don't do anything |
| if (msgInfo->Flags & kWeaveMessageFlag_MessageEncoded) |
| { |
| WeaveMessageInfo existingMsgInfo; |
| existingMsgInfo.Clear(); |
| err = DecodeHeader(msgBuf, &existingMsgInfo, &p1); |
| if (err != WEAVE_NO_ERROR) |
| { |
| return err; |
| } |
| msgInfo->DestNodeId = existingMsgInfo.DestNodeId; |
| return WEAVE_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 & kWeaveMessageFlag_SourceNodeId) |
| headLen += 8; |
| if (msgInfo->Flags & kWeaveMessageFlag_DestNodeId) |
| headLen += 8; |
| switch (msgInfo->EncryptionType) |
| { |
| case kWeaveEncryptionType_None: |
| break; |
| case kWeaveEncryptionType_AES128CTRSHA1: |
| // Can only encrypt non-zero length payloads. |
| if (payloadLen == 0) |
| return WEAVE_ERROR_INVALID_MESSAGE_LENGTH; |
| headLen += 2; |
| tailLen += HMACSHA1::kDigestLength; |
| break; |
| default: |
| return WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE; |
| } |
| |
| // Error if the encoded message would be longer than the requested maximum. |
| if ((headLen + msgBuf->DataLength() + tailLen) > maxLen) |
| return WEAVE_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 WEAVE_ERROR_BUFFER_TOO_SMALL; |
| |
| // Error if not enough space after the message payload. |
| if ((msgBuf->DataLength() + tailLen) > msgBuf->MaxDataLength()) |
| return WEAVE_ERROR_BUFFER_TOO_SMALL; |
| |
| uint8_t *payloadStart = msgBuf->Start(); |
| |
| // Get the session state for the given destination node and encryption key. |
| WeaveSessionState 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 != WEAVE_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. |
| if ((msgInfo->Flags & kWeaveMessageFlag_ReuseMessageId) == 0) |
| msgInfo->MessageId = sessionState.NewMessageId(); |
| |
| #if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| // Request message counter synchronization if peer group key counter is not synchronized. |
| if (sessionState.MessageIdNotSynchronized() && WeaveKeyId::IsAppGroupKey(msgInfo->KeyId)) |
| { |
| // Set the flag. |
| msgInfo->Flags |= kWeaveMessageFlag_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 WEAVE_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 & kWeaveMessageFlag_SourceNodeId) |
| { |
| LittleEndian::Write64(p, msgInfo->SourceNodeId); |
| } |
| |
| // If specified, encode the destination node id. |
| if (msgInfo->Flags & kWeaveMessageFlag_DestNodeId) |
| { |
| LittleEndian::Write64(p, msgInfo->DestNodeId); |
| } |
| |
| switch (msgInfo->EncryptionType) |
| { |
| case kWeaveEncryptionType_None: |
| // If no encryption requested, skip over the payload in the message buffer. |
| p += payloadLen; |
| break; |
| |
| case kWeaveEncryptionType_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 |= kWeaveMessageFlag_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 WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::DecodeMessage(PacketBuffer *msgBuf, uint64_t sourceNodeId, WeaveConnection *con, |
| WeaveMessageInfo *msgInfo, uint8_t **rPayload, uint16_t *rPayloadLen) // TODO: use references |
| { |
| WEAVE_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 != WEAVE_NO_ERROR) |
| return err; |
| |
| // Get the session state for the given source node and encryption key. |
| WeaveSessionState sessionState; |
| |
| err = FabricState->GetSessionState(sourceNodeId, msgInfo->KeyId, msgInfo->EncryptionType, con, sessionState); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| switch (msgInfo->EncryptionType) |
| { |
| case kWeaveEncryptionType_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 kWeaveEncryptionType_AES128CTRSHA1: |
| { |
| // Error if the message is short given the expected fields. |
| if ((p + kMinPayloadLen + HMACSHA1::kDigestLength) > msgEnd) |
| return WEAVE_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 WEAVE_ERROR_INTEGRITY_CHECK_FAILED; |
| // Skip past the payload and the integrity check value. |
| p += payloadLen + HMACSHA1::kDigestLength; |
| |
| break; |
| } |
| |
| default: |
| return WEAVE_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 |= kWeaveMessageFlag_DuplicateMessage; |
| |
| #if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| // Set flag if peer group key message counter is not synchronized. |
| if (sessionState.MessageIdNotSynchronized() && WeaveKeyId::IsAppGroupKey(msgInfo->KeyId)) |
| msgInfo->Flags |= kWeaveMessageFlag_PeerGroupMsgIdNotSynchronized; |
| #endif |
| |
| // Pass the peer authentication mode back to the application via the weave message header structure. |
| msgInfo->PeerAuthMode = sessionState.AuthMode; |
| |
| return err; |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::EncodeMessageWithLength(WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf, |
| WeaveConnection *con, uint16_t maxLen) |
| { |
| // Encode the message, reserving 2 bytes for the length. |
| WEAVE_ERROR err = EncodeMessage(msgInfo, msgBuf, con, maxLen - 2, 2); |
| if (err != WEAVE_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 WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::DecodeMessageWithLength(PacketBuffer *msgBuf, uint64_t sourceNodeId, WeaveConnection *con, |
| WeaveMessageInfo *msgInfo, uint8_t **rPayload, uint16_t *rPayloadLen, uint16_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 WEAVE_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 = 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 WEAVE_ERROR_MESSAGE_TOO_LONG; |
| return WEAVE_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. |
| WEAVE_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 == WEAVE_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 WeaveMessageLayer::HandleUDPMessage(UDPEndPoint *endPoint, PacketBuffer *msg, const IPPacketInfo *pktInfo) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| WeaveMessageLayer *msgLayer = (WeaveMessageLayer *) endPoint->AppState; |
| WeaveMessageInfo msgInfo; |
| uint64_t sourceNodeId; |
| uint8_t *payload; |
| uint16_t payloadLen; |
| |
| WEAVE_FAULT_INJECT(FaultInjection::kFault_DropIncomingUDPMsg, |
| PacketBuffer::Free(msg); |
| ExitNow(err = WEAVE_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 = WEAVE_ERROR_INVALID_ADDRESS; |
| |
| if (err == WEAVE_NO_ERROR) |
| { |
| // If the soruce 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()) ? IPv6InterfaceIdToWeaveNodeId(pktInfo->SrcAddress.InterfaceId()) : kNodeIdNotSpecified; |
| |
| // Attempt to decode the message. |
| err = msgLayer->DecodeMessage(msg, sourceNodeId, NULL, &msgInfo, &payload, &payloadLen); |
| |
| if (err == WEAVE_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 == WEAVE_NO_ERROR) |
| { |
| if (msgInfo.DestNodeId != msgLayer->FabricState->LocalNodeId && msgInfo.DestNodeId != kAnyNodeId) |
| err = WEAVE_ERROR_INVALID_DESTINATION_NODE_ID; |
| } |
| |
| // If an error occurred, discard the message and call the on receive error handler. |
| SuccessOrExit(err); |
| |
| //Check if message carries tunneled data and needs to be sent to Tunnel Agent |
| if (msgInfo.MessageVersion == kWeaveMessageVersion_V2) |
| { |
| if (msgInfo.Flags & kWeaveMessageFlag_TunneledData) |
| { |
| #if WEAVE_CONFIG_ENABLE_TUNNELING |
| // Policy for handling duplicate tunneled UDP message: |
| // - Eliminate duplicate tunneled encrypted messages to prevent replay of messages by |
| // a malicious man-in-the-middle. |
| // - Handle duplicate tunneled unencrypted message. |
| // Dispatch the tunneled data message to the application if it is not a duplicate or unencrypted. |
| if (!(msgInfo.Flags & kWeaveMessageFlag_DuplicateMessage) || msgInfo.KeyId == WeaveKeyId::kNone) |
| { |
| if (msgLayer->OnUDPTunneledMessageReceived) |
| { |
| msgLayer->OnUDPTunneledMessageReceived(msgLayer, msg); |
| } |
| else |
| { |
| ExitNow(err = WEAVE_ERROR_NO_MESSAGE_HANDLER); |
| } |
| } |
| #endif |
| } |
| else |
| { |
| // Call the supplied OnMessageReceived callback. |
| if (msgLayer->OnMessageReceived != NULL) |
| { |
| msgLayer->OnMessageReceived(msgLayer, &msgInfo, msg); |
| } |
| else |
| { |
| ExitNow(err = WEAVE_ERROR_NO_MESSAGE_HANDLER); |
| } |
| } |
| } |
| else if (msgInfo.MessageVersion == kWeaveMessageVersion_V1) |
| { |
| // Call the supplied OnMessageReceived callback. |
| if (msgLayer->OnMessageReceived != NULL) |
| msgLayer->OnMessageReceived(msgLayer, &msgInfo, msg); |
| else |
| { |
| ExitNow(err = WEAVE_ERROR_NO_MESSAGE_HANDLER); |
| } |
| } |
| |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| WeaveLogError(MessageLayer, "HandleUDPMessage Error %d", 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, NULL, err); |
| |
| if (msgLayer->OnReceiveError != NULL) |
| msgLayer->OnReceiveError(msgLayer, err, pktInfo); |
| } |
| |
| return; |
| } |
| |
| void WeaveMessageLayer::HandleUDPReceiveError(UDPEndPoint *endPoint, INET_ERROR err, const IPPacketInfo *pktInfo) |
| { |
| WeaveLogError(MessageLayer, "HandleUDPReceiveError Error %d", err); |
| |
| WeaveMessageLayer *msgLayer = (WeaveMessageLayer *) endPoint->AppState; |
| if (msgLayer->OnReceiveError != NULL) |
| msgLayer->OnReceiveError(msgLayer, err, pktInfo); |
| } |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| void WeaveMessageLayer::HandleIncomingBleConnection(BLEEndPoint *bleEP) |
| { |
| WeaveMessageLayer *msgLayer = (WeaveMessageLayer *) bleEP->mAppState; |
| |
| // Immediately close the connection if there's no callback registered. |
| if (msgLayer->OnConnectionReceived == NULL && msgLayer->ExchangeMgr == NULL) |
| { |
| bleEP->Close(); |
| if (msgLayer->OnAcceptError != NULL) |
| msgLayer->OnAcceptError(msgLayer, WEAVE_ERROR_NO_CONNECTION_HANDLER); |
| return; |
| } |
| |
| // Attempt to allocate a connection object. Fail if too many connections. |
| WeaveConnection *con = msgLayer->NewConnection(); |
| if (con == NULL) |
| { |
| bleEP->Close(); |
| if (msgLayer->OnAcceptError != NULL) |
| msgLayer->OnAcceptError(msgLayer, WEAVE_ERROR_TOO_MANY_CONNECTIONS); |
| return; |
| } |
| |
| // Setup the connection object. |
| con->MakeConnectedBle(bleEP); |
| |
| #if WEAVE_PROGRESS_LOGGING |
| { |
| WeaveLogProgress(MessageLayer, "WoBle con rcvd"); |
| } |
| #endif |
| |
| // Set the default idle timeout. |
| con->SetIdleTimeout(msgLayer->IncomingConIdleTimeout); |
| |
| // If the exchange manager has been initialized, call its callback. |
| if (msgLayer->ExchangeMgr != NULL) |
| msgLayer->ExchangeMgr->HandleConnectionReceived(con); |
| |
| // Call the app's OnConnectionReceived callback. |
| if (msgLayer->OnConnectionReceived != NULL) |
| msgLayer->OnConnectionReceived(msgLayer, con); |
| } |
| #endif /* CONFIG_NETWORK_LAYER_BLE */ |
| |
| void WeaveMessageLayer::HandleIncomingTcpConnection(TCPEndPoint *listeningEP, TCPEndPoint *conEP, const IPAddress &peerAddr, uint16_t peerPort) |
| { |
| INET_ERROR err; |
| IPAddress localAddr; |
| uint16_t localPort; |
| WeaveMessageLayer *msgLayer = (WeaveMessageLayer *) listeningEP->AppState; |
| |
| // Immediately close the connection if there's no callback registered. |
| if (msgLayer->OnConnectionReceived == NULL && msgLayer->ExchangeMgr == NULL) |
| { |
| conEP->Free(); |
| if (msgLayer->OnAcceptError != NULL) |
| msgLayer->OnAcceptError(msgLayer, WEAVE_ERROR_NO_CONNECTION_HANDLER); |
| return; |
| } |
| |
| // Attempt to allocate a connection object. Fail if too many connections. |
| WeaveConnection *con = msgLayer->NewConnection(); |
| if (con == NULL) |
| { |
| conEP->Free(); |
| if (msgLayer->OnAcceptError != NULL) |
| msgLayer->OnAcceptError(msgLayer, WEAVE_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 != NULL) |
| msgLayer->OnAcceptError(msgLayer, err); |
| return; |
| } |
| |
| // Setup the connection object. |
| con->MakeConnectedTcp(conEP, localAddr, peerAddr); |
| |
| #if WEAVE_PROGRESS_LOGGING |
| { |
| char ipAddrStr[64]; |
| peerAddr.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| WeaveLogProgress(MessageLayer, "Con %s %04" PRIX16 " %s %d", "rcvd", con->LogId(), ipAddrStr, (int)peerPort); |
| } |
| #endif |
| |
| // Set the default idle timeout. |
| con->SetIdleTimeout(msgLayer->IncomingConIdleTimeout); |
| |
| // If the exchange manager has been initialized, call it's callback. |
| if (msgLayer->ExchangeMgr != NULL) |
| msgLayer->ExchangeMgr->HandleConnectionReceived(con); |
| |
| // Call the app's OnConnectionReceived callback. |
| if (msgLayer->OnConnectionReceived != NULL) |
| msgLayer->OnConnectionReceived(msgLayer, con); |
| |
| // If connection was received on unsecured port, call the app's OnUnsecuredConnectionReceived callback. |
| if (msgLayer->OnUnsecuredConnectionReceived != NULL && conEP->GetLocalInfo(&localAddr, &localPort) == |
| WEAVE_NO_ERROR && localPort == WEAVE_UNSECURED_PORT) |
| msgLayer->OnUnsecuredConnectionReceived(msgLayer, con); |
| |
| } |
| |
| void WeaveMessageLayer::HandleAcceptError(TCPEndPoint *ep, INET_ERROR err) |
| { |
| WeaveMessageLayer *msgLayer = (WeaveMessageLayer *) ep->AppState; |
| if (msgLayer->OnAcceptError != NULL) |
| 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 #WEAVE_NO_ERROR on successful refreshing of endpoints. |
| * @retval InetLayer errors based on calls to create TCP/UDP endpoints. |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::RefreshEndpoints() |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| #if INET_CONFIG_ENABLE_IPV4 |
| bool listenIPv4 = (mFlags & kFlag_ListenIPv4) != 0; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| bool listenIPv6 = (mFlags & kFlag_ListenIPv6) != 0; |
| bool listenTCP = (mFlags & kFlag_ListenTCP) != 0; |
| bool listenUDP = (mFlags & kFlag_ListenUDP) != 0; |
| #if CONFIG_NETWORK_LAYER_BLE |
| bool listenBLE = (mFlags & kFlag_ListenBLE) != 0; |
| #endif |
| |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| char ipAddrStr[64]; |
| char intfStr[64]; |
| #endif |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| // Close and free the general-purpose IPv4 and IPv6 UDP endpoints. |
| if (mIPv4UDP != NULL) |
| { |
| mIPv4UDP->Free(); |
| mIPv4UDP = NULL; |
| } |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| if (mIPv6UDP != NULL) |
| { |
| mIPv6UDP->Free(); |
| mIPv6UDP = NULL; |
| } |
| |
| // Close and free all the currently open IPv6 interface endpoints. We will re-create them |
| // below based on the current network interface config. |
| for (int i = 0; i < WEAVE_CONFIG_MAX_LOCAL_ADDR_UDP_ENDPOINTS; i++) |
| if (mIPv6UDPLocalAddr[i] != NULL) |
| { |
| if (mIPv6UDPLocalAddr[i] != mIPv6UDP) |
| mIPv6UDPLocalAddr[i]->Free(); |
| mIPv6UDPLocalAddr[i] = NULL; |
| } |
| |
| // Clear the list of interfaces. |
| memset(mInterfaces, 0, sizeof(mInterfaces)); |
| |
| #if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| #define WEAVE_IPV4_LISTEN_ADDR (FabricState->ListenIPv4Addr) |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| #define WEAVE_IPV6_LISTEN_ADDR (FabricState->ListenIPv6Addr) |
| #define WEAVE_IPV6_LISTEN_INTF (mInterfaces[0]) |
| |
| // 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()) |
| { |
| res = Inet->GetInterfaceFromAddr(WEAVE_IPV6_LISTEN_ADDR, mInterfaces[0]); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| } |
| |
| #else // !WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| #define WEAVE_IPV4_LISTEN_ADDR (IPAddress::Any) |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| #define WEAVE_IPV6_LISTEN_ADDR (IPAddress::Any) |
| #define WEAVE_IPV6_LISTEN_INTF (INET_NULL_INTERFACEID) |
| |
| #endif // !WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| // If needed, create a IPv4 TCP listening endpoint... |
| if (listenTCP && listenIPv4) |
| { |
| if (mIPv4TCPListen == NULL) |
| { |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| WEAVE_IPV4_LISTEN_ADDR.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| WeaveBindLog("Binding IPv4 TCP listen endpoint to [%s]:%d", ipAddrStr, WEAVE_PORT); |
| #endif |
| |
| res = Inet->NewTCPEndPoint(&mIPv4TCPListen); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Bind the endpoint to the IPv4 listening address (if specified) and the Weave port. |
| res = mIPv4TCPListen->Bind(kIPAddressType_IPv4, WEAVE_IPV4_LISTEN_ADDR, WEAVE_PORT, true); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| WeaveBindLog("Listening on IPv4 TCP endpoint"); |
| |
| // Listen for incoming TCP connections. |
| mIPv4TCPListen->AppState = this; |
| mIPv4TCPListen->OnConnectionReceived = HandleIncomingTcpConnection; |
| mIPv4TCPListen->OnAcceptError = HandleAcceptError; |
| res = mIPv4TCPListen->Listen(1); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| } |
| } |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| // If needed, create a IPv6 TCP listening endpoint... |
| if (listenTCP && listenIPv6) |
| { |
| if (mIPv6TCPListen == NULL) |
| { |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| WEAVE_IPV6_LISTEN_ADDR.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| WeaveBindLog("Binding IPv6 TCP listen endpoint to [%s]:%d", ipAddrStr, WEAVE_PORT); |
| #endif |
| |
| res = Inet->NewTCPEndPoint(&mIPv6TCPListen); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Bind the endpoint to the IPv6 listening address (if specified) and the Weave port. |
| res = mIPv6TCPListen->Bind(kIPAddressType_IPv6, WEAVE_IPV6_LISTEN_ADDR, WEAVE_PORT, true); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| WeaveBindLog("Listening on IPv6 TCP endpoint"); |
| #endif |
| |
| // Listen for incoming TCP connections. |
| mIPv6TCPListen->AppState = this; |
| mIPv6TCPListen->OnConnectionReceived = HandleIncomingTcpConnection; |
| mIPv6TCPListen->OnAcceptError = HandleAcceptError; |
| res = mIPv6TCPListen->Listen(1); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| } |
| |
| } |
| |
| #if WEAVE_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| if (listenIPv6 && (mFlags & kFlag_ListenUnsecured) != 0) |
| { |
| if (mUnsecuredIPv6TCPListen == NULL) |
| { |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| WEAVE_IPV6_LISTEN_ADDR.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| WeaveBindLog("Binding unsecured IPv6 TCP listen endpoint to [%s]:%d", ipAddrStr, WEAVE_UNSECURED_PORT); |
| #endif |
| |
| res = Inet->NewTCPEndPoint(&mUnsecuredIPv6TCPListen); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Bind the endpoint to the IPv6 listening address (if specified) and the unsecured Weave port. |
| res = mUnsecuredIPv6TCPListen->Bind(kIPAddressType_IPv6, WEAVE_IPV6_LISTEN_ADDR, WEAVE_UNSECURED_PORT, true); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| WeaveBindLog("Listening on unsecured IPv6 TCP endpoint"); |
| #endif |
| |
| // Listen for incoming TCP connections. |
| mUnsecuredIPv6TCPListen->AppState = this; |
| mUnsecuredIPv6TCPListen->OnConnectionReceived = HandleIncomingTcpConnection; |
| mUnsecuredIPv6TCPListen->OnAcceptError = HandleAcceptError; |
| res = mUnsecuredIPv6TCPListen->Listen(1); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| } |
| } |
| |
| else if (mUnsecuredIPv6TCPListen != NULL) |
| { |
| mUnsecuredIPv6TCPListen->Free(); |
| mUnsecuredIPv6TCPListen = NULL; |
| } |
| #endif // WEAVE_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| // Create a general-purpose IPv4 UDP endpoint... |
| if (mIPv4UDP == NULL) |
| { |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| WEAVE_IPV4_LISTEN_ADDR.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| WeaveBindLog("Binding general purpose IPv4 UDP endpoint to [%s]:%d", ipAddrStr, WEAVE_PORT); |
| #endif // WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| |
| res = Inet->NewUDPEndPoint(&mIPv4UDP); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Bind the endpoint. If a listening IPv4 address was specified bind to that, |
| // otherwise bind to all addresses. |
| res = mIPv4UDP->Bind(kIPAddressType_IPv4, WEAVE_IPV4_LISTEN_ADDR, WEAVE_PORT); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Listen for incoming IPv4 UDP messages if so configured. |
| if (listenUDP && listenIPv4) |
| { |
| WeaveBindLog("Listening on general purpose IPv4 UDP endpoint"); |
| |
| mIPv4UDP->AppState = this; |
| mIPv4UDP->OnMessageReceived = HandleUDPMessage; |
| mIPv4UDP->OnReceiveError = HandleUDPReceiveError; |
| res = mIPv4UDP->Listen(); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| } |
| } |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| // Create a general-purpose IPv6 UDP endpoint... |
| if (mIPv6UDP == NULL) |
| { |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| GetInterfaceName(WEAVE_IPV6_LISTEN_INTF, intfStr, sizeof(intfStr)); |
| WEAVE_IPV6_LISTEN_ADDR.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| WeaveBindLog("Binding general purpose IPv6 UDP endpoint to [%s]:%d (%s)", ipAddrStr, WEAVE_PORT, intfStr); |
| #endif // WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| |
| res = Inet->NewUDPEndPoint(&mIPv6UDP); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Bind the endpoint. If a particular IPv6 address was specified, bind to that address and its |
| // associated interface. Otherwise bind to all IPv6 addresses. |
| res = mIPv6UDP->Bind(kIPAddressType_IPv6, WEAVE_IPV6_LISTEN_ADDR, WEAVE_PORT, WEAVE_IPV6_LISTEN_INTF); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Listen for incoming IPv6 UDP messages if so configured. |
| if (listenUDP && listenIPv6) |
| { |
| WeaveBindLog("Listening on general purpose IPv6 UDP endpoint"); |
| |
| mIPv6UDP->AppState = this; |
| mIPv6UDP->OnMessageReceived = HandleUDPMessage; |
| mIPv6UDP->OnReceiveError = HandleUDPReceiveError; |
| res = mIPv6UDP->Listen(); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| } |
| } |
| |
| #if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| // If configured to use a specific IPv6 address... |
| if (IsBoundToLocalIPv6Address()) |
| { |
| // If IPv6 listening has been enabled, create a IPv6 UDP endpoint for receiving multicast messages. |
| // Bind this interface to the link-local, all-nodes multicast address (ff02::1) and the interface |
| // associated with the listening IPv6 address. |
| if (listenIPv6 && mIPv6UDPMulticastRcv == NULL) |
| { |
| IPAddress ipv6LinkLocalAllNodes = IPAddress::MakeIPv6Multicast(kIPv6MulticastScope_Link, kIPV6MulticastGroup_AllNodes); |
| |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| ipv6LinkLocalAllNodes.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| GetInterfaceName(WEAVE_IPV6_LISTEN_INTF, intfStr, sizeof(intfStr)); |
| WeaveBindLog("Binding IPv6 multicast receive endpoint to [%s]:%d (%s)", ipAddrStr, WEAVE_PORT, intfStr); |
| #endif |
| |
| res = Inet->NewUDPEndPoint(&mIPv6UDPMulticastRcv); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Bind the endpoint to the weave port on the IPv6 link-local all nodes multicast address. |
| res = mIPv6UDPMulticastRcv->Bind(kIPAddressType_IPv6, ipv6LinkLocalAllNodes, WEAVE_PORT, WEAVE_IPV6_LISTEN_INTF); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| WeaveBindLog("Listening on IPv6 multicast receive endpoint"); |
| |
| // Enable reception of incoming messages. |
| mIPv6UDPMulticastRcv->AppState = this; |
| mIPv6UDPMulticastRcv->OnMessageReceived = HandleUDPMessage; |
| mIPv6UDPMulticastRcv->OnReceiveError = HandleUDPReceiveError; |
| res = mIPv6UDPMulticastRcv->Listen(); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| } |
| } |
| |
| // Otherwise, the messaging layer is configured to use all available interfaces/addresses, so ... |
| else |
| |
| #endif // WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| { |
| uint16_t epCount = 0; |
| uint16_t i; |
| |
| // Scan the list of addresses assigned to the system's network interfaces. For each address... |
| for (InterfaceAddressIterator addrIter; addrIter.HasCurrent(); addrIter.Next()) |
| { |
| InterfaceId curIntfId = addrIter.GetInterface(); |
| |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| GetInterfaceName(curIntfId, intfStr, sizeof(intfStr)); |
| #endif |
| |
| // Skip any interface that doesn't support multicast. |
| if (!addrIter.SupportsMulticast()) |
| continue; |
| |
| // Add the interface to the interface list if it doesn't already exist. |
| for (i = 0; i < WEAVE_CONFIG_MAX_INTERFACES; i++) |
| { |
| if (mInterfaces[i] == curIntfId) |
| break; |
| if (mInterfaces[i] == INET_NULL_INTERFACEID) |
| { |
| WeaveBindLog("Adding %s to interface table", intfStr); |
| mInterfaces[i] = curIntfId; |
| break; |
| } |
| } |
| if (i == WEAVE_CONFIG_MAX_INTERFACES) |
| WeaveLogError(MessageLayer, "Interface table full"); |
| |
| // If we haven't exceeded the max ULA endpoints... |
| if (epCount < WEAVE_CONFIG_MAX_LOCAL_ADDR_UDP_ENDPOINTS) |
| { |
| WEAVE_ERROR epErr; |
| UDPEndPoint *& ep = mIPv6UDPLocalAddr[epCount]; |
| |
| // Skip the address if it is not a ULA. |
| IPAddress curAddr = addrIter.GetAddress(); |
| if (!curAddr.IsIPv6ULA()) |
| continue; |
| |
| // Skip the address if we're a member of a fabric and the ULA is not a fabric address |
| // (in particular, the global identifier in the ULA does not match the bottom 40 bits of the |
| // fabric id). |
| if (FabricState->FabricId != 0 && !FabricState->IsFabricAddress(curAddr)) |
| continue; |
| |
| #if WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING |
| curAddr.ToString(ipAddrStr, sizeof(ipAddrStr)); |
| WeaveBindLog("Binding IPv6 UDP interface endpoint to [%s]:%d (%s)", ipAddrStr, WEAVE_PORT, intfStr); |
| #endif |
| |
| // Create an IPv6 UDP endpoint to be used for sending/receiving messages over the associated interface. |
| res = Inet->NewUDPEndPoint(&ep); |
| if (res != WEAVE_NO_ERROR) |
| goto exit; |
| |
| // Bind the endpoint to the identified address. This ensures that messages sent over the endpoint |
| // have the correct source address and port. |
| epErr = ep->Bind(kIPAddressType_IPv6, curAddr, WEAVE_PORT, curIntfId); |
| |
| // Enable reception of incoming messages. |
| WeaveBindLog("Listening on IPv6 UDP interface endpoint"); |
| if (epErr == WEAVE_NO_ERROR) |
| { |
| ep->AppState = this; |
| ep->OnMessageReceived = HandleUDPMessage; |
| ep->OnReceiveError = HandleUDPReceiveError; |
| epErr = ep->Listen(); |
| } |
| |
| // If we successfully bound the endpoint, add it to the list. Otherwise, discard it and move on to |
| // the next address. |
| if (epErr == WEAVE_NO_ERROR) |
| epCount++; |
| else |
| { |
| ep->Free(); |
| ep = NULL; |
| } |
| } |
| } |
| } |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| if (listenBLE) |
| { |
| if (mBle != NULL) |
| { |
| mBle->mAppState = this; |
| mBle->OnWeaveBleConnectReceived = HandleIncomingBleConnection; |
| } |
| else |
| WeaveLogError(ExchangeManager, "Cannot listen for BLE connections, null BleLayer"); |
| } |
| #endif |
| |
| exit: |
| if (res != WEAVE_NO_ERROR) |
| WeaveBindLog("RefreshEndpoints failed: %ld", (long)res); |
| return res; |
| } |
| |
| void WeaveMessageLayer::Encrypt_AES128CTRSHA1(const WeaveMessageInfo *msgInfo, const uint8_t *key, |
| const uint8_t *inData, uint16_t inLen, uint8_t *outBuf) |
| { |
| AES128CTRMode aes128CTR; |
| aes128CTR.SetKey(key); |
| aes128CTR.SetWeaveMessageCounter(msgInfo->SourceNodeId, msgInfo->MessageId); |
| aes128CTR.EncryptData(inData, inLen, outBuf); |
| } |
| |
| void WeaveMessageLayer::ComputeIntegrityCheck_AES128CTRSHA1(const WeaveMessageInfo *msgInfo, const uint8_t *key, |
| const uint8_t *inData, uint16_t inLen, uint8_t *outBuf) |
| { |
| 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, WeaveEncryptionKey_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); |
| |
| // Hash the message header field and the message Id for the message version V2. |
| if (msgInfo->MessageVersion == kWeaveMessageVersion_V2) |
| { |
| // 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); |
| } |
| |
| /** |
| * Close all open TCP and UDP endpoints. Then abort any |
| * open WeaveConnections and shutdown any open |
| * WeaveConnectionTunnel objects. |
| * |
| * @note |
| * A call to CloseEndpoints() terminates all communication |
| * channels within the WeaveMessageLayer but does not terminate |
| * the WeaveMessageLayer object. |
| * |
| * @sa Shutdown(). |
| * |
| */ |
| WEAVE_ERROR WeaveMessageLayer::CloseEndpoints() |
| { |
| WeaveBindLog("Closing endpoints"); |
| |
| if (mIPv6TCPListen != NULL) |
| { |
| mIPv6TCPListen->Free(); |
| mIPv6TCPListen = NULL; |
| } |
| |
| if (mIPv6UDP != NULL) |
| { |
| mIPv6UDP->Free(); |
| mIPv6UDP = NULL; |
| } |
| |
| #if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN |
| |
| if (mIPv6UDPMulticastRcv != NULL) |
| { |
| mIPv6UDPMulticastRcv->Free(); |
| mIPv6UDPMulticastRcv = NULL; |
| } |
| |
| #endif |
| |
| #if WEAVE_CONFIG_ENABLE_UNSECURED_TCP_LISTEN |
| |
| if (mUnsecuredIPv6TCPListen != NULL) |
| { |
| mUnsecuredIPv6TCPListen->Free(); |
| mUnsecuredIPv6TCPListen = NULL; |
| } |
| |
| #endif |
| |
| for (int i = 0; i < WEAVE_CONFIG_MAX_LOCAL_ADDR_UDP_ENDPOINTS; i++) |
| { |
| if (mIPv6UDPLocalAddr[i] != NULL) |
| { |
| if (mIPv6UDPLocalAddr[i] != mIPv6UDP) |
| mIPv6UDPLocalAddr[i]->Free(); |
| mIPv6UDPLocalAddr[i] = NULL; |
| } |
| } |
| |
| #if INET_CONFIG_ENABLE_IPV4 |
| if (mIPv4TCPListen != NULL) |
| { |
| mIPv4TCPListen->Free(); |
| mIPv4TCPListen = NULL; |
| } |
| |
| if (mIPv4UDP != NULL) |
| { |
| mIPv4UDP->Free(); |
| mIPv4UDP = NULL; |
| } |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| |
| memset(mInterfaces, 0, sizeof(mInterfaces)); |
| |
| // Abort any open connections. |
| WeaveConnection *con = static_cast<WeaveConnection *>(mConPool); |
| for (int i = 0; i < WEAVE_CONFIG_MAX_CONNECTIONS; i++, con++) |
| if (con->mRefCount > 0) |
| con->Abort(); |
| |
| // Shut down any open tunnels. |
| WeaveConnectionTunnel *tun = static_cast<WeaveConnectionTunnel *>(mTunnelPool); |
| for (int i = 0; i < WEAVE_CONFIG_MAX_TUNNELS; i++, tun++) |
| { |
| if (tun->mMessageLayer != NULL) |
| { |
| // Suppress callback as we're shutting down the whole stack. |
| tun->OnShutdown = NULL; |
| tun->Shutdown(); |
| } |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::EnableUnsecuredListen() |
| { |
| // Enable reception of connections on the unsecured Weave port. This allows devices to establish |
| // a connection while provisionally connected (i.e. without security) at the network layer. |
| mFlags |= kFlag_ListenUnsecured; |
| return RefreshEndpoints(); |
| } |
| |
| WEAVE_ERROR WeaveMessageLayer::DisableUnsecuredListen() |
| { |
| mFlags &= ~kFlag_ListenUnsecured; |
| return RefreshEndpoints(); |
| } |
| |
| bool WeaveMessageLayer::IsUnsecuredListenEnabled() const |
| { |
| return mFlags & kFlag_ListenUnsecured; |
| } |
| |
| /** |
| * 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 Weave conversations or pending responses. |
| * The handler must be set after the WeaveMessageLayer has been initialized; |
| * shutting down the WeaveMessageLayer 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 WeaveMessageLayer::SetSignalMessageLayerActivityChanged(MessageLayerActivityChangeHandlerFunct messageLayerActivityChangeHandler) |
| { |
| OnMessageLayerActivityChange = messageLayerActivityChangeHandler; |
| } |
| |
| /** |
| * 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 WeaveMessageLayer::SignalMessageLayerActivityChanged(void) |
| { |
| bool messageLayerIsActive; |
| |
| if (OnMessageLayerActivityChange) |
| { |
| messageLayerIsActive = (ExchangeMgr->mContextsInUse != 0) |
| #if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC |
| || FabricState->IsMsgCounterSyncReqInProgress() |
| #endif |
| ; |
| |
| OnMessageLayerActivityChange(messageLayerIsActive); |
| } |
| } |
| |
| /** |
| * Get the max Weave 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 Weave message that will not overflow the specified UDP MTU. |
| * |
| * Finally, the maximum payload size returned will not result in a Weave |
| * message that will overflow the max Weave 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 Weave payload size. |
| */ |
| uint32_t WeaveMessageLayer::GetMaxWeavePayloadSize(const PacketBuffer *msgBuf, bool isUDP, uint32_t udpMTU) |
| { |
| uint32_t maxWeaveMessageSize = isUDP ? udpMTU - INET_CONFIG_MAX_IP_AND_UDP_HEADER_SIZE : UINT16_MAX; |
| uint32_t maxWeavePayloadSize = maxWeaveMessageSize - WEAVE_HEADER_RESERVE_SIZE - WEAVE_TRAILER_RESERVE_SIZE; |
| uint32_t maxBufferablePayloadSize = msgBuf->AvailableDataLength() - WEAVE_TRAILER_RESERVE_SIZE; |
| |
| return maxBufferablePayloadSize < maxWeavePayloadSize |
| ? maxBufferablePayloadSize |
| : maxWeavePayloadSize; |
| } |
| |
| /** |
| * Constructs a string containing a Weave node id and associated address / connection information. |
| * |
| * The generated string has the following format: |
| * |
| * <node-id> ([<ip-address>]:<port>, con <con-id>) |
| * |
| * @param[in] buf A pointer to a buffer into which the string should be written. The output |
| * will include a nul termination character. The supplied buffer should be at |
| * least as big as WEAVE_MAX_NODE_ADDR_STR_LENGTH. If the buffer is smaller |
| * than that size the string will be truncated to fit. |
| * @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] con A pointer to a WeaveConnection object whose logging id should be printed; or NULL |
| * if no connection id should be printed. |
| */ |
| void WeaveNodeAddrToStr(char *buf, uint32_t bufSize, uint64_t nodeId, const IPAddress *addr, uint16_t port, WeaveConnection *con) |
| { |
| uint32_t len; |
| bool needSep = false; |
| |
| if (nodeId != kNodeIdNotSpecified) |
| len = snprintf(buf, bufSize, "%" PRIX64 " (", nodeId); |
| else |
| len = snprintf(buf, bufSize, "unknown ("); |
| VerifyOrExit(len < bufSize, /* no-op */); |
| |
| if (addr != NULL) |
| { |
| 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 */); |
| |
| needSep = true; |
| } |
| |
| if (con != NULL) |
| { |
| len += snprintf(buf + len, bufSize - len, "%scon %04" PRIX16, (needSep) ? ", " : "", con->LogId()); |
| VerifyOrExit(len < bufSize, /* no-op */); |
| } |
| |
| snprintf(buf + len, bufSize - len, ")"); |
| |
| exit: |
| return; |
| } |
| |
| /** |
| * Constructs a string describing the source of a received Weave message. |
| * |
| * @param[in] buf A pointer to a buffer into which the string should be written. The size |
| * of the supplied buffer should be at least as big as WEAVE_MAX_MESSAGE_SOURCE_STR_LENGTH. |
| * @param[in] bufSize The size of the buffer pointed at by buf. |
| * @param[in] msgInfo A pointer to a WeaveMessageInfo structure containing inforamtion about the message. |
| * |
| */ |
| void WeaveMessageSourceToStr(char *buf, uint32_t bufSize, const WeaveMessageInfo *msgInfo) |
| { |
| WeaveNodeAddrToStr(buf, bufSize, msgInfo->SourceNodeId, |
| (msgInfo->InPacketInfo != NULL) ? &msgInfo->InPacketInfo->SrcAddress : NULL, |
| (msgInfo->InPacketInfo != NULL) ? msgInfo->InPacketInfo->SrcPort : 0, |
| msgInfo->InCon); |
| } |
| |
| } // namespace nl |
| } // namespace Weave |