blob: d259978e238896effc4a4ab9358bf3ab76dd500b [file] [log] [blame]
/*
*
* 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 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/WeaveCore.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 err = WEAVE_NO_ERROR;
VerifyOrExit(State == kState_NotInitialized, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(context != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
State = kState_Initializing;
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 = WEAVE_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 WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
SetEphemeralUDPPortEnabled(context->enableEphemeralUDPPort);
#endif
mIPv6TCPListen = NULL;
mIPv6UDP = NULL;
#if INET_CONFIG_ENABLE_IPV4
mIPv4TCPListen = NULL;
mIPv4UDP = NULL;
#endif // INET_CONFIG_ENABLE_IPV4
#if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
mIPv6UDPMulticastRcv = NULL;
#if INET_CONFIG_ENABLE_IPV4
mIPv4UDPBroadcastRcv = NULL;
#endif // INET_CONFIG_ENABLE_IPV4
#endif //WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
#if WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
mIPv6EphemeralUDP = NULL;
#if INET_CONFIG_ENABLE_IPV4
mIPv4EphemeralUDP = NULL;
#endif // INET_CONFIG_ENABLE_IPV4
#endif // WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
#if WEAVE_CONFIG_ENABLE_UNSECURED_TCP_LISTEN
mUnsecuredIPv6TCPListen = NULL;
#endif
err = RefreshEndpoints();
SuccessOrExit(err);
#if CONFIG_NETWORK_LAYER_BLE
if (context->listenBLE && mBle != NULL)
{
mBle->mAppState = this;
mBle->OnWeaveBleConnectReceived = HandleIncomingBleConnection;
WeaveLogProgress(MessageLayer, "Accepting WoBLE connections");
}
else
{
WeaveLogProgress(MessageLayer, "WoBLE disabled%s", (mBle != NULL) ? " by application" : " (BLE layer not initialized)");
}
#endif // CONFIG_NETWORK_LAYER_BLE
State = kState_Initialized;
exit:
if (err != WEAVE_NO_ERROR && State == kState_Initializing)
{
Shutdown();
}
return err;
}
/**
* 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();
#if CONFIG_NETWORK_LAYER_BLE
if (mBle != NULL && mBle->mAppState == this)
{
mBle->mAppState = NULL;
mBle->OnWeaveBleConnectReceived = NULL;
}
#endif // CONFIG_NETWORK_LAYER_BLE
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] sendIntId The interface on which to send the Weave message.
*
* @param[in] msgInfo A pointer to a WeaveMessageInfo object.
*
* @param[in] payload 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] aDestAddr 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 ||
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
err == System::MapErrorLwIP(ERR_RTE)
#else
err == System::MapErrorPOSIX(ENETUNREACH) || err == System::MapErrorPOSIX(EADDRNOTAVAIL)
#endif
;
}
WEAVE_ERROR WeaveMessageLayer::FilterUDPSendError(WEAVE_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 WEAVE_SYSTEM_CONFIG_USE_LWIP
if (err == System::MapErrorLwIP(ERR_RTE))
{
err = WEAVE_NO_ERROR;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (err == System::MapErrorPOSIX(ENETUNREACH) || err == System::MapErrorPOSIX(EADDRNOTAVAIL))
{
err = WEAVE_NO_ERROR;
}
#endif
}
return err;
}
/**
* 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));
}
/**
* Set the 'ForceRefreshUDPEndpoints' flag if needed.
*
* Based on the error returned when sending a UDP message, set a flag in the WeaveMessageLayer
* that will force a complete refresh of all UDPEndPoints the next time \c RefreshEndPoints is
* called.
*/
void WeaveMessageLayer::CheckForceRefreshUDPEndPointsNeeded(WEAVE_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 WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (err == System::MapErrorPOSIX(EPIPE))
{
SetFlag(mFlags, kFlag_ForceRefreshUDPEndPoints);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
}
/**
* 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, uint32_t msgFlags)
{
WEAVE_ERROR err = WEAVE_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 WEAVE_NO_ERROR;
VerifyOrExit(!mDropMessage, err = WEAVE_NO_ERROR);
// Drop the message and return. Free the buffer if it does not need to be
// retained(e.g., for WRM retransmissions).
WEAVE_FAULT_INJECT(FaultInjection::kFault_DropOutgoingUDPMsg,
ExitNow(err = WEAVE_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 Weave fabric, AND MulticastFromLinkLocal has NOT been specified, send
// the message once for each Weave 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 WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
else if (destAddr.IsIPv4() ? IsBoundToLocalIPv4Address() : IsBoundToLocalIPv6Address())
{
sendAction = kMulticast_OneInterface;
}
#endif // WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
else if (destAddr.IsIPv6() && FabricState->FabricId != kFabricIdNotSpecified &&
!GetFlag(msgFlags, kWeaveMessageFlag_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, kWeaveMessageFlag_RetainBuffer) ? UDPEndPoint::kSendFlag_RetainBuffer : 0;
err = ep->SendMsg(&pktInfo, payload, udpSendFlags);
payload = NULL; // 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, only report an error
// if *none* of the interfaces succeed (b/195258416).
err = 0xFF;
for (InterfaceIterator intfIter; intfIter.HasCurrent(); intfIter.Next())
{
if (intfIter.SupportsMulticast())
{
pktInfo.Interface = intfIter.GetInterface();
WEAVE_ERROR sendErr = ep->SendMsg(&pktInfo, payload, UDPEndPoint::kSendFlag_RetainBuffer);
CheckForceRefreshUDPEndPointsNeeded(sendErr);
if (err != WEAVE_NO_ERROR)
{
err = FilterUDPSendError(sendErr, true);
}
}
}
break;
case kMulticast_AllFabricAddrs:
// Send the message once for each Weave 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.
// Only report an error if *none* of the interfaces succeed (b/195258416).
err = 0xFF;
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))
{
WEAVE_ERROR sendErr = ep->SendMsg(&pktInfo, payload, UDPEndPoint::kSendFlag_RetainBuffer);
CheckForceRefreshUDPEndPointsNeeded(sendErr);
if (err != WEAVE_NO_ERROR)
{
err = FilterUDPSendError(sendErr, true);
}
}
}
break;
}
exit:
if (payload != NULL && !GetFlag(msgFlags, kWeaveMessageFlag_RetainBuffer))
PacketBuffer::Free(payload);
return err;
}
/**
* Select an appropriate UDP endpoint for sending a Weave message.
*/
WEAVE_ERROR WeaveMessageLayer::SelectOutboundUDPEndPoint(const IPAddress & destAddr, uint32_t msgFlags, UDPEndPoint *& ep)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Select a UDP endpoint object for sending a message based on the destination address type
// and the message send flags.
//
// If the WEAVE_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 Weave UDP endpoint. This will result in the source port
// field being set to the well-known Weave port.
//
switch (destAddr.Type())
{
#if INET_CONFIG_ENABLE_IPV4
case kIPAddressType_IPv4:
if (GetFlag(msgFlags, kWeaveMessageFlag_ViaEphemeralUDPPort))
{
#if WEAVE_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, kWeaveMessageFlag_ViaEphemeralUDPPort))
{
#if WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
ep = mIPv6EphemeralUDP;
#else
ep = NULL;
#endif
}
else
{
ep = mIPv6UDP;
}
break;
default:
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
}
VerifyOrExit(ep != NULL, err = WEAVE_ERROR_NO_ENDPOINT);
exit:
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] aDestAddr 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.
*
*/
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;
}
void WeaveMessageLayer::GetIncomingTCPConCount(const IPAddress &peerAddr, uint16_t &count, uint16_t &countFromIP)
{
count = 0;
countFromIP = 0;
WeaveConnection *con = (WeaveConnection *) mConPool;
for (int i = 0; i < WEAVE_CONFIG_MAX_CONNECTIONS; i++, con++)
{
if (con->mRefCount > 0 &&
con->NetworkType == WeaveConnection::kNetworkType_IP &&
con->IsIncoming())
{
count++;
if (con->PeerAddr == peerAddr)
{
countFromIP++;
}
}
}
}
/**
* 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 (UnsecuredListenEnabled() == 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 (UnsecuredListenEnabled() == 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, 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 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 = 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 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 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()) ? 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);
// Record whether the message was sent to the local node's ephemeral port.
#if WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
SetFlag(msgInfo.Flags, kWeaveMessageFlag_ViaEphemeralUDPPort,
(endPoint == msgLayer->mIPv6EphemeralUDP
#if INET_CONFIG_ENABLE_IPV4
|| endPoint == msgLayer->mIPv4EphemeralUDP
#endif // INET_CONFIG_ENABLE_IPV4
));
#endif // WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
//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 %s", nl::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, 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 %s", nl::ErrorStr(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);
// Set incoming connection flag.
con->SetIncoming(true);
// 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;
uint16_t incomingTCPConCount;
uint16_t incomingTCPConCountFromIP;
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;
}
// Fail if too many incoming TCP connections.
msgLayer->GetIncomingTCPConCount(peerAddr, incomingTCPConCount, incomingTCPConCountFromIP);
if (incomingTCPConCount == WEAVE_CONFIG_MAX_INCOMING_TCP_CONNECTIONS ||
incomingTCPConCountFromIP == WEAVE_CONFIG_MAX_INCOMING_TCP_CON_FROM_SINGLE_IP)
{
conEP->Free();
if (msgLayer->OnAcceptError != NULL)
msgLayer->OnAcceptError(msgLayer, WEAVE_ERROR_TOO_MANY_CONNECTIONS);
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);
// Set incoming connection flag.
con->SetIncoming(true);
// 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);
// 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 err = WEAVE_NO_ERROR;
const bool listenIPv6 = IPv6ListenEnabled();
#if INET_CONFIG_ENABLE_IPV4
const bool listenIPv4 = IPv4ListenEnabled();
#endif // INET_CONFIG_ENABLE_IPV4
#if WEAVE_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 // WEAVE_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 // WEAVE_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 Weave IPv4 TCP listening endpoint
//
// The Weave IPv4 TCP listening endpoint is use to listen for incoming IPv4 TCP connections
// to the local node's Weave port.
//
err = RefreshEndpoint(mIPv4TCPListen, listenIPv4TCP,
"Weave IPv4 TCP listen", kIPAddressType_IPv4, listenIPv4Addr, WEAVE_PORT);
SuccessOrExit(err);
#endif // INET_CONFIG_ENABLE_IPV4
// Enable / disable the Weave IPv6 TCP listening endpoint
//
// The Weave IPv6 TCP listening endpoint is use to listen for incoming IPv6 TCP connections
// to the local node's Weave port.
//
err = RefreshEndpoint(mIPv6TCPListen, listenIPv6TCP,
"Weave IPv6 TCP listen", kIPAddressType_IPv6, listenIPv6Addr, WEAVE_PORT);
SuccessOrExit(err);
#if WEAVE_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 Weave 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, WEAVE_UNSECURED_PORT);
SuccessOrExit(err);
#endif // WEAVE_CONFIG_ENABLE_UNSECURED_TCP_LISTEN
}
// ================================================================================
// Enable / disable UDP endpoints...
// ================================================================================
{
const bool listenUDP = UDPListenEnabled();
#if INET_CONFIG_ENABLE_IPV4
// Enabled / disable the Weave IPv4 UDP endpoint as necessary.
//
// The Weave IPv4 UDP endpoint is used to listen for unsolicited IPv4 UDP Weave messages sent
// to the local node's Weave port. Is is also used by the local node to send IPv4 UDP Weave
// 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,
"Weave IPv4 UDP", kIPAddressType_IPv4, listenIPv4Addr, WEAVE_PORT, INET_NULL_INTERFACEID);
SuccessOrExit(err);
#if WEAVE_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 Weave 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 // WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
#endif // INET_CONFIG_ENABLE_IPV4
// Enabled / disable the Weave IPv6 UDP endpoint as necessary.
//
// The Weave IPv6 UDP endpoint is used to listen for unsolicited IPv6 UDP Weave messages sent
// to the local node's Weave port. Is is also used by the local node to send IPv6 UDP Weave
// 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,
"Weave IPv6 UDP", kIPAddressType_IPv6, listenIPv6Addr, WEAVE_PORT, listenIPv6Intf);
SuccessOrExit(err);
#if WEAVE_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 Weave 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 // WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
#if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
// Enable / disable the Weave IPv6 UDP multicast endpoint.
//
// The Weave IPv6 UDP multicast endpoint is used to listen for unsolicited IPv6 UDP Weave 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 Weave IPv6 UDP endpoint will not receive multicast
// messages in this configuration.
//
IPAddress ipv6LinkLocalAllNodes = IPAddress::MakeIPv6WellKnownMulticast(kIPv6MulticastScope_Link, kIPV6MulticastGroup_AllNodes);
const bool listenWeaveIPv6UDPMulticastEP = (listenIPv6UDP && IsBoundToLocalIPv6Address());
err = RefreshEndpoint(mIPv6UDPMulticastRcv, listenWeaveIPv6UDPMulticastEP,
"Weave IPv6 UDP multicast", kIPAddressType_IPv6, ipv6LinkLocalAllNodes, WEAVE_PORT, listenIPv6Intf);
SuccessOrExit(err);
#if INET_CONFIG_ENABLE_IPV4
// Enable / disable the Weave 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 listenWeaveIPv4UDPBroadcastEP = (listenIPv4UDP && IsBoundToLocalIPv4Address());
err = RefreshEndpoint(mIPv4UDPBroadcastRcv, listenWeaveIPv4UDPBroadcastEP,
"Weave IPv4 UDP broadcast", kIPAddressType_IPv4, ipv4Broadcast, WEAVE_PORT, INET_NULL_INTERFACEID);
SuccessOrExit(err);
#endif // INET_CONFIG_ENABLE_IPV4
#endif // WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
}
exit:
if (err != WEAVE_NO_ERROR)
WeaveBindLog("RefreshEndpoints failed: %s", ErrorStr(err));
return err;
}
WEAVE_ERROR WeaveMessageLayer::RefreshEndpoint(TCPEndPoint *& endPoint, bool enable, const char * name, IPAddressType addrType, IPAddress addr, uint16_t port)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Release any existing endpoint as needed.
if (endPoint != NULL && !enable)
{
endPoint->Free();
endPoint = NULL;
}
// If needed, create and bind a new endpoint...
if (endPoint == NULL && 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 WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING
{
char ipAddrStr[64];
addr.ToString(ipAddrStr, sizeof(ipAddrStr));
WeaveBindLog("Listening on %s endpoint ([%s]:%" PRIu16 ")", name, ipAddrStr, port);
}
#endif // WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING
}
exit:
if (err != WEAVE_NO_ERROR)
{
if (endPoint != NULL)
{
endPoint->Free();
endPoint = NULL;
}
WeaveLogError(MessageLayer, "Error initializing %s endpoint: %s", name, ErrorStr(err));
}
return err;
}
WEAVE_ERROR WeaveMessageLayer::RefreshEndpoint(UDPEndPoint *& endPoint, bool enable, const char * name, IPAddressType addrType, IPAddress addr, uint16_t port, InterfaceId intfId)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Release any existing endpoint as needed.
if (endPoint != NULL && (!enable || GetFlag(mFlags, kFlag_ForceRefreshUDPEndPoints)))
{
endPoint->Free();
endPoint = NULL;
}
// If needed, create and bind a new endpoint...
if (endPoint == NULL && 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 WEAVE_BIND_DETAIL_LOGGING && WEAVE_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';
}
WeaveBindLog("Listening on %s endpoint ([%s]:%" PRIu16 "%s)", name, ipAddrStr, endPoint->GetBoundPort(), intfStr);
}
#endif // WEAVE_BIND_DETAIL_LOGGING && WEAVE_DETAIL_LOGGING
}
exit:
if (err != WEAVE_NO_ERROR)
{
if (endPoint != NULL)
{
endPoint->Free();
endPoint = NULL;
}
WeaveLogError(MessageLayer, "Error initializing %s endpoint: %s", name, ErrorStr(err));
}
return err;
}
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()
{
// Close all endpoints used for listening.
CloseListeningEndpoints();
// 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;
}
void WeaveMessageLayer::CloseListeningEndpoints(void)
{
WeaveBindLog("Closing endpoints");
if (mIPv6TCPListen != NULL)
{
mIPv6TCPListen->Free();
mIPv6TCPListen = NULL;
}
if (mIPv6UDP != NULL)
{
mIPv6UDP->Free();
mIPv6UDP = NULL;
}
#if WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
if (mIPv6EphemeralUDP != NULL)
{
mIPv6EphemeralUDP->Free();
mIPv6EphemeralUDP = NULL;
}
#endif // WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
#if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
if (mIPv6UDPMulticastRcv != NULL)
{
mIPv6UDPMulticastRcv->Free();
mIPv6UDPMulticastRcv = NULL;
}
#endif // WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
#if WEAVE_CONFIG_ENABLE_UNSECURED_TCP_LISTEN
if (mUnsecuredIPv6TCPListen != NULL)
{
mUnsecuredIPv6TCPListen->Free();
mUnsecuredIPv6TCPListen = NULL;
}
#endif // WEAVE_CONFIG_ENABLE_UNSECURED_TCP_LISTEN
#if INET_CONFIG_ENABLE_IPV4
if (mIPv4TCPListen != NULL)
{
mIPv4TCPListen->Free();
mIPv4TCPListen = NULL;
}
if (mIPv4UDP != NULL)
{
mIPv4UDP->Free();
mIPv4UDP = NULL;
}
#if WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
if (mIPv4EphemeralUDP != NULL)
{
mIPv4EphemeralUDP->Free();
mIPv4EphemeralUDP = NULL;
}
#endif // WEAVE_CONFIG_ENABLE_EPHEMERAL_UDP_PORT
#if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
if (mIPv4UDPBroadcastRcv != NULL)
{
mIPv4UDPBroadcastRcv->Free();
mIPv4UDPBroadcastRcv = NULL;
}
#endif // WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
#endif // INET_CONFIG_ENABLE_IPV4
}
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.
SetFlag(mFlags, kFlag_ListenUnsecured);
return RefreshEndpoints();
}
WEAVE_ERROR WeaveMessageLayer::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 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;
}
bool WeaveMessageLayer::IsMessageLayerActive(void)
{
return (ExchangeMgr->mContextsInUse != 0)
#if WEAVE_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 WeaveMessageLayer::SignalMessageLayerActivityChanged(void)
{
if (OnMessageLayerActivityChange)
{
bool messageLayerIsActive = IsMessageLayerActive();
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 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 kWeavePeerDescription_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 WeaveConnection object whose logging id should be printed;
* or NULL if no connection id should be printed.
*/
void WeaveMessageLayer::GetPeerDescription(char * buf, size_t bufSize, uint64_t nodeId,
const IPAddress * addr, uint16_t port, InterfaceId interfaceId, const WeaveConnection * 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 != 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 */);
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 != NULL)
{
const char *conType;
switch (con->NetworkType)
{
case WeaveConnection::kNetworkType_BLE:
conType = "ble ";
break;
case WeaveConnection::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 kWeavePeerDescription_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 WeaveMessageInfo structure containing information about the message.
*
*/
void WeaveMessageLayer::GetPeerDescription(char * buf, size_t bufSize, const WeaveMessageInfo * msgInfo)
{
GetPeerDescription(buf, bufSize, msgInfo->SourceNodeId,
(msgInfo->InPacketInfo != NULL) ? &msgInfo->InPacketInfo->SrcAddress : NULL,
(msgInfo->InPacketInfo != NULL) ? msgInfo->InPacketInfo->SrcPort : 0,
(msgInfo->InPacketInfo != NULL) ? msgInfo->InPacketInfo->Interface : INET_NULL_INTERFACEID,
msgInfo->InCon);
}
/**
* @brief
* Generate random Weave node Id.
*
* @details
* This function generates 64-bit locally unique Weave node Id. This function uses cryptographically strong
* random data source to guarantee uniqueness of generated value. Note that bit 57 of the generated Weave
* node Id is set to 1 to indicate that generated Weave node Id is locally (not globally) unique.
*
* @param nodeId A reference to the 64-bit Weave node Id.
*
* @retval #WEAVE_NO_ERROR If Weave node Id was successfully generated.
*/
NL_DLL_EXPORT WEAVE_ERROR GenerateWeaveNodeId(uint64_t & nodeId)
{
WEAVE_ERROR err;
uint64_t id = 0;
while (id <= kMaxAlwaysLocalWeaveNodeId)
{
err = nl::Weave::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 nl
} // namespace Weave