blob: f776a0d5a031612de9d2129f36f89410492b51fb [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
#define DEFAULT_TFE_NODE_ID 0xC0FFEE
#include <inttypes.h>
#include <unistd.h>
#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 "TestWeaveTunnelServer"
#define TUNNEL_SERVICE_INTF "service-tun0"
#define TUNNEL_SERVICE_LL_ADDR "fe80::2"
using nl::StatusReportStr;
using namespace nl::Weave::Encoding;
using namespace nl::Weave::Profiles::WeaveTunnel;
using namespace nl::Weave::TLV;
static WEAVE_ERROR SendTunnelReconnectMessage(ExchangeContext *ec, const uint16_t port = WEAVE_PORT,
const char *tunnelHostname = NULL, uint16_t hostLen = 0);
static WEAVE_ERROR VerifyAndParseStatusResponse(uint32_t profileId,
uint8_t msgType, PacketBuffer *payload,
StatusReport &outReport);
static WEAVE_ERROR SendStatusReportResponse(ExchangeContext *ec, uint32_t profileId, uint32_t tunStatusCode,
bool isRoutingRestricted = false);
WeaveTunnelServer gTunServer;
WeaveEchoServer gEchoServer;
uint32_t gCurrTestNum = 0;
bool gReconnectSent = false;
bool gStatusReportSuppressed = false;
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 ServiceTunnelInterfaceUp(InterfaceId tunIf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint64_t globalId = 0;
IPAddress tunULAAddr;
/*
* Add the service interface ULA address to the tunnel interface to ensure the selection of
* a Weave ULA as the source address for packets originating on the local node but destined
* for addresses reachable via the tunnel. Without this, the default IPv6 source address
* selection algorithm might choose an inappropriate source address, making it impossible
* for the destination node to respond.
*/
globalId = WeaveFabricIdToIPv6GlobalId(ExchangeMgr.FabricState->FabricId);
tunULAAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_Service,
nl::Weave::WeaveNodeIdToIPv6InterfaceId(ExchangeMgr.FabricState->LocalNodeId));
err = InterfaceAddAddress(tunIf, tunULAAddr, NL_INET_IPV6_MAX_PREFIX_LEN);
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(WeaveTunnel, "Failed to service address to Weave tunnel interface\n");
}
}
VirtualRouteTable::VirtualRouteTable(void)
{
memset(RouteTable, 0, sizeof(RouteTable));
}
/* Lookup Route */
int VirtualRouteTable::FindRouteEntry (IPPrefix &ip6Route)
{
int index = -1;
for (int i = 0; i < SERVICE_ROUTE_TABLE_SIZE; i++)
{
if ((ip6Route == RouteTable[i].prefix))
{
index = i;
break;
}
}
return index;
}
/* Purge entries matching the connection */
void VirtualRouteTable::RemoveRouteEntryByConnection (WeaveConnection *con)
{
for (int i = 0; i < SERVICE_ROUTE_TABLE_SIZE; i++)
{
if (con == RouteTable[i].outgoingCon[0])
{
RouteTable[i].outgoingCon[0] = NULL;
RouteTable[i].priority[0] = 0;
}
else if (con == RouteTable[i].outgoingCon[1])
{
RouteTable[i].outgoingCon[1] = NULL;
RouteTable[i].priority[1] = 0;
}
if (RouteTable[i].outgoingCon[0] == NULL &&
RouteTable[i].outgoingCon[1] == NULL)
{
memset(&RouteTable[i].prefix, 0, sizeof(IPPrefix));
}
}
}
/* Create a new route entry */
int VirtualRouteTable::NewRouteEntry (void)
{
int retIndex = -1;
for (int i = 0; i < SERVICE_ROUTE_TABLE_SIZE; i++)
{
if (RouteTable[i].prefix.IsZero())
{
retIndex = i;
break;
}
}
return retIndex;
}
/* Free a route entry at a particular index */
WEAVE_ERROR VirtualRouteTable::FreeRouteEntry (int index)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if ((index < 0) || (index >= SERVICE_ROUTE_TABLE_SIZE))
{
ExitNow();
}
memset(&RouteTable[index].prefix, 0, sizeof(IPPrefix));
RouteTable[index].routeState = kRouteEntryState_Invalid;
memset(RouteTable[index].BorderGwList, 0, sizeof(RouteTable[index].BorderGwList));
RouteTable[index].routeLifetime = INVALID_RT_LIFETIME;
exit:
return err;
}
WeaveTunnelServer::WeaveTunnelServer()
{
ExchangeMgr = NULL;
memset((void *)vRouteDB.RouteTable, 0, sizeof(vRouteDB.RouteTable));
}
void WeaveTunnelServer::HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con)
{
char ipAddrStr[64];
WEAVE_ERROR err = WEAVE_NO_ERROR;
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
WeaveLogDetail(WeaveTunnel, "Connection received from node (%s)\n", ipAddrStr);
con->OnConnectionClosed = HandleConnectionClosed;
con->AppState = &gTunServer;
err = gTunServer.mConTable.AddConnection(con);
if (err != WEAVE_NO_ERROR ||
gCurrTestNum == kTestNum_TestTunnelConnectionDownReconnect ||
gCurrTestNum == kTestNum_TestTunnelResetReconnectBackoffImmediately ||
gCurrTestNum == kTestNum_TestTunnelResetReconnectBackoffRandomized)
{
WeaveLogDetail(WeaveTunnel, "Closing Connection for test %d with node (%s)\n",
gCurrTestNum, ipAddrStr);
gTunServer.mConTable.RemoveConnection(con);
con->Close();
con = NULL;
}
}
WEAVE_ERROR WeaveTunnelServer::Init (WeaveExchangeManager *exchangeMgr)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeMgr = exchangeMgr;
MessageLayer.OnConnectionReceived = HandleConnectionReceived;
MessageLayer.OnReceiveError = HandleMessageReceiveError;
MessageLayer.OnAcceptError = HandleAcceptConnectionError;
ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_TunnelTest_Start,
HandleTunnelControlMsg,
this);
ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_TunnelTest_End,
HandleTunnelControlMsg,
this);
ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_TunnelTest_RequestTunnelConnDrop,
HandleTunnelControlMsg,
this);
ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelOpenV2, HandleTunnelControlMsg,
this);
ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelRouteUpdate, HandleTunnelControlMsg,
this);
ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelClose, HandleTunnelControlMsg,
this);
ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelLiveness, HandleTunnelControlMsg,
this);
//Create Tunnel EndPoint and populate into member mTunEP
err = CreateServiceTunEndPoint();
SuccessOrExit(err);
err = SetupServiceTunEndPoint();
SuccessOrExit(err);
//Register Recv function for TunEndPoint
mTunEP->OnPacketReceived = RecvdFromServiceTunEndPoint;
//Set the TunEndPoint appState to the WeaveTunnelServer.
mTunEP->AppState = this;
// Initialize the gEchoServer application.
err = gEchoServer.Init(ExchangeMgr);
FAIL_ERROR(err, "WeaveEchoServer.Init failed");
// Arrange to get a callback whenever an Echo Request is received.
gEchoServer.OnEchoRequestReceived = HandleEchoRequestReceived;
SecurityMgr.OnSessionEstablished = HandleSecureSessionEstablished;
SecurityMgr.OnSessionError = HandleSecureSessionError;
exit:
return err;
}
WEAVE_ERROR WeaveTunnelServer::Shutdown (void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
CloseConnections();
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelOpenV2);
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelRouteUpdate);
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelClose);
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
kMsgType_TunnelLiveness);
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_TunnelTest_Start);
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_TunnelTest_End);
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_TunnelTest_RequestTunnelConnDrop);
//Tear down the tun endpoint setup
err = TeardownServiceTunEndPoint();
gEchoServer.Shutdown();
return err;
}
WEAVE_ERROR WeaveTunnelServer::ProcessIPv6Message (WeaveConnection *con, const WeaveMessageInfo *recvMsgInfo,
PacketBuffer *msg)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
char ipAddrStr[64];
uint8_t *p = NULL;
struct ip6_hdr *ip6hdr = NULL;
WeaveTunnelHeader tunHeader;
WeaveMessageInfo msgInfo;
IPAddress destIP6Addr;
IPAddress srcIP6Addr;
IPPrefix ip6Prefix;
WeaveConnection *outgoingWeaveCon = NULL;
int8_t index = -1;
VerifyOrExit(con != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
p = msg->Start();
ip6hdr = (struct ip6_hdr *)p;
//Check destination address
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
ip6_addr_t tempAddr6;
ip6_addr_copy(tempAddr6, ip6hdr->dest);
destIP6Addr = IPAddress::FromIPv6(tempAddr6);
ip6_addr_copy(tempAddr6, ip6hdr->src);
srcIP6Addr = IPAddress::FromIPv6(tempAddr6);
#else
destIP6Addr = IPAddress::FromIPv6(ip6hdr->ip6_dst);
srcIP6Addr = IPAddress::FromIPv6(ip6hdr->ip6_src);
#endif
//Prepare the msg header
msgInfo.Clear();
//Set message version to V2
msgInfo.MessageVersion = kWeaveMessageVersion_V2;
//Set the tunneling flag
msgInfo.Flags |= kWeaveMessageFlag_TunneledData;
if (destIP6Addr.Subnet() == kWeaveSubnetId_Service)
{
//Send down Tunnel Endpoint to the network stack to be
//routed back up InetLayer to Weave.
mTunEP->Send(msg);
msg = NULL;
}
else
{
//Perform some sanity checks on the destination address
if (!destIP6Addr.IsIPv6ULA())
{
ExitNow();
}
if (destIP6Addr.Subnet() != kWeaveSubnetId_MobileDevice &&
destIP6Addr.Subnet() != kWeaveSubnetId_PrimaryWiFi &&
destIP6Addr.Subnet() != kWeaveSubnetId_ThreadMesh)
{
WeaveLogError(WeaveTunnel, "Received packet's destination unknown. Discarding\n");
ExitNow();
}
//Prepare IPPrefix for look-up in virtual route table
if (destIP6Addr.Subnet() == kWeaveSubnetId_MobileDevice)
{
ip6Prefix.IPAddr = destIP6Addr;
ip6Prefix.Length = NL_INET_IPV6_MAX_PREFIX_LEN;
}
else
{
ip6Prefix.IPAddr = IPAddress::MakeULA(destIP6Addr.GlobalId(), destIP6Addr.Subnet(), 0);
ip6Prefix.Length = NL_INET_IPV6_DEFAULT_PREFIX_LEN;
}
//Lookup virtual table
index = vRouteDB.FindRouteEntry(ip6Prefix);
if (index >= 0)
{
//Ensure Reserved size
msg->EnsureReservedSize(sizeof(tunHeader) + sizeof(msgInfo));
//Set version to V1
tunHeader.Version = kWeaveTunnelVersion_V1;
err = WeaveTunnelHeader::EncodeTunnelHeader(&tunHeader, msg);
SuccessOrExit(err);
outgoingWeaveCon = GetOutgoingConn(index);
outgoingWeaveCon->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
WeaveLogDetail(WeaveTunnel, "Received Message:Forwarding to node %" PRIX64 " (%s): len=%u.\n",
outgoingWeaveCon->PeerNodeId, ipAddrStr, msg->DataLength());
//Encrypt message
msgInfo.EncryptionType = vRouteDB.RouteTable[index].encryptionType;
msgInfo.KeyId = vRouteDB.RouteTable[index].keyId;
//Set the source and destination node ids;
msgInfo.SourceNodeId = recvMsgInfo->DestNodeId;
msgInfo.DestNodeId = outgoingWeaveCon->PeerNodeId;
err = outgoingWeaveCon->SendTunneledMessage(&msgInfo, msg);
msg = NULL;
}
else
{
WeaveLogDetail(WeaveTunnel, "No route to host\n");
//Send No Route to host;
}
}
exit:
if (msg != NULL)
{
PacketBuffer::Free(msg);
}
return 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 WeaveTunnelServer::StoreGatewayInfoForPriority(WeaveConnection *conn, uint8_t rtIndex, uint8_t priorityIndex,
uint8_t priorityVal, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo)
{
vRouteDB.RouteTable[rtIndex].priority[priorityIndex] = priorityVal;
vRouteDB.RouteTable[rtIndex].outgoingCon[priorityIndex] = conn;
//Set the Tunnel Data handler
vRouteDB.RouteTable[rtIndex].outgoingCon[priorityIndex]->OnTunneledMessageReceived = HandleTunnelDataMessage;
//Set the PeerNodeId in connection object
vRouteDB.RouteTable[rtIndex].outgoingCon[priorityIndex]->PeerNodeId = msgInfo->SourceNodeId;
vRouteDB.RouteTable[rtIndex].outgoingCon[priorityIndex]->PeerAddr = pktInfo->SrcAddress;
}
/**
* Send a reconnect message to the border gateway.
*/
WEAVE_ERROR SendTunnelReconnectMessage(ExchangeContext *ec, const uint16_t port,
const char *tunnelHostname, uint16_t hostLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer *msg = NULL;
uint8_t *p = NULL;
msg = PacketBuffer::New();
VerifyOrExit(msg, err = WEAVE_ERROR_NO_MEMORY);
if (tunnelHostname)
{
p = msg->Start();
// Add a new hostname and port for connection
LittleEndian::Write16(p, port);
memcpy(p, tunnelHostname, hostLen);
msg->SetDataLength(p + hostLen - msg->Start());
}
// Send an Tunnel Reconnect message.
err = ec->SendMessage(kWeaveProfile_Tunneling, kMsgType_TunnelReconnect, msg);
msg = NULL;
exit:
return err;
}
WEAVE_ERROR VerifyAndParseStatusResponse(uint32_t profileId,
uint8_t msgType, PacketBuffer *payload,
StatusReport &outReport)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Verify that message is a StatusReport
VerifyOrExit(profileId == kWeaveProfile_Common, err = WEAVE_ERROR_INVALID_PROFILE_ID);
VerifyOrExit(msgType == Common::kMsgType_StatusReport, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
// Parse the StatusReport.
err = StatusReport::parse(payload, outReport);
SuccessOrExit(err);
exit:
return err;
}
void WeaveTunnelServer::HandleReconnectResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId,
uint8_t msgType, PacketBuffer *payload)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
StatusReport report;
WeaveTunnelServer *tunServer = static_cast<WeaveTunnelServer *>(ec->AppState);
err = VerifyAndParseStatusResponse(profileId, msgType, payload, report);
SuccessOrExit(err);
if (report.mProfileId == kWeaveProfile_Common && report.mStatusCode == Common::kStatus_Success)
{
// Received a Success Status report
WeaveLogDetail(WeaveTunnel, "Received Status Success for TunnelReconnect message for test %d\n", gCurrTestNum);
}
else
{
err = WEAVE_ERROR_STATUS_REPORT_RECEIVED;
}
exit:
// Free the payload buffer.
if (payload)
{
PacketBuffer::Free(payload);
}
// Drop the connection
tunServer->CloseConnections();
// Discard the exchange context.
if (ec)
{
ec->Close();
ec = NULL;
}
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(WeaveTunnel, "HandleReconnectResponse FAILED with error: %ld\n", (long)err);
}
return;
}
void WeaveTunnelServer::CloseConnections(void)
{
WeaveConnection *con;
printf("closing connections\n");
for (int i = 0; i < CONNECTION_TABLE_SIZE; i++)
{
con = mConTable.mTable[i].mConnection;
if (con)
{
vRouteDB.RemoveRouteEntryByConnection(con);
con->Close();
mConTable.mTable[i].mConnection = NULL;
}
}
}
void WeaveTunnelServer::HandleTunnelControlMsg (ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId,
uint8_t msgType, PacketBuffer *payload)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveTunnelServer *tunServer = static_cast<WeaveTunnelServer *>(ec->AppState);
WeaveTunnelRoute tunRoute;
uint64_t msgFabricId = 0;
uint8_t *p = NULL;
int index = -1;
Role role;
TunnelType tunnelType;
SrcInterfaceType srcIntfType;
LivenessStrategy livenessStrategy;
uint16_t livenessTimeout;
bool isRoutingRestricted = false;
ExchangeContext *exchangeCtx = NULL;
VerifyOrExit(tunServer, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Test for WeaveTunnelTest Profile and Test messages
if (profileId == kWeaveProfile_TunnelTest_Start)
{
gCurrTestNum = msgType;
WeaveLogDetail(WeaveTunnel, "Received message for starting test %d\n", gCurrTestNum);
if (gCurrTestNum == kTestNum_TestTunnelConnectionDownReconnect)
{
WeaveLogDetail(WeaveTunnel, "TestTunnelConnectionDownReconnect: closing connections if any is already open\n");
gTunServer.CloseConnections();
}
}
else if (profileId == kWeaveProfile_TunnelTest_End)
{
WeaveLogDetail(WeaveTunnel, "Received message for stopping test %d\n", gCurrTestNum);
gCurrTestNum = 0;
}
else if (profileId == kWeaveProfile_TunnelTest_RequestTunnelConnDrop)
{
if (gCurrTestNum == kTestNum_TestQueueingOfTunneledPackets)
{
// Drop the connection
gTunServer.CloseConnections();
}
}
else 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);
if (gCurrTestNum == kTestNum_TestTunnelNoStatusReportReconnect ||
gCurrTestNum == kTestNum_TestTunnelNoStatusReportResetReconnectBackoff)
{
gStatusReportSuppressed = true;
WeaveLogDetail(WeaveTunnel, "Received TunOpenV2 message for test %d\n", gCurrTestNum);
ExitNow();
}
if (gCurrTestNum == kTestNum_TestTunnelErrorStatusReportReconnect)
{
WeaveLogDetail(WeaveTunnel, "Sending error StatusReport message for test %d\n", gCurrTestNum);
SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_UnexpectedMessage);
ExitNow();
}
if (gCurrTestNum == kTestNum_TestTunnelRestrictedRoutingOnTunnelOpen)
{
isRoutingRestricted = true;
}
for (int i = 0; i < tunRoute.numOfPrefixes; i++)
{
index = tunServer->vRouteDB.FindRouteEntry(tunRoute.tunnelRoutePrefix[i]);
if (index < 0)
{
//Not found; Create a new entry
index = tunServer->vRouteDB.NewRouteEntry();
//Fill in the details at the index
tunServer->vRouteDB.RouteTable[index].prefix = tunRoute.tunnelRoutePrefix[i];
tunServer->vRouteDB.RouteTable[index].fabricId = msgFabricId;
tunServer->vRouteDB.RouteTable[index].routeState = VirtualRouteTable::kRouteEntryState_Valid;
//Set encryption type and key id for the connection
tunServer->vRouteDB.RouteTable[index].keyId = msgInfo->KeyId;
tunServer->vRouteDB.RouteTable[index].encryptionType = msgInfo->EncryptionType;
tunServer->StoreGatewayInfoForPriority(ec->Con, index, 0, tunRoute.priority[i], pktInfo,
msgInfo);
}
else
{
// Route already exists
if (tunServer->vRouteDB.RouteTable[index].priority[0] == 0)
{
// Add a different priority entry
tunServer->StoreGatewayInfoForPriority(ec->Con, index, 0, tunRoute.priority[i], pktInfo,
msgInfo);
}
else
{
tunServer->StoreGatewayInfoForPriority(ec->Con, index, 1, tunRoute.priority[i], pktInfo,
msgInfo);
}
}
}
// Send a status report
err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success, isRoutingRestricted);
SuccessOrExit(err);
if (gCurrTestNum == kTestNum_TestReceiveReconnectFromService && !gReconnectSent)
{
// Wait for a short while and then send a Tunnel Reconnect message
sleep(1); // Sleep for a second
// Create a new ExchangeContext
exchangeCtx = tunServer->ExchangeMgr->NewContext(ec->Con, tunServer);
VerifyOrExit(exchangeCtx != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Assign the appropriate message receipt handler to the callback.
exchangeCtx->OnMessageReceived = HandleReconnectResponse;
err = SendTunnelReconnectMessage(exchangeCtx, WEAVE_PORT,
TEST_TUNNEL_RECONNECT_HOSTNAME,
strlen(TEST_TUNNEL_RECONNECT_HOSTNAME));
SuccessOrExit(err);
gReconnectSent = true;
}
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:
if (gCurrTestNum == kTestNum_TestTunnelErrorStatusReportOnTunnelClose)
{
WeaveLogDetail(WeaveTunnel, "Sending error StatusReport message for test %d\n", gCurrTestNum);
SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_UnexpectedMessage);
ExitNow();
}
err = WeaveTunnelRoute::DecodeFabricTunnelRoutes(&msgFabricId, &tunRoute, payload);
SuccessOrExit(err);
err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success);
SuccessOrExit(err);
break;
case kMsgType_TunnelLiveness:
if (gCurrTestNum == kTestNum_TestTunnelLivenessDisconnectOnNoResponse)
{
WeaveLogDetail(WeaveTunnel, "Received Tunnel Liveness message for test %d\n", gCurrTestNum);
ExitNow();
}
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 WeaveTunnelServer::HandleTunnelDataMessage (WeaveConnection *con, const WeaveMessageInfo *recvMsgInfo,
PacketBuffer *msg)
{
char ipAddrStr[64];
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveTunnelHeader tunHeader;
WeaveTunnelServer *tunServer = static_cast<WeaveTunnelServer *>(con->AppState);
//Decrypt payload
//Decapsulate Tunnel header and metadata
err = WeaveTunnelHeader::DecodeTunnelHeader(&tunHeader, msg);
SuccessOrExit(err);
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
WeaveLogDetail(WeaveTunnel, "Message from node %" PRIX64 " (%s): len=%u.\n",
con->PeerNodeId, ipAddrStr, msg->DataLength());
if (tunServer)
{
tunServer->ProcessIPv6Message(con, recvMsgInfo, msg);
}
exit:
return;
}
void WeaveTunnelServer::HandleConnectionClosed (WeaveConnection *con, WEAVE_ERROR conErr)
{
char ipAddrStr[64];
WeaveTunnelServer *tServer = static_cast<WeaveTunnelServer *>(con->AppState);
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);
}
if (tServer)
{
//Remove route table entry
tServer->vRouteDB.RemoveRouteEntryByConnection(con);
tServer->mConTable.RemoveConnection(con);
}
con->Close();
}
/* Create a new Tunnel endpoint */
WEAVE_ERROR WeaveTunnelServer::CreateServiceTunEndPoint (void)
{
WEAVE_ERROR res = WEAVE_NO_ERROR;
res = Inet.NewTunEndPoint(&mTunEP);
SuccessOrExit(res);
mTunEP->Init(&Inet);
exit:
return res;
}
/* Setup the TunEndPoint interface and configure the link-local address and fabric default route */
WEAVE_ERROR WeaveTunnelServer::SetupServiceTunEndPoint (void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
#if !WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS
IPPrefix prefix;
uint64_t globalId = 0;
IPAddress tunULAAddr;
#endif // WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
err = mTunEP->Open();
#else
err = mTunEP->Open(TUNNEL_SERVICE_INTF);
#endif
SuccessOrExit(err);
if (!mTunEP->IsInterfaceUp())
{
//Bring interface up
err = mTunEP->InterfaceUp();
SuccessOrExit(err);
}
#if !WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS
ServiceTunnelInterfaceUp(mTunEP->GetTunnelInterfaceId());
//Create prefix fd<globalId>::/48 to install route to tunnel interface
globalId = WeaveFabricIdToIPv6GlobalId(ExchangeMgr->FabricState->FabricId);
tunULAAddr = IPAddress::MakeULA(globalId, 0, 0);
prefix.IPAddr = tunULAAddr;
prefix.Length = 48;
//Add route to tunnel interface
err = SetRouteToTunnelInterface(mTunEP->GetTunnelInterfaceId(), prefix, TunEndPoint::kRouteTunIntf_Add);
SuccessOrExit(err);
#endif // WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS
exit:
if (err != WEAVE_NO_ERROR)
{
mTunEP->Free();
mTunEP = NULL;
}
return err;
}
/* Tear down TunEndpoint interface and remove the link-local address and fabric default route */
WEAVE_ERROR WeaveTunnelServer::TeardownServiceTunEndPoint (void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
#if !WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS
uint64_t globalId = 0;
IPAddress tunULAAddr;
IPPrefix prefix;
//Delete route to tunnel interface for prefix fd<globalId>::/48
globalId = WeaveFabricIdToIPv6GlobalId(ExchangeMgr->FabricState->FabricId);
tunULAAddr = IPAddress::MakeULA(globalId, 0, 0);
prefix.IPAddr = tunULAAddr;
prefix.Length = 48;
//Delete route
err = SetRouteToTunnelInterface(mTunEP->GetTunnelInterfaceId(), prefix, TunEndPoint::kRouteTunIntf_Del);
SuccessOrExit(err);
#endif // WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS
if (mTunEP->IsInterfaceUp())
{
//Bring interface down
err = mTunEP->InterfaceDown();
SuccessOrExit(err);
}
exit:
//Free Tunnel Endpoint
if (mTunEP != NULL)
{
mTunEP->Free();
mTunEP = NULL;
}
return err;
}
void WeaveTunnelServer::RecvdFromServiceTunEndPoint (TunEndPoint *tunEP, PacketBuffer *msg)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint8_t *p = NULL;
struct ip6_hdr *ip6hdr = NULL;
WeaveTunnelHeader tunHeader;
WeaveMessageInfo msgInfo;
IPAddress destIP6Addr;
IPPrefix ip6Prefix;
int8_t index = -1;
WeaveConnection *outgoingWeaveCon = NULL;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
ip6_addr_t tempAddr6;
#endif
WeaveTunnelServer *tServer = static_cast<WeaveTunnelServer *>(tunEP->AppState);
//Extract the IPv6 header to look at the destination address
p = msg->Start();
ip6hdr = (struct ip6_hdr *)p;
//Check destination address
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
ip6_addr_copy(tempAddr6, ip6hdr->dest);
destIP6Addr = IPAddress::FromIPv6(tempAddr6);
#else
destIP6Addr = IPAddress::FromIPv6(ip6hdr->ip6_dst);
#endif
if ((destIP6Addr.Subnet() == kWeaveSubnetId_PrimaryWiFi) ||
(destIP6Addr.Subnet() == kWeaveSubnetId_ThreadMesh))
{
//Prepare the msg header
msgInfo.Clear();
//Set message version to V2
msgInfo.MessageVersion = kWeaveMessageVersion_V2;
//Ensure Reserved size
msg->EnsureReservedSize(sizeof(tunHeader) + sizeof(msgInfo));
//Set version to V1
tunHeader.Version = kWeaveTunnelVersion_V1;
err = WeaveTunnelHeader::EncodeTunnelHeader(&tunHeader, msg);
SuccessOrExit(err);
//Prepare prefix for route table lookup
ip6Prefix.IPAddr = IPAddress::MakeULA(destIP6Addr.GlobalId(), destIP6Addr.Subnet(), 0);
ip6Prefix.Length = NL_INET_IPV6_DEFAULT_PREFIX_LEN;
index = tServer->vRouteDB.FindRouteEntry(ip6Prefix);
if (index >= 0)
{
//Encrypt message
msgInfo.EncryptionType = tServer->vRouteDB.RouteTable[index].encryptionType;
msgInfo.KeyId = tServer->vRouteDB.RouteTable[index].keyId;
outgoingWeaveCon = tServer->GetOutgoingConn(index);
if (outgoingWeaveCon)
{
//Set the source and destination node ids; Interchange and copy from recvd msgInfo
msgInfo.SourceNodeId = tServer->ExchangeMgr->FabricState->LocalNodeId;
msgInfo.DestNodeId = outgoingWeaveCon->PeerNodeId;
//Send over TCP Connection
err = outgoingWeaveCon->SendTunneledMessage(&msgInfo, msg);
msg = NULL;
SuccessOrExit(err);
}
else
{
printf("No appropriate outgoing connection found; Discarding message\n");
}
}
else
{
printf("No entry in route table for connection\n");
}
}
exit:
if (msg != NULL)
{
PacketBuffer::Free(msg);
msg = NULL;
}
return;
}
WeaveConnection * WeaveTunnelServer::GetOutgoingConn(uint8_t index)
{
WeaveConnection *outgoingCon = NULL;
VirtualRouteTable::RouteEntry rtEntry = vRouteDB.RouteTable[index];
if (rtEntry.outgoingCon[0] && !rtEntry.outgoingCon[1])
{
outgoingCon = rtEntry.outgoingCon[0];
}
else if (rtEntry.outgoingCon[1] && !rtEntry.outgoingCon[0])
{
outgoingCon = rtEntry.outgoingCon[1];
}
else if (rtEntry.priority[0] < rtEntry.priority[1])
{
outgoingCon = rtEntry.outgoingCon[0];
}
else
{
outgoingCon = rtEntry.outgoingCon[1];
}
return outgoingCon;
}
void WeaveTunnelServer::HandleEchoRequestReceived (uint64_t nodeId, IPAddress nodeAddr,
PacketBuffer *payload)
{
char ipAddrStr[64];
nodeAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
WeaveLogDetail(WeaveTunnel, "Echo Request from node %" PRIX64 " (%s): len=%u ... sending response.\n", nodeId, ipAddrStr,
payload->DataLength());
}
void WeaveTunnelServer::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));
WeaveLogDetail(WeaveTunnel, "Secure session established with node %" PRIX64 " (%s)\n", peerNodeId, ipAddrStr);
}
void WeaveTunnelServer::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));
}
ConnectionTable::ConnectionTable(void)
{
memset(&mTable, 0, sizeof(mTable));
}
WEAVE_ERROR ConnectionTable::AddConnection(WeaveConnection *aCon)
{
size_t i;
size_t freeEntry = CONNECTION_TABLE_SIZE;
WEAVE_ERROR err = WEAVE_NO_ERROR;
for (i = 0; i < CONNECTION_TABLE_SIZE; i++)
{
// If the entry exists already, exit and return success
VerifyOrExit(mTable[i].mConnection != aCon, /* no-op */);
if (freeEntry == CONNECTION_TABLE_SIZE &&
mTable[i].mConnection == NULL)
{
freeEntry = i;
}
}
VerifyOrExit(freeEntry < CONNECTION_TABLE_SIZE, err = WEAVE_ERROR_NO_MEMORY);
mTable[freeEntry].mConnection = aCon;
exit:
return err;
}
void ConnectionTable::RemoveConnection(WeaveConnection *aCon)
{
size_t i;
for (i = 0; i < CONNECTION_TABLE_SIZE; i++)
{
if (mTable[i].mConnection == aCon)
{
memset(&(mTable[i]), 0, sizeof(mTable[i]));
break;
}
}
}
#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);
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 = gTunServer.Init(&ExchangeMgr);
FAIL_ERROR(err, "TunnelServer.Init failed");
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
ServiceNetwork(sleepTime);
}
gTunServer.Shutdown();
ProcessStats(before, after, printStats, NULL);
PrintFaultInjectionCounters();
ShutdownWeaveStack();
ShutdownNetwork();
ShutdownSystemLayer();
#endif //WEAVE_CONFIG_ENABLE_TUNNELING
return 0;
}