| /* | 
 |  * | 
 |  *    Copyright (c) 2020 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 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/Support/WeaveFaultInjection.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 | 
 |     { } | 
 | }; | 
 |  | 
 | 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_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); | 
 |  | 
 |     // Inject a packet drop by the application. | 
 |     WEAVE_FAULT_INJECT(FaultInjection::kFault_TunnelPacketDropByPolicy, | 
 |                        toDrop = 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_Onyx; | 
 |     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) || | 
 |         !ResolveWeaveNetworkOptions(TOOL_NAME, gWeaveNodeOptions, gNetworkOptions)) | 
 |     { | 
 |         exit(EXIT_FAILURE); | 
 |     } | 
 |  | 
 | #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), | 
 |             GetRootServiceDirectoryEntry, kWeaveAuthMode_CASE_ServiceEndPoint, | 
 |             NULL, NULL, OverrideServiceConnectArguments); | 
 |     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; | 
 | } |