blob: 4e27b81194a62570a490a61237afb19ece517344 [file] [log] [blame]
/*
*
* 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