blob: 86ed3f26001ddf0f820f17233472acfc231cf1b9 [file] [log] [blame]
/*
*
* Copyright (c) 2018 Google LLC.
* Copyright (c) 2014-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 Weave Mock Tunnel Service.
*
* This instantiates a Server that accepts connections from
* a border gateway and may perform routing functions
* between different border gateways or respond to ping6
* over the tunnel.
* Beyond the Tunneling profile, the server also understands
* private test profiles (@see TestWeaveTunnel.h).
* The tunnel client implemented in @see TestWeaveTunnelBR.cpp
* uses the private profiles to test various scenarios.
*/
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <unistd.h>
#include <fstream>
#include "ToolCommon.h"
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Core/WeaveSecurityMgr.h>
#include <Weave/Profiles/security/WeaveSecurity.h>
#include <Weave/Profiles/service-directory/ServiceDirectory.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Profiles/ProfileCommon.h>
#include <Weave/Profiles/echo/WeaveEcho.h>
#include <Weave/Core/WeaveTLV.h>
#include "TestWeaveTunnel.h"
#include "TestWeaveTunnelServer.h"
#if WEAVE_CONFIG_ENABLE_TUNNELING
#define TOOL_NAME "TestWeaveTunnelCASEPersistServer"
#define TUNNEL_SERVICE_INTF "service-tun0"
#define TUNNEL_SERVICE_LL_ADDR "fe80::2"
#define DEFAULT_TFE_NODE_ID (0x18b4300000000002)
#define BUFF_AVAILABLE_SIZE (1024)
#define PERSISTENT_TUNNEL_SESSION_PATH "./persistentTunnelCASE-Server"
using nl::StatusReportStr;
using namespace nl::Weave::Encoding;
using namespace nl::Weave::Profiles::WeaveTunnel;
using namespace nl::Weave::TLV;
/**
* Handler for a Weave Tunnel control message.
*/
static void HandleTunnelControlMsg(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId,
uint8_t msgType, PacketBuffer *payload);
static WEAVE_ERROR TunServerInit (WeaveExchangeManager *exchangeMgr);
static WEAVE_ERROR TunServerShutdown (void);
static void HandleConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr);
static void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con);
static void HandleSecureSessionEstablished(WeaveSecurityManager *sm, WeaveConnection *con,
void *reqState, uint16_t sessionKeyId, uint64_t peerNodeId,
uint8_t encType);
static void HandleSecureSessionError(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState,
WEAVE_ERROR localErr, uint64_t peerNodeId, StatusReport *statusReport);
static WEAVE_ERROR SendStatusReportResponse(ExchangeContext *ec, uint32_t profileId, uint32_t tunStatusCode,
bool isRoutingRestricted = false);
static void HandleSessionPersistOnTunnelClosure(nl::Weave::WeaveConnection *con);
static WEAVE_ERROR RestorePersistedTunnelCASESession(nl::Weave::WeaveConnection *con);
static bool IsPersistentTunnelSessionPresent(uint64_t peerNodeId);
static HelpOptions gHelpOptions(
TOOL_NAME,
"Usage: " TOOL_NAME " [<options...>]\n",
WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT
);
static OptionSet *gToolOptionSets[] =
{
&gNetworkOptions,
&gWeaveNodeOptions,
&gCASEOptions,
&gDeviceDescOptions,
&gFaultInjectionOptions,
&gHelpOptions,
NULL
};
void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con)
{
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
WeaveLogDetail(WeaveTunnel, "Connection received from node (%s)\n", ipAddrStr);
con->OnConnectionClosed = HandleConnectionClosed;
if (IsPersistentTunnelSessionPresent(kServiceEndpoint_WeaveTunneling))
{
RestorePersistedTunnelCASESession(con);
}
}
WEAVE_ERROR TunServerInit (WeaveExchangeManager *exchangeMgr)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
//ExchMgr = exchangeMgr;
MessageLayer.OnConnectionReceived = HandleConnectionReceived;
MessageLayer.OnReceiveError = HandleMessageReceiveError;
MessageLayer.OnAcceptError = HandleAcceptConnectionError;
ExchangeMgr.RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelOpenV2, HandleTunnelControlMsg,
NULL);
ExchangeMgr.RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelRouteUpdate, HandleTunnelControlMsg,
NULL);
ExchangeMgr.RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelClose, HandleTunnelControlMsg,
NULL);
ExchangeMgr.RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelLiveness, HandleTunnelControlMsg,
NULL);
SecurityMgr.OnSessionEstablished = HandleSecureSessionEstablished;
SecurityMgr.OnSessionError = HandleSecureSessionError;
return err;
}
WEAVE_ERROR TunServerShutdown (void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeMgr.UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelOpenV2);
ExchangeMgr.UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelRouteUpdate);
ExchangeMgr.UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelClose);
ExchangeMgr.UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelLiveness);
return err;
}
bool PersistedSessionKeyExists(const char *name)
{
return (access(name, F_OK) != -1);
}
bool IsPersistentTunnelSessionPresent(uint64_t peerNodeId)
{
const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
return PersistedSessionKeyExists(persistentTunnelSessionPath);
}
WEAVE_ERROR
SuspendAndPersistTunnelCASESession(nl::Weave::WeaveConnection *con)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
std::ofstream persistentTunOfStream;
nl::Weave::WeaveSessionKey * persistedTunnelSessionKey;
const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
VerifyOrExit(con, err = WEAVE_ERROR_INVALID_ARGUMENT);
// If exist, this functions has already been called
VerifyOrExit(!PersistedSessionKeyExists(persistentTunnelSessionPath),
err = WEAVE_ERROR_SESSION_KEY_SUSPENDED);
uint8_t buf[BUFF_AVAILABLE_SIZE];
uint16_t dataLen;
err = FabricState.FindSessionKey(con->DefaultKeyId, con->PeerNodeId, false,
persistedTunnelSessionKey);
SuccessOrExit(err);
// Set the resumptionMsgIdValid flag
persistedTunnelSessionKey->SetResumptionMsgIdsValid(true);
// This call suspends the CASE session and returns a serialized byte stream
err = FabricState.SuspendSession(persistedTunnelSessionKey->MsgEncKey.KeyId,
persistedTunnelSessionKey->NodeId,
buf,
BUFF_AVAILABLE_SIZE,
dataLen);
SuccessOrExit(err);
// If success, set goodbit in the internal state flag
// In case of failure, set failbit.
persistentTunOfStream.open(persistentTunnelSessionPath, std::ofstream::binary | std::ios::trunc);
// If not open and associated with this stream object, directly fail
VerifyOrExit(persistentTunOfStream.is_open(),
err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
// If fail, sets badbit or failbit in the internal state flags
persistentTunOfStream.write((char*)buf, dataLen);
// In case of failure, set failbit.
persistentTunOfStream.close();
// Check the stream's state flags
VerifyOrExit(persistentTunOfStream.good(),
err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
printf("Suspending and persisting of tunnel CASE session successful\n");
exit:
if (err != WEAVE_NO_ERROR)
{
printf("Suspending and persisting of tunnel CASE Session failed with Weave error: %d\n", err);
}
return err;
}
WEAVE_ERROR
RestorePersistedTunnelCASESession(nl::Weave::WeaveConnection *con)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
std::ifstream persistentCASE;
const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
if (PersistedSessionKeyExists(persistentTunnelSessionPath))
{
printf("persistent tunnel CASE session exists\n");
uint8_t buf[BUFF_AVAILABLE_SIZE];
// In case of failure, set failbit.
persistentCASE.open(persistentTunnelSessionPath, std::ifstream::binary);
VerifyOrExit(persistentCASE.is_open(),
err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
persistentCASE.seekg(0, persistentCASE.end);
uint16_t dataLen = persistentCASE.tellg();
persistentCASE.seekg(0, persistentCASE.beg);
VerifyOrExit(dataLen <= BUFF_AVAILABLE_SIZE,
err = WEAVE_ERROR_BUFFER_TOO_SMALL);
persistentCASE.read((char*) buf, dataLen);
// In case of failure, set failbit.
persistentCASE.close();
VerifyOrExit(!persistentCASE.fail(),
err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
// delete persist storage before restore session
VerifyOrExit(std::remove(persistentTunnelSessionPath) == 0,
err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
con->AuthMode = kWeaveAuthModeCategory_CASE;
err = FabricState.RestoreSession(buf, dataLen, con);
SuccessOrExit(err);
printf("Restored persistent tunnel CASE session successfully\n");
}
else
{
printf("Persistent tunnel CASE Session doesn't exist\n");
}
exit:
if (persistentCASE.is_open())
{
persistentCASE.close();
}
if (err != WEAVE_NO_ERROR)
{
printf("Restore Persistent CASE Session Failed with weave err: %d\n", err);
}
return err;
}
void HandleSessionPersistOnTunnelClosure(nl::Weave::WeaveConnection *con)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
err = SuspendAndPersistTunnelCASESession(con);
if (err != WEAVE_NO_ERROR)
{
printf("Suspending and persisting Tunnel CASE Session failed with Weave error: %d\n", err);
}
}
/* Send a tunnel control status report message */
WEAVE_ERROR SendStatusReportResponse(ExchangeContext *ec, uint32_t profileId, uint32_t tunStatusCode,
bool isRoutingRestricted)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
StatusReport tunStatusReport;
PacketBuffer *msgBuf = NULL;
nl::Weave::TLV::TLVWriter tunWriter;
nl::Weave::TLV::TLVType containerType;
uint8_t *p = NULL;
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
p = msgBuf->Start();
// Encode the profile id and status code.
LittleEndian::Write32(p, profileId);
LittleEndian::Write16(p, tunStatusCode);
msgBuf->SetDataLength(4 + 2);
if (isRoutingRestricted)
{
// Encode the Tunnel TLVData.
tunWriter.Init(msgBuf);
// Start the anonymous container that wraps the contents.
err = tunWriter.StartContainer(AnonymousTag, kTLVType_Structure, containerType);
SuccessOrExit(err);
// Write the boolean tag
err = tunWriter.PutBoolean(ProfileTag(kWeaveProfile_Tunneling, kTag_TunnelRoutingRestricted), true);
SuccessOrExit(err);
// End the anonymous container that wraps the contents.
err = tunWriter.EndContainer(containerType);
SuccessOrExit(err);
err = tunWriter.Finalize();
SuccessOrExit(err);
}
err = ec->SendMessage(kWeaveProfile_Common, Common::kMsgType_StatusReport, msgBuf, 0);
msgBuf = NULL;
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
void HandleTunnelControlMsg (ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId,
uint8_t msgType, PacketBuffer *payload)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveTunnelRoute tunRoute;
uint64_t msgFabricId = 0;
uint8_t *p = NULL;
Role role;
TunnelType tunnelType;
SrcInterfaceType srcIntfType;
LivenessStrategy livenessStrategy;
uint16_t livenessTimeout;
bool isRoutingRestricted = false;
if (profileId == kWeaveProfile_Tunneling)
{
switch (msgType)
{
case kMsgType_TunnelOpenV2:
//Decode the Tunnel Device Role, TunnelType and Source Interface
p = payload->Start();
role = static_cast<Role>(nl::Weave::Encoding::Read8(p));
tunnelType = static_cast<TunnelType>(nl::Weave::Encoding::Read8(p));
srcIntfType = static_cast<SrcInterfaceType>(nl::Weave::Encoding::Read8(p));
livenessStrategy = static_cast<LivenessStrategy>(nl::Weave::Encoding::Read8(p));
livenessTimeout = nl::Weave::Encoding::LittleEndian::Read16(p);
WeaveLogDetail(WeaveTunnel, "Received TunOpenV2 message for Tunnel role :%u, type :%u, \
srcIntf :%u, livenessStrategy :%u, livenessTimeout:%u\n",
role, tunnelType, srcIntfType, livenessStrategy, livenessTimeout);
// Set the buffer start pointer for the subsequent parsing of the fabric and routes
payload->SetStart(p);
//Save the routes and connection object
memset(&tunRoute, 0, sizeof(tunRoute));
err = WeaveTunnelRoute::DecodeFabricTunnelRoutes(&msgFabricId, &tunRoute, payload);
SuccessOrExit(err);
// Send a status report
err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success, isRoutingRestricted);
SuccessOrExit(err);
break;
case kMsgType_TunnelRouteUpdate:
// The reason this is not implemented yet is because for all practical purposes of developmental testing
// we have not needed to modify the routes that were already sent with the TunnelOpen messages. However,
// this message keeps that possibility open to modify the routes that have been sent before.
// Send a status report
err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success);
SuccessOrExit(err);
break;
case kMsgType_TunnelClose:
err = WeaveTunnelRoute::DecodeFabricTunnelRoutes(&msgFabricId, &tunRoute, payload);
SuccessOrExit(err);
err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success);
SuccessOrExit(err);
break;
case kMsgType_TunnelLiveness:
err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success);
SuccessOrExit(err);
break;
}
}
exit:
// Discard the exchange context.
ec->Close();
if (payload != NULL)
{
PacketBuffer::Free(payload);
}
}
void HandleConnectionClosed (WeaveConnection *con, WEAVE_ERROR conErr)
{
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
if (conErr == WEAVE_NO_ERROR)
{
WeaveLogDetail(WeaveTunnel, "Connection closed with node %" PRIx64 " (%s)\n",
con->PeerNodeId, ipAddrStr);
}
else
{
WeaveLogError(WeaveTunnel, "Connection ABORTED with node %" PRIx64 " (%s): %ld\n",
con->PeerNodeId, ipAddrStr, (long)conErr);
}
con->Close();
}
void HandleSecureSessionEstablished (WeaveSecurityManager *sm, WeaveConnection *con,
void *reqState, uint16_t sessionKeyId,
uint64_t peerNodeId, uint8_t encType)
{
char ipAddrStr[64] = "";
if (con)
{
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
con->DefaultKeyId = sessionKeyId;
con->PeerNodeId = peerNodeId;
}
WeaveLogDetail(WeaveTunnel, "Secure session established with node %" PRIX64 " (%s)\n", peerNodeId, ipAddrStr);
}
void HandleSecureSessionError (WeaveSecurityManager *sm, WeaveConnection *con,
void *reqState, WEAVE_ERROR localErr,
uint64_t peerNodeId, StatusReport *statusReport)
{
char ipAddrStr[64] = "";
if (con)
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
if (localErr == WEAVE_ERROR_STATUS_REPORT_RECEIVED && statusReport != NULL)
WeaveLogError(WeaveTunnel, "FAILED to establish secure session to node %" PRIX64 " (%s): %s\n", peerNodeId,
ipAddrStr, StatusReportStr(statusReport->mProfileId, statusReport->mStatusCode));
else
WeaveLogDetail(WeaveTunnel, "FAILED to establish secure session to node %" PRIX64 " (%s): %s\n", peerNodeId,
ipAddrStr, ErrorStr(localErr));
}
#endif //WEAVE_CONFIG_ENABLE_TUNNELING
int main(int argc, char *argv[])
{
#if WEAVE_CONFIG_ENABLE_TUNNELING
WEAVE_ERROR err;
nl::Weave::System::Stats::Snapshot before;
nl::Weave::System::Stats::Snapshot after;
const bool printStats = true;
gWeaveNodeOptions.LocalNodeId = DEFAULT_TFE_NODE_ID;
SetupFaultInjectionContext(argc, argv);
SetSignalHandler(DoneOnHandleSIGUSR1);
if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) ||
!ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets) ||
!ResolveWeaveNetworkOptions(TOOL_NAME, gWeaveNodeOptions, gNetworkOptions))
{
exit(EXIT_FAILURE);
}
InitSystemLayer();
InitNetwork();
InitWeaveStack(true, true);
FabricState.BoundConnectionClosedForSession = HandleSessionPersistOnTunnelClosure;
WeaveLogDetail(WeaveTunnel, "Weave Node Configuration:\n");
WeaveLogDetail(WeaveTunnel, "Fabric Id: %" PRIX64 "\n", FabricState.FabricId);
WeaveLogDetail(WeaveTunnel, "Subnet Number: %X\n", FabricState.DefaultSubnet);
WeaveLogDetail(WeaveTunnel, "Node Id: %" PRIX64 "\n", FabricState.LocalNodeId);
nl::Weave::Stats::UpdateSnapshot(before);
err = TunServerInit(&ExchangeMgr);
FAIL_ERROR(err, "TunnelServer.Init failed");
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
ServiceNetwork(sleepTime);
}
TunServerShutdown();
ProcessStats(before, after, printStats, NULL);
PrintFaultInjectionCounters();
ShutdownWeaveStack();
ShutdownNetwork();
ShutdownSystemLayer();
#endif //WEAVE_CONFIG_ENABLE_TUNNELING
return 0;
}