blob: 33b9e6122a0e6854862badb787138c4eacd9b1d5 [file] [log] [blame]
/*
*
* 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 Weave Border Gateway.
*
* This is used to instantiate a Tunnel Agent which opens a
* tunnel endpoint and forwards IPv6 packets between the
* Service connection and the tunnel endpoint.
*
*/
#define __STDC_FORMAT_MACROS
#define DEFAULT_BG_NODE_ID 0xBADCAFE
#include <inttypes.h>
#include <unistd.h>
#include <string.h>
#include "ToolCommon.h"
#include "CASEOptions.h"
#include <Weave/Support/logging/DecodedIPPacket.h>
#include <Weave/Profiles/ProfileCommon.h>
#include <Weave/Profiles/weave-tunneling/WeaveTunnelAgent.h>
#include <Weave/Profiles/weave-tunneling/WeaveTunnelCommon.h>
#include <InetLayer/InetInterface.h>
#include <Weave/Profiles/device-description/DeviceDescription.h>
#include <Weave/Profiles/vendor/nestlabs/device-description/NestProductIdentifiers.hpp>
#if WEAVE_CONFIG_ENABLE_TUNNELING
using namespace ::nl::Weave::Profiles::WeaveTunnel;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
using namespace ::nl::Weave::Profiles::DeviceDescription;
#endif
#define TOOL_NAME "mock-weave-bg"
#define DEFAULT_TFE_NODE_ID 0xC0FFEE
static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg);
static bool HandleNonOptionArgs(const char *progName, int argc, char *argv[]);
static WeaveTunnelAgent gTunAgent;
static bool gUseCASE = false;
static bool gTunnelLogging = false;
static IPAddress gDestAddr = IPAddress::Any;
static uint16_t gDestPort = 0;
static uint64_t gDestNodeId = DEFAULT_TFE_NODE_ID;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
static bool gUseServiceDirForTunnel = false;
static ServiceDirectory::WeaveServiceManager gServiceMgr;
static uint8_t gServiceDirCache[500];
#endif
static uint8_t gRole = kClientRole_BorderGateway; //Default Value
#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
static const char *gPrimaryIntf;
static const char *gBackupIntf;
static bool gEnableBackup = false;
#endif
enum
{
kToolOpt_ConnectTo = 1000,
kToolOpt_UseServiceDir,
};
static OptionDef gToolOptionDefs[] =
{
{ "role", kArgumentRequired, 'r' },
#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
{ "primary-intf", kArgumentRequired, 'P' },
{ "backup-intf", kArgumentRequired, 'B' },
{ "enable-backup", kNoArgument, 'e' },
#endif
{ "connect-to", kArgumentRequired, kToolOpt_ConnectTo },
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
{ "service-dir", kNoArgument, kToolOpt_UseServiceDir },
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
{ "case", kNoArgument, 'C' },
#if WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
{ "tunnel-log", kNoArgument, 'l' },
#endif // WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
{ NULL }
};
static const char *const gToolOptionHelp =
" -r, --role <num>\n"
" Role for local client node, i.e., 1) Border Gateway or 2) Mobile Device.\n"
"\n"
" --connect-to <addr>[:<port>][%<interface>]\n"
" Connect to the tunnel service at the supplied address.\n"
"\n"
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
" --service-dir\n"
" Use service directory to lookup the address of the tunnel server.\n"
"\n"
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
" -C, --case\n"
" Use CASE to create an authenticated session with the tunnel server.\n"
"\n"
#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
" -P, --primary-intf <interface-name>\n"
" Interface name for primary tunnel.\n"
"\n"
" -B, --backup-intf <interface-name>\n"
" Interface name for backup tunnel.\n"
"\n"
" -e, --enable-backup\n"
" Enable the use of a backup tunnel.\n"
"\n"
#endif
#if WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
" -l, --tunnel-log\n"
" Use detailed logging of Tunneled IP packet\n"
"\n"
#endif // WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
"";
static OptionSet gToolOptions =
{
HandleOption,
gToolOptionDefs,
"GENERAL OPTIONS",
gToolOptionHelp
};
static HelpOptions gHelpOptions(
TOOL_NAME,
"Usage: " TOOL_NAME " <options>\n",
WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT
);
static OptionSet *gToolOptionSets[] =
{
&gToolOptions,
&gNetworkOptions,
&gWeaveNodeOptions,
&gWRMPOptions,
&gCASEOptions,
&gDeviceDescOptions,
&gServiceDirClientOptions,
&gFaultInjectionOptions,
&gHelpOptions,
NULL
};
bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
{
switch (id)
{
case 'r':
if (!ParseInt(arg, gRole) ||
(gRole != kClientRole_BorderGateway && gRole != kClientRole_MobileDevice))
{
PrintArgError("%s: Invalid value specified for device role: %s. Possible values: (1)BorderGateway and (2)MobileDevice\n", progName, arg);
return false;
}
break;
#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
case 'P':
gPrimaryIntf = arg;
break;
case 'B':
gBackupIntf = arg;
break;
case 'e':
gEnableBackup = true;
break;
#endif // WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
case kToolOpt_ConnectTo:
{
const char *host;
uint16_t hostLen;
if (ParseHostAndPort(arg, strlen(arg), host, hostLen, gDestPort) != WEAVE_NO_ERROR)
{
PrintArgError("%s: Invalid value specified for --connect-to: %s\n", progName, arg);
return false;
}
char *hostCopy = strndup(host, hostLen);
bool isValidAddr = IPAddress::FromString(hostCopy, gDestAddr);
free(hostCopy);
if (!isValidAddr)
{
PrintArgError("%s: Invalid value specified for --connect-to (expected IP address): %s\n", progName, arg);
return false;
}
break;
}
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
case kToolOpt_UseServiceDir:
gUseServiceDirForTunnel = true;
break;
#endif
case 'C':
gUseCASE = true;
break;
case 'l':
gTunnelLogging = true;
break;
default:
PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
return false;
}
return true;
}
bool HandleNonOptionArgs(const char *progName, int argc, char *argv[])
{
if (argc > 0)
{
if (argc > 1)
{
PrintArgError("%s: Unexpected argument: %s\n", progName, argv[1]);
return false;
}
if (!ParseNodeId(argv[0], gDestNodeId))
{
PrintArgError("%s: Invalid value specified for destination node-id: %s\n", progName, argv[0]);
return false;
}
}
return true;
}
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
WEAVE_ERROR GetRootDirectoryEntry (uint8_t *buf, uint16_t bufSize)
{
return gServiceDirClientOptions.GetRootDirectoryEntry(buf, bufSize);
}
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
#if WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
void TunneledPacketTransitHandler(const PacketBuffer &pkt, TunnelPktDirection pktDir, TunnelType tunnelType, bool &toDrop)
{
DecodedIPPacket decodedPkt;
char InOrOut[9];
char tunTypeStr[9];
// Decode the packet; skip the tunnel header and pass the IP packet
decodedPkt.PacketHeaderDecode(pkt.Start() + TUN_HDR_SIZE_IN_BYTES, pkt.DataLength() - TUN_HDR_SIZE_IN_BYTES);
strncpy(InOrOut, (pktDir == kDir_Outbound) ? "Outbound" : "Inbound", sizeof(InOrOut));
strncpy(tunTypeStr, (tunnelType == kType_TunnelPrimary) ? "primary" : (tunnelType == kType_TunnelBackup) ? "backup" : "shortcut", sizeof(tunTypeStr));
WeaveLogDetail(WeaveTunnel, "Tun: %s over %s", InOrOut, tunTypeStr);
// Log the header fields
LogPacket(decodedPkt, true);
}
#endif // WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
#endif // WEAVE_CONFIG_ENABLE_TUNNELING
int main(int argc, char *argv[])
{
#if WEAVE_CONFIG_ENABLE_TUNNELING
WEAVE_ERROR err;
gWeaveNodeOptions.LocalNodeId = DEFAULT_BG_NODE_ID;
WeaveAuthMode authMode = kWeaveAuthMode_Unauthenticated;
nl::Weave::System::Stats::Snapshot before;
nl::Weave::System::Stats::Snapshot after;
const bool printStats = true;
InitToolCommon();
SetupFaultInjectionContext(argc, argv);
UseStdoutLineBuffering();
SetSignalHandler(DoneOnHandleSIGUSR1);
// Configure some alternate defaults for the device descriptor values.
gDeviceDescOptions.BaseDeviceDesc.ProductId = nl::Weave::Profiles::Vendor::Nestlabs::DeviceDescription::kNestWeaveProduct_Topaz2;
strcpy(gDeviceDescOptions.BaseDeviceDesc.SerialNumber, "mock-weave-bg");
strcpy(gDeviceDescOptions.BaseDeviceDesc.SoftwareVersion, "mock-weave-bg/1.0");
gDeviceDescOptions.BaseDeviceDesc.DeviceFeatures = WeaveDeviceDescriptor::kFeature_LinePowered;
if (argc == 1)
{
gHelpOptions.PrintBriefUsage(stderr);
exit(EXIT_FAILURE);
}
if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) ||
!ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets, HandleNonOptionArgs))
{
exit(EXIT_FAILURE);
}
if (gNetworkOptions.LocalIPv6Addr != IPAddress::Any)
{
if (!gNetworkOptions.LocalIPv6Addr.IsIPv6ULA())
{
printf("ERROR: Local address must be an IPv6 ULA\n");
exit(EXIT_FAILURE);
}
gWeaveNodeOptions.FabricId = gNetworkOptions.LocalIPv6Addr.GlobalId();
gWeaveNodeOptions.LocalNodeId = IPv6InterfaceIdToWeaveNodeId(gNetworkOptions.LocalIPv6Addr.InterfaceId());
gWeaveNodeOptions.SubnetId = gNetworkOptions.LocalIPv6Addr.Subnet();
}
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDirForTunnel && gDestAddr != IPAddress::Any)
{
printf("ERROR: Please specify only one of --connect-to or --service-dir\n");
exit(EXIT_FAILURE);
}
if (!gUseServiceDirForTunnel && gDestAddr == IPAddress::Any)
{
printf("ERROR: Please specify how to find the tunnel server using either --connect-to or --service-dir\n");
exit(EXIT_FAILURE);
}
#else
if (gDestAddr == IPAddress::Any)
{
printf("ERROR: Please specify the address of the tunnel server using --connect-to\n");
exit(EXIT_FAILURE);
}
#endif
InitSystemLayer();
InitNetwork();
InitWeaveStack(false, true);
printf("Weave Node Configuration:\n");
printf(" Fabric Id: %" PRIX64 "\n", FabricState.FabricId);
printf(" Subnet Number: %X\n", FabricState.DefaultSubnet);
printf(" Node Id: %" PRIX64 "\n", FabricState.LocalNodeId);
nl::Weave::Stats::UpdateSnapshot(before);
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
err = gServiceMgr.init(&ExchangeMgr, gServiceDirCache, sizeof(gServiceDirCache),
GetRootDirectoryEntry, kWeaveAuthMode_CASE_ServiceEndPoint);
FAIL_ERROR(err, "gServiceMgr.Init failed");
#endif
if (gUseCASE)
authMode = kWeaveAuthMode_CASE_AnyCert;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDirForTunnel)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
authMode, &gServiceMgr,
"weave-tun0", gRole);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
authMode,
"weave-tun0", gRole);
}
FAIL_ERROR(err, "TunnelAgent.Init failed");
if (gDestAddr != IPAddress::Any)
{
gTunAgent.SetDestination(gDestNodeId, gDestAddr, gDestPort);
}
#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
if (gPrimaryIntf)
{
gTunAgent.SetPrimaryTunnelInterface(gPrimaryIntf);
}
if (gBackupIntf)
{
gTunAgent.SetBackupTunnelInterface(gBackupIntf);
}
if (gEnableBackup)
{
gTunAgent.EnableBackupTunnel();
}
#endif
#if WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
if (gTunnelLogging)
{
gTunAgent.OnTunneledPacketTransit = TunneledPacketTransitHandler;
}
else
{
gTunAgent.OnTunneledPacketTransit = NULL;
}
#endif // WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
err = gTunAgent.StartServiceTunnel();
FAIL_ERROR(err, "TunnelAgent.StartServiceTunnel failed");
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
ServiceNetwork(sleepTime);
}
if (gSigusr1Received) {
printf("SIGUSR1 received: proceed to exit gracefully\n");
}
gTunAgent.StopServiceTunnel();
gTunAgent.Shutdown();
ProcessStats(before, after, printStats, NULL);
PrintFaultInjectionCounters();
ShutdownWeaveStack();
ShutdownNetwork();
ShutdownSystemLayer();
#endif // WEAVE_CONFIG_ENABLE_TUNNELING
return EXIT_SUCCESS;
}