blob: c5b67f9939ed11d0abca6dbaacbaf4ddfbaa2ae3 [file] [log] [blame]
/*
*
* Copyright (c) 2016-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
* Implementation of Weave Binding and related classes.
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif // __STDC_FORMAT_MACROS
#include <Weave/Core/WeaveCore.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/WeaveFaultInjection.h>
#include <SystemLayer/SystemStats.h>
namespace nl {
namespace Weave {
/**
* @fn Binding::State Binding::GetState(void) const
*
* Retrieve the current state of the binding.
*
* @return The binding state.
*/
/**
* @fn bool Binding::IsPreparing() const
*
* Returns true if the Binding is currently being prepared.
*/
/**
* @fn bool Binding::IsReady() const
*
* Returns true if the Binding is in the Ready state.
*/
/**
* @fn uint64_t Binding::GetPeerNodeId() const
*
* Retrieve the node id of the binding peer.
*
* Only valid once the binding object has been prepared.
*
* @return Weave node ID of the peer
*/
/**
* @fn uint32_t Binding::GetKeyId() const
*
* Retrieve the id of the message encryption key to be used when encrypting messages to/from to the peer.
*/
/**
* @fn uint8_t Binding::GetEncryptionType() const
*
* Retrieve the message encryption type to be used when encrypting messages to/from the peer.
*/
/**
* @fn uint32_t Binding::GetDefaultResponseTimeout() const
*
* Get the default exchange response timeout to be used when communicating with the peer.
*
* @return Response timeout in ms.
*/
/**
* @fn void Binding::SetDefaultResponseTimeout(uint32_t timeout)
*
* Set the default exchange response timeout to be used when communicating with the peer.
*
* @param[in] timeout The new response timeout in ms.
*/
/**
* @fn const nl::Weave::WRMPConfig& Binding::GetDefaultWRMPConfig(void) const
*
* Get the default WRMP configuration to be used when communicating with the peer.
*
* @return A reference to a WRMPConfig structure containing
* the default configuration values.
*/
/**
* @fn void Binding::SetDefaultWRMPConfig(const nl::Weave::WRMPConfig& aWRMPConfig)
*
* Set the default WRMP configuration to be used when communicating with the peer.
*
* @param[in] aWRMPConfig A reference to a WRMPConfig structure containing
* the new default configuration.
*/
/**
* @fn Binding::EventCallback Binding::GetEventCallback() const
*
* Get the function that will be called when an API event occurs for the Binding.
*
* @return A pointer to the callback function.
*/
/**
* @fn Binding::SetEventCallback(EventCallback aEventCallback)
*
* Set the application-defined function to be called when an API event occurs for the Binding.
*
* @param[in] aEventCallback A pointer to the callback function.
*/
/**
* @fn void Binding::SetProtocolLayerCallback(EventCallback callback, void *state)
*
* Set an event callback function for protocol layer code using the Binding on behalf of an
* application. This function will be called in addition to the application-defined callback
* function when API events occur for the Binding.
*
* @param[in] callback A pointer to the callback function.
* @param[in] state A pointer to a state object that will be supplied to the
* protocol layer code when a protocol layer callback occurs.
*/
/**
* @fn Binding::Configuration Binding::BeginConfiguration()
*
* Being the process of configuring the Binding. Applications must call BeginConfiguration() to
* configure the Binding prior to preparing it for communicating with the peer.
*
* @return A Binding::Configuration object that can be used to configure
* the binding.
*/
/**
* @fn WEAVE_ERROR Binding::Configuration::PrepareBinding(void)
*
* Being the process of preparing the Binding for communication with the peer.
*/
/**
* @fn WEAVE_ERROR Binding::Configuration::GetError(void) const
*
* Return any error that has occurred while configuring the Binding.
*/
/**
* Reserve a reference to the binding object.
*/
void Binding::AddRef()
{
VerifyOrDie(mState != kState_NotAllocated);
VerifyOrDie(mRefCount > 0);
++mRefCount;
}
/**
* Release a reference to the binding object.
*
* If there are no more references to the binding object, the binding is closed and freed.
*/
void Binding::Release()
{
VerifyOrDie(mState != kState_NotAllocated);
VerifyOrDie(mRefCount > 0);
if (mRefCount > 1)
{
--mRefCount;
}
else
{
DoClose();
mRefCount = 0;
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Freed", GetLogId(), mRefCount);
mExchangeManager->FreeBinding(this);
}
}
/**
* Close the binding object and release a reference.
*
* When called, this method causes the binding to enter the Closed state. Any in-progress prepare actions
* for the binding are canceled and all external communications resources held by the binding are released.
*
* Calling Close() decrements the reference count associated with the binding, freeing the object if the
* reference count becomes zero.
*/
void Binding::Close(void)
{
VerifyOrDie(mState != kState_NotAllocated);
VerifyOrDie(mRefCount > 0);
DoClose();
Release();
}
/**
* Reset the binding back to an unconfigured state.
*
* When Reset() is called, any in-progress prepare actions for the binding are canceled and all external
* communications resources held by the binding are released. Reset() places the binding in the
* Unconfigured state, after which it may be configured and prepared again.
*
* Reset() does not alter the reference count of the binding.
*/
void Binding::Reset()
{
VerifyOrDie(mState != kState_NotAllocated);
VerifyOrDie(mRefCount > 0);
DoReset(kState_NotConfigured);
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Reset", GetLogId(), mRefCount);
}
/**
* Get a unique id for the binding.
*/
uint16_t Binding::GetLogId(void) const
{
return mExchangeManager->GetBindingLogId(this);
}
/**
* Default handler for binding API events.
*
* Applications are required to call this method for any API events that they don't recognize or handle.
* Supplied parameters must be the same as those passed by the binding to the application's event handler
* function.
*
* @param[in] apAppState A pointer to application-defined state information associated with the binding.
* @param[in] aEvent Event ID passed by the event callback
* @param[in] aInParam Reference of input event parameters passed by the event callback
* @param[in] aOutParam Reference of output event parameters passed by the event callback
*
*/
void Binding::DefaultEventHandler(void *apAppState, EventType aEvent, const InEventParam& aInParam, OutEventParam& aOutParam)
{
// No actions required for current implementation
aOutParam.DefaultHandlerCalled = true;
}
/**
* Initialize this Binding object
*
* @param[in] apAppState A pointer to some context which would be carried in event callback later
* @param[in] aEventCallback A function pointer to be used for event callback
*
*/
WEAVE_ERROR Binding::Init(void *apAppState, EventCallback aEventCallback)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(aEventCallback != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
mState = kState_NotConfigured;
mRefCount = 1;
AppState = apAppState;
SetEventCallback(aEventCallback);
mProtocolLayerCallback = NULL;
mProtocolLayerState = NULL;
ResetConfig();
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Allocated", GetLogId(), mRefCount);
#if DEBUG
// Verify that the application's event callback function correctly calls the default handler.
//
// NOTE: If your code receives WEAVE_ERROR_DEFAULT_EVENT_HANDLER_NOT_CALLED it means that the event hander
// function you supplied for a binding does not properly call Binding::DefaultEventHandler for unrecognized/
// unhandled events.
//
{
InEventParam inParam;
OutEventParam outParam;
inParam.Clear();
inParam.Source = this;
outParam.Clear();
aEventCallback(apAppState, kEvent_DefaultCheck, inParam, outParam);
VerifyOrExit(outParam.DefaultHandlerCalled, err = WEAVE_ERROR_DEFAULT_EVENT_HANDLER_NOT_CALLED);
}
#endif
exit:
if (err != WEAVE_NO_ERROR)
{
mState = kState_NotAllocated;
mRefCount = 0;
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Freed", GetLogId(), mRefCount);
}
WeaveLogFunctError(err);
return err;
}
/**
* Reset the state of the binding, canceling any outstanding activities and releasing all external resources.
*/
void Binding::DoReset(State newState)
{
VerifyOrDie(mState != kState_NotAllocated);
WeaveSecurityManager *sm = mExchangeManager->MessageLayer->SecurityMgr;
State origState = mState;
// Temporarily enter the resetting state. This has the effect of suppressing any callbacks
// from lower layers that might result from the effort of resetting the binding.
mState = kState_Resetting;
// Release any reservation held on the message encryption key. In the case of
// locally-initiated, non-shared session keys, this will result in the session
// being removed.
if (GetFlag(kFlag_KeyReserved))
{
sm->ReleaseKey(mPeerNodeId, mKeyId);
}
// If a session establishment was in progress, cancel it.
if (origState == kState_PreparingSecurity_EstablishSession)
{
sm->CancelSessionEstablishment(this);
}
// Reset the configuration state of the binding.
ResetConfig();
// Advance to the new state.
mState = newState;
}
/**
* Transition the binding to the Closed state if not already closed.
*/
void Binding::DoClose(void)
{
// If not already closed...
if (mState != kState_Closed)
{
// Clear pointers to application state/code to prevent any further use.
AppState = NULL;
SetEventCallback(NULL);
SetProtocolLayerCallback(NULL, NULL);
// Reset the binding and enter the Closed state.
DoReset(kState_Closed);
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Closed", GetLogId(), mRefCount);
}
}
/**
* Reset the configuration parameters to their default values.
*/
void Binding::ResetConfig()
{
mPeerNodeId = kNodeIdNotSpecified;
mAddressingOption = kAddressing_NotSpecified;
mPeerPort = WEAVE_PORT;
mInterfaceId = INET_NULL_INTERFACEID;
mTransportOption = kTransport_NotSpecified;
mDefaultResponseTimeoutMsec = 0;
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
mDefaultWRMPConfig = gDefaultWRMPConfig;
#endif
mSecurityOption = kSecurityOption_NotSpecified;
mKeyId = WeaveKeyId::kNone;
mEncType = kWeaveEncryptionType_None;
mAuthMode = kWeaveAuthMode_Unauthenticated;
mFlags = 0;
}
/**
* Request the application to configure and prepare the Binding.
*
* Protocol layer code can use this method on a Binding that has not been configured, or
* has failed, to trigger an event to the application (kEvent_PrepareRequested) requesting
* that it configure and prepare the binding for use.
*
* This method can only be called on Bindings in the NotConfigured or Failed states.
*
* If the application does not support on-demand configuration/preparation of Bindings, the
* method will fail with WEAVE_ERROR_NOT_IMPLEMENTED.
*
*/
WEAVE_ERROR Binding::RequestPrepare()
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
InEventParam inParam;
OutEventParam outParam;
// Ensure the binding doesn't get freed while we make calls to the application.
AddRef();
// Make sure the binding is in a state where preparing is possible.
VerifyOrExit(CanBePrepared(), err = WEAVE_ERROR_INCORRECT_STATE);
inParam.Clear();
inParam.Source = this;
outParam.Clear();
outParam.PrepareRequested.PrepareError = WEAVE_NO_ERROR;
// Invoke the application to configure and prepare the binding. Note that this event
// is only ever delivered to the application, not the protocol layer.
mAppEventCallback(AppState, kEvent_PrepareRequested, inParam, outParam);
// If the application didn't handle the PrepareRequested event then it doesn't support
// on-demand configuration/preparation so fail with an error.
VerifyOrExit(!outParam.DefaultHandlerCalled, err = WEAVE_ERROR_NOT_IMPLEMENTED);
// Check for a preparation error returned by the app's event handler. Note that the application
// is not required to set an error value, since if preparation fails, and the error value is not
// set, then the code below will catch this and substitute WEAVE_ERROR_INCORRECT_STATE.
err = outParam.PrepareRequested.PrepareError;
SuccessOrExit(err);
// If the application failed to fully configure the binding, fail with an error.
VerifyOrExit(mState != kState_NotConfigured && mState != kState_Configuring, err = WEAVE_ERROR_INCORRECT_STATE);
exit:
Release();
WeaveLogFunctError(err);
return err;
}
/**
* Conduct preparation for this Binding based on configurations supplied before this call.
*
* @return #WEAVE_NO_ERROR on success and an event callback will happen. Otherwise no event callback will happen.
*/
WEAVE_ERROR Binding::DoPrepare(WEAVE_ERROR configErr)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Immediately return an error, without changing the state of the Binding, if the Binding is not
// in the correct state.
if (kState_Configuring != mState)
{
return WEAVE_ERROR_INCORRECT_STATE;
}
// Fail if an error occurred during configuration.
VerifyOrExit(WEAVE_NO_ERROR == configErr, err = configErr);
// App must set peer node id
VerifyOrExit(kNodeIdNotSpecified != mPeerNodeId, err = WEAVE_ERROR_INVALID_ARGUMENT);
// App must pick a transport option
VerifyOrExit(kTransport_NotSpecified != mTransportOption, err = WEAVE_ERROR_INVALID_ARGUMENT);
// App must pick a security option
VerifyOrExit(kSecurityOption_NotSpecified != mSecurityOption, err = WEAVE_ERROR_INVALID_ARGUMENT);
mState = kState_Preparing;
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Preparing", GetLogId(), mRefCount);
// Start by preparing the peer address
PrepareAddress();
exit:
if (WEAVE_NO_ERROR != err)
{
HandleBindingFailed(err, false);
}
WeaveLogFunctError(err);
return err;
}
/**
* Do any work necessary to determine the address of the peer in preparation for communication.
*/
void Binding::PrepareAddress()
{
mState = kState_PreparingAddress;
// TODO FUTURE: Add support for hostname resolution
// Default to using a Weave fabric address in the default subnet if an address was not specified.
if (kAddressing_NotSpecified == mAddressingOption)
{
mPeerAddress = mExchangeManager->FabricState->SelectNodeAddress(mPeerNodeId);
}
// If requested, form a Weave fabric address for the peer in the configured subnet.
else if (kAddressing_WeaveFabric == mAddressingOption)
{
mPeerAddress = mExchangeManager->FabricState->SelectNodeAddress(mPeerNodeId, mPeerAddress.Subnet());
}
PrepareTransport();
}
/**
* Do any work necessary to determine to establish transport-level communication with the peer.
*/
void Binding::PrepareTransport()
{
mState = kState_PreparingTransport;
// TODO FUTURE: Add support for TCP and existing connection
PrepareSecurity();
}
/**
* Do any work necessary to establish communication security with the peer.
*/
void Binding::PrepareSecurity()
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *sm = mExchangeManager->MessageLayer->SecurityMgr;
mState = kState_PreparingSecurity;
// Default encryption type, if not specified.
if (kSecurityOption_None != mSecurityOption && kWeaveEncryptionType_None == mEncType)
{
mEncType = kWeaveEncryptionType_AES128CTRSHA1;
}
switch (mSecurityOption)
{
case kSecurityOption_CASESession:
case kSecurityOption_SharedCASESession:
{
IPAddress peerAddress;
uint16_t peerPort;
uint64_t terminatingNodeId;
const bool isSharedSession = (mSecurityOption == kSecurityOption_SharedCASESession);
if (isSharedSession)
{
// This is also defined in Weave/Profiles/ServiceDirectory.h, but this is in Weave Core
// TODO: move this to a common location.
static const uint64_t kServiceEndpoint_CoreRouter = 0x18B4300200000012ull;
const uint64_t fabricGlobalId = WeaveFabricIdToIPv6GlobalId(mExchangeManager->FabricState->FabricId);
peerAddress = IPAddress::MakeULA(fabricGlobalId, nl::Weave::kWeaveSubnetId_Service,
nl::Weave::WeaveNodeIdToIPv6InterfaceId(kServiceEndpoint_CoreRouter));
peerPort = WEAVE_PORT;
terminatingNodeId = kServiceEndpoint_CoreRouter;
}
else
{
peerAddress = mPeerAddress;
peerPort = mPeerPort;
terminatingNodeId = kNodeIdNotSpecified;
}
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Initiating %sCASE session",
GetLogId(), mRefCount, isSharedSession ? "shared " : "");
mState = kState_PreparingSecurity_EstablishSession;
// Call the security manager to initiate the CASE session. Note that security manager will call the
// OnSecureSessionReady function during this call if a shared session is requested and the session is
// already available.
err = sm->StartCASESession(NULL, mPeerNodeId, peerAddress, peerPort, mAuthMode, this,
OnSecureSessionReady, OnSecureSessionFailed, NULL, terminatingNodeId);
// If the security manager is currently busy, wait for it to finish. When this happens,
// Binding::OnSecurityManagerAvailable() will be called, which will give the binding an opportunity
// to try again.
if (err == WEAVE_ERROR_SECURITY_MANAGER_BUSY)
{
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Security manager busy; waiting.",
GetLogId(), mRefCount);
mState = kState_PreparingSecurity_WaitSecurityMgr;
err = WEAVE_NO_ERROR;
}
SuccessOrExit(err);
}
break;
case kSecurityOption_SpecificKey:
// Add a reservation on the specified key. This reservation will be owned by the binding
// until it closes.
sm->ReserveKey(mPeerNodeId, mKeyId);
SetFlag(kFlag_KeyReserved);
HandleBindingReady();
break;
case kSecurityOption_None:
// No further preparation needed.
HandleBindingReady();
break;
default:
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
}
exit:
if (WEAVE_NO_ERROR != err)
{
HandleBindingFailed(err, true);
}
}
/**
* Transition the Binding to the Ready state.
*/
void Binding::HandleBindingReady()
{
InEventParam inParam;
OutEventParam outParam;
// Should never be called in anything other than a preparing state.
VerifyOrDie(IsPreparing());
// Transition to the Ready state.
mState = kState_Ready;
{
char ipAddrStr[64];
char intfStr[64];
const char *transportStr;
mPeerAddress.ToString(ipAddrStr, sizeof(ipAddrStr));
nl::Inet::GetInterfaceName(mInterfaceId, intfStr, sizeof(intfStr));
switch (mTransportOption)
{
case kTransport_UDP:
transportStr = "UDP";
break;
case kTransport_UDP_WRM:
transportStr = "WRM";
break;
case kTransport_TCP:
transportStr = "TCP"; // TODO FUTURE: Add id of connection
break;
case kTransport_ExistingConnection:
transportStr = "ExistingCon"; // TODO FUTURE: Add id of connection
break;
default:
transportStr = "Unknown";
break;
}
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Ready, peer %016" PRIX64 " @ [%s]:%" PRId16 " (%s) via %s",
GetLogId(), mRefCount, mPeerNodeId, ipAddrStr, mPeerPort,
(mInterfaceId != INET_NULL_INTERFACEID) ? intfStr : "default",
transportStr);
}
inParam.Clear();
inParam.Source = this;
outParam.Clear();
// Prevent the application from freeing the Binding until we're done using it.
AddRef();
// Tell the application that the prepare operation succeeded and the binding is ready for use.
mAppEventCallback(AppState, kEvent_BindingReady, inParam, outParam);
// If the Binding is still in the Ready state, and a protocol layer callback has been registered,
// tell the protocol layer that the Binding is ready for use.
if (mState == kState_Ready && mProtocolLayerCallback != NULL)
{
mProtocolLayerCallback(mProtocolLayerState, kEvent_BindingReady, inParam, outParam);
}
Release();
}
/**
* Transition the Binding to the Failed state.
*/
void Binding::HandleBindingFailed(WEAVE_ERROR err, bool raiseEvents) // TODO: add status report information.
{
InEventParam inParam;
OutEventParam outParam;
EventType eventType;
inParam.Clear();
inParam.Source = this;
outParam.Clear();
if (IsPreparing())
{
inParam.PrepareFailed.Reason = err;
eventType = kEvent_PrepareFailed;
}
else
{
inParam.BindingFailed.Reason = err;
eventType = kEvent_BindingFailed;
}
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): %s: peer %" PRIX64 ", %s",
GetLogId(), mRefCount,
(eventType == kEvent_BindingFailed) ? "Binding FAILED" : "Prepare FAILED",
mPeerNodeId, ErrorStr(err));
// Reset the binding and enter the Failed state.
DoReset(kState_Failed);
// Prevent the application from freeing the Binding until we're done using it.
AddRef();
// If requested, deliver the failure events to the application and protocol layer.
if (raiseEvents)
{
mAppEventCallback(AppState, eventType, inParam, outParam);
if (mProtocolLayerCallback != NULL)
{
mProtocolLayerCallback(mProtocolLayerState, eventType, inParam, outParam);
}
}
Release();
}
/**
* Invoked when security session establishment has completed successfully.
*/
void Binding::OnSecureSessionReady(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, uint16_t keyId, uint64_t peerNodeId, uint8_t encType)
{
Binding *_this = (Binding *)reqState;
// Verify the state of the binding.
VerifyOrDie(_this->mState == kState_PreparingSecurity_EstablishSession);
// Save the session key id and encryption type.
_this->mKeyId = keyId;
_this->mEncType = encType;
// Remember that the key must be released when the binding closes.
_this->SetFlag(kFlag_KeyReserved);
// Tell the application that the binding is ready.
_this->HandleBindingReady();
}
/**
* Invoked when security session establishment fails.
*/
void Binding::OnSecureSessionFailed(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState,
WEAVE_ERROR localErr, uint64_t peerNodeId, Profiles::StatusReporting::StatusReport *statusReport)
{
Binding *_this = (Binding *)reqState;
// Verify the state of the binding.
VerifyOrDie(_this->mState == kState_PreparingSecurity_EstablishSession);
// Tell the application that the binding has failed.
_this->HandleBindingFailed(localErr, true);
}
/**
* Invoked when a message encryption key has been rejected by a peer (via a KeyError), or a key has
* otherwise become invalid (e.g. by ending a session).
*/
void Binding::OnKeyFailed(uint64_t peerNodeId, uint32_t keyId, WEAVE_ERROR keyErr)
{
// NOTE: This method is called for any and all key errors that occur system-wide. Thus this code
// must filter for errors that apply to the current binding.
// Ignore the key error if the binding is not in the Ready state or one of the preparing states.
VerifyOrExit(IsPreparing() || mState == kState_Ready, /* no-op */);
// Ignore the key error if it is not in relation to the specified peer node.
VerifyOrExit(peerNodeId == mPeerNodeId, /* no-op */);
// Ignore the key error if the binding is in the Ready state and the failed key id does
// not match the key id associated with the binding.
VerifyOrExit(mState != kState_Ready || keyId == mKeyId, /* no-op */);
// Fail the binding.
HandleBindingFailed(keyErr, true);
exit:
return;
}
/**
* Invoked when the security manager becomes available for initiating new sessions.
*/
void Binding::OnSecurityManagerAvailable()
{
// NOTE: This method is called for all binding objects any time the security manager becomes
// available. Thus this method must filter the notification based on the state of the binding.
// If the binding is waiting for the security manager, retry preparing security.
if (mState == kState_PreparingSecurity_WaitSecurityMgr)
{
PrepareSecurity();
}
}
/**
* Re-configure an existing Exchange Context to adjust the response timeout.
*
* @param[in] apExchangeContext A pointer to an Exchange Context object to be re-configured
*
*/
WEAVE_ERROR Binding::AdjustResponseTimeout(nl::Weave::ExchangeContext *apExchangeContext) const
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Binding must be in the Ready state.
VerifyOrExit(kState_Ready == mState, err = WEAVE_ERROR_INCORRECT_STATE);
// If a default response timeout has been configured, adjust the response timeout value in
// the exchange to match.
if (mDefaultResponseTimeoutMsec)
{
apExchangeContext->ResponseTimeout = mDefaultResponseTimeoutMsec;
}
exit:
WeaveLogFunctError(err);
return err;
}
/**
* Determine if a particular incoming message is from the configured peer and is suitably authenticated.
*
* This method confirms that the message in question originated from the peer node of the binding and
* that the encryption key and type used to encrypt the message matches those configured in the binding.
* For bindings configured without the use of security, the method confirms that the incoming message is
* NOT encrypted.
*
* This method is intended to be used in protocols such as WDM where peers can spontaneously initiate
* exchanges back to the local node after an initial exchange from the node to the peer. In such cases,
* the method allows the local node to confirm that the incoming unsolicited message was sent by the
* associated peer. (Of course, for Bindings configured without the use of message encryption, this
* assertion provides no value from a security perspective. It merely confirms that the sender node
* id in the received message matches the peer's node id.)
*
* Note that if the binding is not in the Ready state, this method will always return false.
*
* @param[in] msgInfo The Weave message information for the incoming message.
*
* @return True if the message is authentically from the peer.
*/
bool Binding::IsAuthenticMessageFromPeer(const nl::Weave::WeaveMessageHeader *msgInfo)
{
if (mState != kState_Ready)
return false;
if (msgInfo->SourceNodeId != mPeerNodeId)
return false;
if (msgInfo->EncryptionType != mEncType)
return false;
if (mEncType != kWeaveEncryptionType_None && !WeaveKeyId::IsSameKeyOrGroup(msgInfo->KeyId, mKeyId))
return false;
return true;
}
/**
* Get the max Weave payload size that can fit inside the supplied PacketBuffer.
*
* For UDP, including UDP with WRM, the maximum payload size returned will
* ensure the resulting Weave message will not overflow the configured UDP MTU.
*
* Additionally, this method will ensure the Weave payload will not overflow
* the supplied PacketBuffer.
*
* @param[in] msgBuf A pointer to the PacketBuffer to which the message
* payload will be written.
*
* @return the max Weave payload size.
*/
uint32_t Binding::GetMaxWeavePayloadSize(const System::PacketBuffer *msgBuf)
{
// Constrain the max Weave payload size by the UDP MTU if we are using UDP.
// TODO: Eventually, we may configure a custom UDP MTU size on the binding
// instead of using the default value directly.
bool isUDP = (mTransportOption == kTransport_UDP || mTransportOption == kTransport_UDP_WRM);
return WeaveMessageLayer::GetMaxWeavePayloadSize(msgBuf, isUDP, WEAVE_CONFIG_DEFAULT_UDP_MTU_SIZE);
}
/**
* Allocate a new Exchange Context for communicating with the peer that is the target of the binding.
*
* @param[out] ec A reference to a pointer that will receive the newly allocated
* Exchange Context object. The pointer will be set to NULL in
* the event that the method fails.
*
* @retval #WEAVE_NO_ERROR If the exchange context was successfully allocated.
*
* @retval #WEAVE_ERROR_NO_MEMORY If no memory was available to allocate the exchange context.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If the binding is not in the Ready state.
*
* @retval other Other errors related to configuring the exchange context based
* on the configuration of the binding.
*/
WEAVE_ERROR Binding::NewExchangeContext(nl::Weave::ExchangeContext *& ec)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ec = NULL;
// Fail if the binding is not in the Ready state.
VerifyOrExit(kState_Ready == mState, err = WEAVE_ERROR_INCORRECT_STATE);
// Attempt to allocate a new exchange context.
ec = mExchangeManager->NewContext(mPeerNodeId, mPeerAddress, mPeerPort, mInterfaceId, NULL);
VerifyOrExit(NULL != ec, err = WEAVE_ERROR_NO_MEMORY);
// TODO FUTURE: Add support for connection-based exchanges
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
// Set the default WRMP configuration in the new exchange.
ec->mWRMPConfig = mDefaultWRMPConfig;
// If Weave reliable messaging was expressly requested as a transport...
if (mTransportOption == kTransport_UDP_WRM)
{
// Enable the auto-request ACK feature in the exchange so that all outgoing messages
// include a request for acknowledgment.
ec->SetAutoRequestAck(true);
}
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
// If message encryption is enabled...
if (mSecurityOption != kSecurityOption_None)
{
uint32_t keyId;
// If the key id specifies a logical group key (e.g. the "current" rotating group key), resolve it to
// the id for a specific key.
err = mExchangeManager->FabricState->GroupKeyStore->GetCurrentAppKeyId(mKeyId, keyId);
SuccessOrExit(err);
// Configure the exchange context with the selected key id and encryption type.
ec->KeyId = keyId;
ec->EncryptionType = mEncType;
// Add a reservation for the key.
mExchangeManager->MessageLayer->SecurityMgr->ReserveKey(mPeerNodeId, keyId);
// Arrange for the exchange context to automatically release the key when it is freed.
ec->SetAutoReleaseKey(true);
}
err = AdjustResponseTimeout(ec);
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR && ec != NULL)
{
ec->Close();
ec = NULL;
}
WeaveLogFunctError(err);
return err;
}
/**
* Construct a new binding configuration object.
*
* @param[in] aBinding A reference to the Binding to be configured.
*/
Binding::Configuration::Configuration(Binding& aBinding)
: mBinding(aBinding)
{
if (mBinding.CanBePrepared())
{
mBinding.mState = kState_Configuring;
mError = WEAVE_NO_ERROR;
WeaveLogDetail(ExchangeManager, "Binding[%" PRIu8 "] (%" PRIu16 "): Configuring", mBinding.GetLogId(), mBinding.mRefCount);
}
else
{
mError = WEAVE_ERROR_INCORRECT_STATE;
}
}
/**
* Configure the binding to communicate with a specific Weave node id.
*
* @param[in] aPeerNodeId Node id of the peer node.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Target_NodeId(uint64_t aPeerNodeId)
{
mBinding.mPeerNodeId = aPeerNodeId;
return *this;
}
/**
* Configure the binding to communicate with a specific Weave service endpoint.
*
* If not otherwise configured, the peer address is set to the Weave fabric address of the service endpoint.
*
* @param[in] serviceEndpointId The node id of the service endpoint with which communication will take place.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Target_ServiceEndpoint(uint64_t serviceEndpointId)
{
Target_NodeId(serviceEndpointId);
if (mBinding.mAddressingOption == Binding::kAddressing_NotSpecified)
{
TargetAddress_WeaveService();
}
return *this;
}
/**
* When communicating with the peer, use the specific IP address, port and network interface.
*
* @param[in] aPeerAddress IP address for the peer
* @param[in] aPeerPort Remote port
* @param[in] aInterfaceId The ID of local network interface to use for communication
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::TargetAddress_IP(const nl::Inet::IPAddress aPeerAddress, const uint16_t aPeerPort, const InterfaceId aInterfaceId)
{
mBinding.mAddressingOption = Binding::kAddressing_UnicastIP;
mBinding.mPeerAddress = aPeerAddress;
mBinding.mPeerPort = (aPeerPort != 0) ? aPeerPort : WEAVE_PORT;
mBinding.mInterfaceId = aInterfaceId;
return *this;
}
/**
* When communicating with the peer, use a Weave service fabric address derived from the peer's node id.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::TargetAddress_WeaveService()
{
return TargetAddress_WeaveFabric(nl::Weave::kWeaveSubnetId_Service);
}
/**
* When communicating with the peer, use a Weave fabric address derived from the peer's node id and a specified subnet.
*
* @param[in] aSubnetId The subnet id to be used in forming the Weave fabric address of the peer.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::TargetAddress_WeaveFabric(uint16_t aSubnetId)
{
mBinding.mAddressingOption = kAddressing_WeaveFabric;
mBinding.mPeerAddress = IPAddress::MakeULA(0, aSubnetId, 0); // Save the subnet in the peer address field.
return *this;
}
/**
* Use TCP to communicate with the peer.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Transport_TCP()
{
mError = WEAVE_ERROR_NOT_IMPLEMENTED;
return *this;
}
/**
* Use UDP to communicate with the peer.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Transport_UDP()
{
mBinding.mTransportOption = kTransport_UDP;
return *this;
}
/**
* Use the Weave Reliable Messaging protocol when communicating with the peer.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Transport_UDP_WRM()
{
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
mBinding.mTransportOption = kTransport_UDP_WRM;
#else // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
mError = WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
return *this;
}
/**
* Set the default WRMP configuration for exchange contexts created from this Binding object.
*
* @param[in] aWRMPConfig A reference to the new default WRMP configuration.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Transport_DefaultWRMPConfig(const nl::Weave::WRMPConfig& aWRMPConfig)
{
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
mBinding.mDefaultWRMPConfig = aWRMPConfig;
#else // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
mError = WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
return *this;
}
/**
* Use an existing Weave connection to communicate with the peer.
*/
Binding::Configuration& Binding::Configuration::Transport_ExistingConnection(WeaveConnection *)
{
mError = WEAVE_ERROR_NOT_IMPLEMENTED;
return *this;
}
/**
* Set default response timeout for exchange contexts created from this Binding object
*
* @param[in] aResponseTimeoutMsec The default response time, in ms.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Exchange_ResponseTimeoutMsec(uint32_t aResponseTimeoutMsec)
{
mBinding.mDefaultResponseTimeoutMsec = aResponseTimeoutMsec;
return *this;
}
/**
* When communicating with the peer, send and receive unencrypted (i.e. unsecured) messages.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Security_None()
{
mBinding.mSecurityOption = kSecurityOption_None;
mBinding.mKeyId = WeaveKeyId::kNone;
mBinding.mAuthMode = kWeaveAuthMode_Unauthenticated;
return *this;
}
/**
* When communicating with the peer, send and receive messages encrypted using a CASE session key
* established with the peer node.
*
* If the necessary session is not available, it will be established automatically as part of
* preparing the binding.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Security_CASESession(void)
{
mBinding.mSecurityOption = kSecurityOption_CASESession;
mBinding.mKeyId = WeaveKeyId::kNone;
mBinding.mAuthMode = kWeaveAuthMode_CASE_AnyCert;
return *this;
}
/**
* When communicating with the peer, send and receive messages encrypted using a shared CASE
* session key established with the Nest core router.
*
* If the necessary session is not available, it will be established automatically as part of
* preparing the binding.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Security_SharedCASESession(void)
{
mBinding.mSecurityOption = kSecurityOption_SharedCASESession;
mBinding.mKeyId = WeaveKeyId::kNone;
mBinding.mAuthMode = kWeaveAuthMode_CASE_ServiceEndPoint;
return *this;
}
/**
* When communicating with the peer, send and receive messages encrypted using a shared CASE
* session key established with a specified router node.
*
* If the necessary session is not available, it will be established automatically as part of
* preparing the binding.
*
* @param[in] aRouterNodeId The Weave node ID of the router with which shared CASE
* session should be established.
*
* @return A reference to the binding object.
*/
Binding::Configuration& Binding::Configuration::Security_SharedCASESession(uint64_t aRouterNodeId)
{
// This is also defined in Weave/Profiles/ServiceDirectory.h, but this is in Weave Core
// TODO: move this elsewhere.
static const uint64_t kServiceEndpoint_CoreRouter = 0x18B4300200000012ull;
// TODO: generalize this
// Only support the router to be Core Router in Nest service
VerifyOrExit(kServiceEndpoint_CoreRouter == aRouterNodeId, mError = WEAVE_ERROR_NOT_IMPLEMENTED);
Security_SharedCASESession();
exit:
return *this;
}
/**
* When communicating with the peer, send and receive messages encrypted using a specified key.
*
* @param[in] aKeyId The id of the encryption key. The specified key must be
* suitable for Weave message encryption.
*
* @return A reference to the Binding object.
*/
Binding::Configuration& Binding::Configuration::Security_Key(uint32_t aKeyId)
{
if (WeaveKeyId::IsMessageEncryptionKeyId(aKeyId))
{
mBinding.mSecurityOption = kSecurityOption_SpecificKey;
if (!WeaveKeyId::IsAppRotatingKey(aKeyId))
mBinding.mKeyId = aKeyId;
else
mBinding.mKeyId = WeaveKeyId::ConvertToCurrentAppKeyId(aKeyId);
mBinding.mAuthMode = kWeaveAuthMode_NotSpecified;
}
else
{
mError = WEAVE_ERROR_INVALID_KEY_ID;
}
return *this;
}
/**
* When communicating with the peer, send and receive messages encrypted for a specified
* Weave Application Group.
*
* @param[in] aAppGroupGlobalId The global id of the application group for which messages should
* be encrypted.
* @param[in] aRootKeyId The root key used to derive encryption keys for the specified
* Weave Application Group.
* @param[in] aUseRotatingKey True if the Weave Application Group uses rotating message keys.
*
* @return A reference to the Binding object.
*/
Binding::Configuration& Binding::Configuration::Security_AppGroupKey(uint32_t aAppGroupGlobalId, uint32_t aRootKeyId, bool aUseRotatingKey)
{
if (mError == WEAVE_NO_ERROR)
{
#if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC
mError = mBinding.mExchangeManager->FabricState->GetMsgEncKeyIdForAppGroup(aAppGroupGlobalId, aRootKeyId, aUseRotatingKey, mBinding.mKeyId);
if (mError == WEAVE_NO_ERROR)
{
mBinding.mSecurityOption = kSecurityOption_SpecificKey;
mBinding.mAuthMode = GroupKeyAuthMode(mBinding.mKeyId);
}
#else
mError = WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE;
#endif
}
return *this;
}
/**
* When communicating with the peer, send and receive messages encrypted using the specified message encryption type.
*
* @param[in] aEncType The Weave message encryption type.
*
* @return A reference to the Binding object.
*/
Binding::Configuration& Binding::Configuration::Security_EncryptionType(uint8_t aEncType)
{
mBinding.mEncType = aEncType;
return *this;
}
/**
* Set the requested authentication mode to be used to authenticate the peer.
*
* @param[in] aAuthMode The requested authentication mode.
*
* @return A reference to the Binding object.
*/
Binding::Configuration& Binding::Configuration::Security_AuthenticationMode(WeaveAuthMode aAuthMode)
{
mBinding.mAuthMode = aAuthMode;
return *this;
}
/**
* Configure the binding to allow communication with the sender of a received message.
*
* @param[in] apMsgHeader Message information structure associated with the received message.
* @param[in] apConnection The connection over which the message was received; or NULL if the message
* was not received via a connection.
* @param[in] apPktInfo Packet information for the received message.
*
*/
Binding::Configuration& Binding::Configuration::ConfigureFromMessage(
const nl::Weave::WeaveMessageHeader *apMsgHeader,
const nl::Inet::IPPacketInfo *apPktInfo,
WeaveConnection *apConnection)
{
mBinding.mPeerNodeId = apMsgHeader->SourceNodeId;
// Configure the outgoing interface only if the received message is from a
// link-local address because we need to specify the interface when we are
// sending to a link local address. Otherwise, defer to the routing logic
// to choose the outgoing interface.
TargetAddress_IP(apPktInfo->SrcAddress, apPktInfo->SrcPort,
apPktInfo->SrcAddress.IsIPv6LinkLocal() ? apPktInfo->Interface : INET_NULL_INTERFACEID);
if (apConnection != NULL)
{
Transport_ExistingConnection(apConnection);
}
else if (apMsgHeader->Flags & kWeaveMessageFlag_PeerRequestedAck)
{
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
Transport_UDP_WRM();
#else
mError = WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
}
else
{
Transport_UDP();
}
if (apMsgHeader->KeyId == WeaveKeyId::kNone)
{
Security_None();
}
else
{
Security_Key(apMsgHeader->KeyId);
Security_EncryptionType(apMsgHeader->EncryptionType);
}
return *this;
}
}; // Weave
}; // nl