blob: 1fe5ead3839571935a05b96c295259e61cbe6bc0 [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
#include "TestWeaveTunnel.h"
#define DEFAULT_BG_NODE_ID 0xBADCAFE
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#include "ToolCommon.h"
#include <SystemLayer/SystemTimer.h>
#include <Weave/Profiles/ProfileCommon.h>
#include <Weave/Profiles/weave-tunneling/WeaveTunnelAgent.h>
#include <SystemLayer/SystemTimer.h>
#if WEAVE_CONFIG_ENABLE_TUNNELING
using namespace ::nl::Weave::Profiles::WeaveTunnel;
#define TOOL_NAME "TestWeaveTunnelBR"
#define DEFAULT_TFE_NODE_ID 0x18b4300200000011
/* Proc file system to read IPv6 routing table. */
#ifndef NL_PATH_PROCNET_IPV6_ROUTE
#define NL_PATH_PROCNET_IPV6_ROUTE "/proc/net/ipv6_route"
#endif /* NL_PATH_PROCNET_IPV6_ROUTE */
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 void
WeaveTunnelOnStatusNotifyHandlerCB(WeaveTunnelConnectionMgr::TunnelConnNotifyReasons reason,
WEAVE_ERROR aErr, void *appCtxt);
static WEAVE_ERROR SendWeavePingMessage(void);
static void WeaveTunnelOnReconnectNotifyCB(TunnelType tunType,
const char *reconnectHost,
const uint16_t reconnectPort,
void *appCtxt);
static WEAVE_ERROR SendTunnelTestMessage(ExchangeContext *ec, uint32_t profileId, uint8_t msgType,
uint16_t sendFlags);
static void HandleTunnelTestResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId,
uint8_t msgType, PacketBuffer *payload);
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
static int AddDeleteIPv4Address(InterfaceId intf, const char *ipAddr, bool isAdd);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
WeaveTunnelAgent gTunAgent;
bool gUseCASE = false;
bool gServiceConnDropSent = false;
const char *gConnectToAddr = NULL;
IPAddress gDestAddr = IPAddress::Any;
IPAddress gRemoteDataAddr = IPAddress::Any;
uint64_t gDestNodeId = DEFAULT_TFE_NODE_ID;
uint32_t gConnectIntervalMS = 2000;
WeaveAuthMode gAuthMode = kWeaveAuthMode_Unauthenticated;
uint64_t gTestStartTime = 0;
uint32_t gCurrTestNum = 0;
uint64_t gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
bool gTestSucceeded = false;
uint8_t gEncryptionType = kWeaveEncryptionType_None;
uint16_t gKeyId = WeaveKeyId::kNone;
uint8_t gTunUpCount = 0;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
bool gUseServiceDir = false;
ServiceDirectory::WeaveServiceManager gServiceMgr;
uint8_t gServiceDirCache[100];
const char *gDirectoryServerURL = "frontdoor.integration.nestlabs.com";
#endif
#define TEST_MAX_TIMEOUT_SECS (30) // Set TCP_USER_TIMEOUT to 30 seconds
#define TEST_KEEPALIVE_INTERVAL_SECS (5) // Set TCP_KEEPALIVE INTERVAL to 5 seconds
#define TEST_GRACE_PERIOD_SECS (4)
#define TEST_TUNNEL_LIVENESS_INTERVAL_SECS (10)
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
// Used for storing the IP address upon removal to restore it at the end of the
// test case.
IPAddress gLocalIPAddr = IPAddress::Any;
InterfaceId gIntf = INET_NULL_INTERFACEID;
uint64_t gTCPUserTimeoutStartTime = 0;
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
#if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
bool gLivenessTestTunnelUp = false;
#endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
uint8_t gTunnelingDeviceRole = kClientRole_BorderGateway; //Default Value
enum
{
kToolOpt_ConnectTo = 1000,
kToolOpt_ConnectToInterval,
kToolOpt_UseServiceDir,
kToolOpt_UseCASE,
};
static OptionDef gToolOptionDefs[] =
{
{ "dest-addr", kArgumentRequired, 'D' },
{ "service-addr", kArgumentRequired, 'S' },
{ "role", kArgumentRequired, 'r' },
{ "connect-to", kArgumentRequired, kToolOpt_ConnectTo },
{ "connect-to-interval", kArgumentRequired, kToolOpt_ConnectToInterval },
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
{ "service-dir", kNoArgument, kToolOpt_UseServiceDir },
#endif
{ "case", kNoArgument, kToolOpt_UseCASE },
{ 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"
" -D, --dest-addr <host>[:<port>][%<interface>]\n"
" Send Echo Requests to a specific address rather than one\n"
" derived from the destination node id. <host> can be a hostname,\n"
" an IPv4 address or an IPv6 address. If <port> is specified, Echo\n"
" requests will be sent to the specified port. If <interface> is\n"
" specified, Echo Requests will be sent over the specified local\n"
" interface.\n"
"\n"
" NOTE: When specifying a port with an IPv6 address, the IPv6 address\n"
" must be enclosed in brackets, e.g. [fd00:0:1:1::1]:11095.\n"
"\n"
" --connect-to <addr>[:<port>][%<interface>]\n"
" Create a Weave connection to the specified address on start up. This\n"
" can be used to initiate a passive rendezvous with remote device manager.\n"
"\n"
" --connect-to-interval <ms>\n"
" Interval at which to perform connect attempts to the connect-to address.\n"
" Defaults to 2 seconds.\n"
"\n"
" -S, --service-addr <remote-ipv6-addr>\n"
" Remote destination IPv6 address for sending data traffic over tunnel.\n"
"\n"
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
" --service-dir\n"
" Use service directory to lookup destination node address.\n"
"\n"
#endif
" --case\n"
" Use CASE to create an authenticated session and encrypt messages using\n"
" the negotiated session key.\n"
"\n";
static OptionSet gToolOptions =
{
HandleOption,
gToolOptionDefs,
"GENERAL OPTIONS",
gToolOptionHelp
};
static HelpOptions gHelpOptions(
TOOL_NAME,
"Usage: " TOOL_NAME " [<options...>] [<dest-node-id>]\n",
WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT
);
static OptionSet *gToolOptionSets[] =
{
&gToolOptions,
&gNetworkOptions,
&gWeaveNodeOptions,
&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 (!arg || !ParseInt(arg, gTunnelingDeviceRole) ||
(gTunnelingDeviceRole != kClientRole_BorderGateway &&
gTunnelingDeviceRole != kClientRole_MobileDevice))
{
PrintArgError("%s: Invalid value specified for device role: %s. Possible values: (1)BorderGateway and (2)MobileDevice\n", progName, arg);
return false;
}
break;
case kToolOpt_ConnectTo:
gConnectToAddr = arg;
break;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
case kToolOpt_UseServiceDir:
gUseServiceDir = true;
break;
#endif
case kToolOpt_UseCASE:
gUseCASE = true;
break;
case 'D':
if (!ParseIPAddress(arg, gDestAddr))
{
PrintArgError("%s: Invalid value specified for destination IP address: %s\n", progName, arg);
return false;
}
break;
case 'S':
if (!ParseIPAddress(arg, gRemoteDataAddr))
{
PrintArgError("%s: Invalid value specified for remote destination IPv6 address: %s\n", progName, arg);
return false;
}
if (!gRemoteDataAddr.IsIPv6ULA())
{
PrintArgError("%s: Remote IP address %s should be IPv6 ULA. \n", progName, arg);
return false;
}
break;
case kToolOpt_ConnectToInterval:
if (!ParseInt(arg, gConnectIntervalMS))
{
PrintArgError("%s: Invalid value specified for connect-to interval: %s\n", progName, arg);
return false;
}
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)
{
WEAVE_ERROR err;
const char *host;
uint16_t hostLen, port;
using namespace nl::Weave::Encoding;
err = ParseHostAndPort(gDirectoryServerURL, strlen(gDirectoryServerURL), host, hostLen, port);
if (err != WEAVE_NO_ERROR)
{
return err;
}
if (port == 0)
{
port = WEAVE_PORT;
}
//TODO: Wrong values: Replace with correct ones when Service has Tunnel FrontEnd defined.
Write8(buf, 0x41);
LittleEndian::Write64(buf, 0x18B4300200000001ULL); // Service Endpoint Id = Directory Service
Write8(buf, 0x80);
Write8(buf, hostLen);
memcpy(buf, host, hostLen); buf += hostLen;
LittleEndian::Write16(buf, port);
return WEAVE_NO_ERROR;
}
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
/**
* Send an appropriate test message to synchronize with the Server.
*/
WEAVE_ERROR SendTunnelTestMessage(ExchangeContext *ec, uint32_t profileId,
uint8_t msgType,
uint16_t sendFlags)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer *msg = NULL;
msg = PacketBuffer::New();
VerifyOrExit(msg, err = WEAVE_ERROR_NO_MEMORY);
// Configure the encryption and signature types to be used to send the request.
ec->EncryptionType = gEncryptionType;
ec->KeyId = gKeyId;
// Arrange for messages in this exchange to go to our response handler.
ec->OnMessageReceived = HandleTunnelTestResponse;
// Send a Test message. Discard the exchange context if the send fails.
err = ec->SendMessage(profileId, msgType, msg, sendFlags);
msg = NULL;
exit:
return err;
}
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
int AddDeleteIPv4Address(InterfaceId intf, const char *ipAddr, bool isAdd)
{
char intfStr[32];
char command[64];
char addOrDel[4];
GetInterfaceName(intf, intfStr, sizeof(intfStr));
strncpy(addOrDel, isAdd ? "add" : "del", sizeof(addOrDel));
snprintf(command, sizeof(command), "ip addr %s %s/24 dev %s", addOrDel, ipAddr, intfStr);
return system(command);
}
bool GetIPAddressOfWeaveTCPConnection(char **ip)
{
FILE *fp = NULL;
char buf [1024];
bool found = false;
// Command to get Weave TCP connections from netstat output
const char *command = "netstat -an 2>/dev/null | grep 11095";
fp = popen(command, "r");
if (fp == NULL)
{
WeaveLogError(WeaveTunnel, "Can't open pipe %s : error %ld", command,
(long)errno);
exit(-1);
}
while (fgets (buf, 1024, fp) != NULL)
{
char proto[4];
int recvq, sendq;
char localAddrPort[32];
char foreignAddrPort[32];
char *foreignPort;
char state[16];
char *str;
sscanf(buf, "%s %04x %04x %s %s %s",
proto, &recvq, &sendq, localAddrPort,
foreignAddrPort, state);
// Match entry for proto == tcp AND destPort == WEAVE_PORT
foreignPort = strstr(foreignAddrPort, ":");
if ((strcmp(foreignPort, ":11095") == 0) &&
(strcmp(proto, "tcp") == 0))
{
str = strtok(localAddrPort, ":");
strncpy(*ip, str, strlen(str));
(*ip)[strlen(str)] = '\0';
found = true;
break;
}
else
{
continue;
}
}
pclose(fp);
return found;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
bool Is48BitIPv6FabricRoutePresent(void)
{
FILE *fp = NULL;
char buf [256];
bool found = false;
/* Open /proc/net/ipv6_route */
fp = fopen(NL_PATH_PROCNET_IPV6_ROUTE, "r");
if (fp == NULL)
{
WeaveLogError(WeaveTunnel, "Can't open %s : error %ld", NL_PATH_PROCNET_IPV6_ROUTE,
(long)errno);
exit(-1);
}
/* Skip the title line. */
while (fgets (buf, 256, fp) != NULL)
{
int n;
char dest[33], src[33], gw[33];
char iface[17];
int destPrefixLen, srcPrefixLen;
int metric, use, refCnt, flags;
n = sscanf (buf, "%32s %02x %32s %02x %32s %08x %08x %08x %08x %s",
dest, &destPrefixLen, src, &srcPrefixLen, gw,
&metric, &use, &refCnt, &flags, iface);
if (n != 10)
{
continue;
}
if (destPrefixLen == 48 && strstr(iface, "weav"))
{
found = true;
break;
}
}
fclose(fp);
return found;
}
/**
* Test successful WeaveTunnelAgent Initialization.
*/
static void TestWeaveTunnelAgentInit(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
gCurrTestNum = kTestNum_TestWeaveTunnelAgentInit;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.Shutdown();
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
}
/**
* Test successful WeaveTunnelAgent configuration.
*/
static void TestWeaveTunnelAgentConfigure(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
IPAddress bogusDestIPAddr = IPAddress::Any;
uint64_t bogusDestNodeId = 0x1001;
gCurrTestNum = kTestNum_TestWeaveTunnelAgentConfigure;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
SuccessOrExit(err);
// Set Auth Mode
gTunAgent.SetAuthMode(kWeaveAuthMode_Unauthenticated);
// Set bogus destination configuration
gTunAgent.SetDestination(bogusDestNodeId, bogusDestIPAddr);
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
// Start Service Tunnel should fail and return an error
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.Shutdown();
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
}
/**
* Test successful WeaveTunnelAgent Initialization.
*/
static void TestWeaveTunnelAgentShutdown(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
gCurrTestNum = kTestNum_TestWeaveTunnelAgentShutdown;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
SuccessOrExit(err);
err = gTunAgent.Shutdown();
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
}
/**
* Test WeaveTunnelAgent StartServiceTunnel without Initialization.
*/
static void TestStartTunnelWithoutInit(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
gCurrTestNum = kTestNum_TestStartTunnelWithoutInit;
gTestStartTime = Now();
// Start Service Tunnel should fail with error WEAVE_ERROR_INCORRECT_STATE
err = gTunAgent.StartServiceTunnel();
NL_TEST_ASSERT(inSuite, err == WEAVE_ERROR_INCORRECT_STATE);
}
/**
* Test back to back Start Stop and then Start Weave tunnel.
*/
static void TestBackToBackStartStopStart(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
gCurrTestNum = kTestNum_TestBackToBackStartStopStart;
gTestStartTime = Now();
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
gTunAgent.StopServiceTunnel();
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test Back to back Stop and Start after a Start completes
* by receiving a StatusReport for a TunnelOpen message.
*/
static void TestTunnelOpenCompleteThenStopStart(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
gTunUpCount = 0;
gCurrTestNum = kTestNum_TestTunnelOpenCompleteThenStopStart;
gTestStartTime = Now();
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
// Start the WeaveTunnel and when it completes do the Stop and Start
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test sending a TunnelOpen and receiving a StatusReport in response successfully.
*/
static void TestReceiveStatusReportForTunnelOpen(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
gCurrTestNum = kTestNum_TestReceiveStatusReportForTunnelOpen;
gTestStartTime = Now();
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test a successful Tunnel Open followed by a successful Tunnel Close.
*/
static void TestTunnelOpenThenTunnelClose(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelOpenThenTunnelClose;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test setting up a Standalone Tunnel.
*/
static void TestStandaloneTunnelSetup(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
gCurrTestNum = kTestNum_TestStandaloneTunnelSetup;
gTestStartTime = Now();
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.SetTunnelingDeviceRole(kClientRole_StandaloneDevice);
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test a successful Tunnel reconnect attempt on NOT receiving a StatusReport in
* response to a TunnelOpen.
*/
static void TestTunnelNoStatusReportReconnect(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = RECONNECT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelNoStatusReportReconnect;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_Start,
kTestNum_TestTunnelNoStatusReportReconnect,
0);
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_End,
kTestNum_TestTunnelNoStatusReportReconnect,
0);
SuccessOrExit(err);
gTunAgent.StopServiceTunnel(WEAVE_ERROR_NOT_CONNECTED);
}
}
exit:
if (exchangeCtxt)
{
exchangeCtxt->Close();
exchangeCtxt = NULL;
}
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test a successful Tunnel reconnect attempt on receiving a StatusReport with
* an Error status code in response to a TunnelOpen.
*/
static void TestTunnelErrorStatusReportReconnect(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = RECONNECT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelErrorStatusReportReconnect;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_Start,
kTestNum_TestTunnelErrorStatusReportReconnect,
0);
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_End,
kTestNum_TestTunnelErrorStatusReportReconnect,
0);
SuccessOrExit(err);
gTunAgent.StopServiceTunnel(WEAVE_ERROR_NOT_CONNECTED);
}
}
exit:
if (exchangeCtxt)
{
exchangeCtxt->Close();
exchangeCtxt = NULL;
}
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* When a StatusReport with an Error status code is received in response to a TunnelClose,
* shutdown the tunnel and notify application.
*/
static void TestTunnelErrorStatusReportOnTunnelClose(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = RECONNECT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelErrorStatusReportOnTunnelClose;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_Start,
kTestNum_TestTunnelErrorStatusReportOnTunnelClose,
0);
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_End,
kTestNum_TestTunnelErrorStatusReportOnTunnelClose,
0);
SuccessOrExit(err);
}
}
exit:
if (exchangeCtxt)
{
exchangeCtxt->Close();
exchangeCtxt = NULL;
}
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test to ensure the WeaveTunnelAgent tries to reconnect when a connection goes down.
*/
static void TestTunnelConnectionDownReconnect(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = RECONNECT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelConnectionDownReconnect;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_Start,
kTestNum_TestTunnelConnectionDownReconnect,
0);
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_End,
kTestNum_TestTunnelConnectionDownReconnect,
0);
SuccessOrExit(err);
gTunAgent.StopServiceTunnel(WEAVE_ERROR_NOT_CONNECTED);
}
}
exit:
if (exchangeCtxt)
{
exchangeCtxt->Close();
exchangeCtxt = NULL;
}
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test to ensure that the WeaveTunnelAgent notifies the application about the Tunnel
* being down after the maximum number of reconnect attempts have been made.
*/
static void TestCallTunnelDownAfterMaxReconnects(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = 21000;
IPAddress fakeAddr;
gCurrTestNum = kTestNum_TestCallTunnelDownAfterMaxReconnects;
gTestStartTime = Now();
// Assign a fake address for the tunnel Service. Loopback should be good enough.
IPAddress::FromString("127.0.0.1", fakeAddr);
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, fakeAddr,
gAuthMode);
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.StopServiceTunnel(WEAVE_ERROR_NOT_CONNECTED);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test receving of a Tunnel Reconnect control message and have the border gateway bring down
* the connection and reconnect.
*/
static void TestReceiveReconnectFromService(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = RECONNECT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestReceiveReconnectFromService;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
gTunAgent.OnServiceTunReconnectNotify = WeaveTunnelOnReconnectNotifyCB;
SuccessOrExit(err);
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_Start,
kTestNum_TestReceiveReconnectFromService,
0);
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_End,
kTestNum_TestReceiveReconnectFromService,
0);
SuccessOrExit(err);
gTunAgent.StopServiceTunnel(WEAVE_ERROR_NOT_CONNECTED);
}
}
exit:
if (exchangeCtxt)
{
exchangeCtxt->Close();
exchangeCtxt = NULL;
}
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test adding of the fabric default route when the Tunnel is established
*/
static void TestWARMRouteAddWhenTunnelEstablished(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
bool fabricRouteFound = false;
Done = false;
gTestSucceeded = false;
gCurrTestNum = kTestNum_TestWARMRouteAddWhenTunnelEstablished;
gTestStartTime = Now();
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
fabricRouteFound = Is48BitIPv6FabricRoutePresent();
NL_TEST_ASSERT(inSuite, fabricRouteFound == false);
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test deleting of the fabric default route when the Tunnel is stopped
*/
static void TestWARMRouteDeleteWhenTunnelStopped(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestWARMRouteDeleteWhenTunnelStopped;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test to successfully send a Weave Ping data message over the Weave Tunnel.
*/
static void TestWeavePingOverTunnel(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestWeavePingOverTunnel;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test to ensure that the WeaveTunnelAgent queues data packets when it is tryng to
* do fast reconnect attempts to the Service.
*/
static void TestQueueingOfTunneledPackets(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
// Set a longer duration for the queueing test. 3 times the default test
// duration(~15 seconds) is sufficient for the completion of this test with
// close to 100% confidence.
gMaxTestDurationMillisecs = (DEFAULT_TEST_DURATION_MILLISECS * 3);
gCurrTestNum = kTestNum_TestQueueingOfTunneledPackets;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_Start,
kTestNum_TestQueueingOfTunneledPackets,
0);
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_End,
kTestNum_TestQueueingOfTunneledPackets,
0);
SuccessOrExit(err);
gTunAgent.StopServiceTunnel(WEAVE_ERROR_NOT_CONNECTED);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
#if WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
/**
* Test gathering of tunnel statistics after performing a few tunnel operations.
*/
static void TestTunnelStatistics(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveTunnelStatistics tunnelStats;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelStatistics;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
}
// Check statistics
err = gTunAgent.GetWeaveTunnelStatistics(tunnelStats);
SuccessOrExit(err);
// Log statistics
WeaveLogDetail(WeaveTunnel, "Current Timestamp = %" PRIu64 "\n", gTunAgent.GetTimeMsec());
WeaveLogDetail(WeaveTunnel, "PrimaryTunnelDownCount = %u\n", tunnelStats.mPrimaryStats.mTunnelDownCount);
WeaveLogDetail(WeaveTunnel, "LastPrimaryTunnelDownWeaveError = %u\n", tunnelStats.mPrimaryStats.mLastTunnelDownError);
WeaveLogDetail(WeaveTunnel, "PrimaryTunnelConnAttemptCount = %u\n", tunnelStats.mPrimaryStats.mTunnelConnAttemptCount);
WeaveLogDetail(WeaveTunnel, "PrimaryTunnelTxBytes = %" PRIu64 "\n", tunnelStats.mPrimaryStats.mTxBytesToService);
WeaveLogDetail(WeaveTunnel, "PrimaryTunnelRxBytes = %" PRIu64 "\n", tunnelStats.mPrimaryStats.mRxBytesFromService);
WeaveLogDetail(WeaveTunnel, "PrimaryTunnelTxMessages = %u\n", tunnelStats.mPrimaryStats.mTxMessagesToService);
WeaveLogDetail(WeaveTunnel, "PrimaryTunnelRxMessages = %u\n", tunnelStats.mPrimaryStats.mRxMessagesFromService);
WeaveLogDetail(WeaveTunnel, "PrimaryTunnelUpTimeStamp = %" PRIu64 "\n", tunnelStats.mPrimaryStats.mLastTimeTunnelEstablished);
WeaveLogDetail(WeaveTunnel, "PrimaryTunnelDownTimeStamp = %" PRIu64 "\n", tunnelStats.mPrimaryStats.mLastTimeTunnelWentDown);
#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
WeaveLogDetail(WeaveTunnel, "BackupTunnelDownCount = %u\n", tunnelStats.mBackupStats.mTunnelDownCount);
WeaveLogDetail(WeaveTunnel, "LastBackupTunnelDownWeaveError = %u\n", tunnelStats.mBackupStats.mLastTunnelDownError);
WeaveLogDetail(WeaveTunnel, "BackupTunnelConnAttemptCount = %u\n", tunnelStats.mBackupStats.mTunnelConnAttemptCount);
WeaveLogDetail(WeaveTunnel, "BackupTunnelTxBytes = %" PRIu64 "\n", tunnelStats.mBackupStats.mTxBytesToService);
WeaveLogDetail(WeaveTunnel, "BackupTunnelRxBytes = %" PRIu64 "\n", tunnelStats.mBackupStats.mRxBytesFromService);
WeaveLogDetail(WeaveTunnel, "BackupTunnelTxMessages = %u\n", tunnelStats.mBackupStats.mTxMessagesToService);
WeaveLogDetail(WeaveTunnel, "BackupTunnelUpTimeStamp = %" PRIu64 "\n", tunnelStats.mBackupStats.mLastTimeTunnelEstablished);
WeaveLogDetail(WeaveTunnel, "BackupTunnelDownTimeStamp = %" PRIu64 "\n", tunnelStats.mBackupStats.mLastTimeTunnelWentDown);
WeaveLogDetail(WeaveTunnel, "BackupTunnelRxMessages = %u\n", tunnelStats.mBackupStats.mRxMessagesFromService);
WeaveLogDetail(WeaveTunnel, "TunnelFailoverCount = %u\n", tunnelStats.mTunnelFailoverCount);
WeaveLogDetail(WeaveTunnel, "TunnelFailoverTimestamp = %" PRIu64 "\n", tunnelStats.mLastTimeForTunnelFailover);
WeaveLogDetail(WeaveTunnel, "PrimaryAndBackupTunnelDownTimeStamp = %" PRIu64 "\n", tunnelStats.mLastTimeWhenPrimaryAndBackupWentDown);
WeaveLogDetail(WeaveTunnel, "LastTunnelFailoverWeaveError = %u\n", tunnelStats.mLastTunnelFailoverError);
#endif // WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
WeaveLogDetail(WeaveTunnel, "DroppedMessageCount = %u\n", tunnelStats.mDroppedMessagesCount);
NL_TEST_ASSERT(inSuite, tunnelStats.mPrimaryStats.mTunnelDownCount == 1);
NL_TEST_ASSERT(inSuite, tunnelStats.mPrimaryStats.mTunnelConnAttemptCount == 1);
NL_TEST_ASSERT(inSuite, tunnelStats.mPrimaryStats.mTxMessagesToService == 1);
NL_TEST_ASSERT(inSuite, tunnelStats.mPrimaryStats.mRxMessagesFromService == 1);
#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
NL_TEST_ASSERT(inSuite, tunnelStats.mTunnelFailoverCount == 0);
#endif // WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
#endif // WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
#if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
/**
* Test to successfully send a Tunnel Liveness Probe and receive a Status Report.
*/
static void TestTunnelLivenessSendAndRecvResponse(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
gMaxTestDurationMillisecs = TEST_TUNNEL_LIVENESS_INTERVAL_SECS * nl::Weave::System::kTimerFactor_milli_per_unit + 2 * DEFAULT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelLivenessSendAndRecvResponse;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
gTunAgent.ConfigurePrimaryTunnelLivenessInterval(TEST_TUNNEL_LIVENESS_INTERVAL_SECS);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
/**
* Test Closing of Tunnel when a Liveness Probe receives no response.
*/
static void TestTunnelLivenessDisconnectOnNoResponse(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
gLivenessTestTunnelUp = false;
gMaxTestDurationMillisecs = TEST_TUNNEL_LIVENESS_INTERVAL_SECS * nl::Weave::System::kTimerFactor_milli_per_unit + 2 * DEFAULT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelLivenessDisconnectOnNoResponse;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_Start,
kTestNum_TestTunnelLivenessDisconnectOnNoResponse,
0);
SuccessOrExit(err);
gTunAgent.ConfigurePrimaryTunnelLivenessInterval(TEST_TUNNEL_LIVENESS_INTERVAL_SECS);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_End,
kTestNum_TestTunnelLivenessDisconnectOnNoResponse,
0);
SuccessOrExit(err);
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
#endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
static void TestTunnelRestrictedRoutingOnTunnelOpen(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
Done = false;
gTestSucceeded = false;
gLivenessTestTunnelUp = false;
gMaxTestDurationMillisecs = DEFAULT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTunnelRestrictedRoutingOnTunnelOpen;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
SuccessOrExit(err);
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_Start,
kTestNum_TestTunnelRestrictedRoutingOnTunnelOpen,
0);
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_End,
kTestNum_TestTunnelRestrictedRoutingOnTunnelOpen,
0);
SuccessOrExit(err);
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && WEAVE_CONFIG_TUNNEL_TCP_USER_TIMEOUT_SUPPORTED && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
/**
* Test to verify that the TCP User Timeout is enforced when the IP address
* on the border gateway interface is removed.
*/
static void TestTCPUserTimeoutOnAddrRemoval(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Done = false;
gTestSucceeded = false;
char ip[32];
char *strPtr = ip;
gMaxTestDurationMillisecs = 4*RECONNECT_TEST_DURATION_MILLISECS;
gCurrTestNum = kTestNum_TestTCPUserTimeoutOnAddrRemoval;
gTestStartTime = Now();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (gUseServiceDir)
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
gAuthMode, &gServiceMgr);
}
else
#endif
{
err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
gAuthMode);
}
gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
SuccessOrExit(err);
err = gTunAgent.StartServiceTunnel();
SuccessOrExit(err);
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = TEST_SLEEP_TIME_WITHIN_LOOP_SECS;
sleepTime.tv_usec = TEST_SLEEP_TIME_WITHIN_LOOP_MICROSECS;
ServiceNetwork(sleepTime);
if (Now() < gTestStartTime + gMaxTestDurationMillisecs * System::kTimerFactor_micro_per_milli)
{
if (gTestSucceeded)
{
Done = true;
}
else
{
continue;
}
}
else // Time's up
{
gTestSucceeded = false;
Done = true;
}
if (Done)
{
gLocalIPAddr.ToString(strPtr, sizeof(ip));
// Add the IP Address back on interface
if (AddDeleteIPv4Address(gIntf, strPtr, true) < 0)
{
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
}
gTunAgent.StopServiceTunnel(WEAVE_ERROR_TUNNEL_FORCE_ABORT);
}
}
exit:
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
NL_TEST_ASSERT(inSuite, gTestSucceeded == true);
gTunAgent.Shutdown();
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS && WEAVE_CONFIG_TUNNEL_TCP_USER_TIMEOUT_SUPPORTED && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
void HandleTunnelTestResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId,
uint8_t msgType, PacketBuffer *payload)
{
switch (gCurrTestNum)
{
case kTestNum_TestWeavePingOverTunnel:
case kTestNum_TestQueueingOfTunneledPackets:
if (profileId == kWeaveProfile_Echo && msgType == kEchoMessageType_EchoResponse)
{
gTestSucceeded = true;
}
else
{
gTestSucceeded = false;
}
break;
case kTestNum_TestTunnelStatistics:
if (profileId == kWeaveProfile_Echo && msgType == kEchoMessageType_EchoResponse)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
else
{
gTestSucceeded = false;
}
break;
default:
break;
}
// Free the payload buffer.
PacketBuffer::Free(payload);
ec->Close();
}
void WeaveTunnelOnReconnectNotifyCB(TunnelType tunType,
const char *reconnectHost,
const uint16_t reconnectPort,
void *appCtxt)
{
if (gCurrTestNum == kTestNum_TestReceiveReconnectFromService)
{
if (0 == strncmp(reconnectHost, TEST_TUNNEL_RECONNECT_HOSTNAME, sizeof(TEST_TUNNEL_RECONNECT_HOSTNAME)))
{
WeaveLogDetail(WeaveTunnel, "Tunnel Reconnect received from Service for Tunnel type %d, to %s:%d\n", tunType,
reconnectHost, reconnectPort);
gTestSucceeded = true;
}
}
}
WEAVE_ERROR SendWeavePingMessage(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gRemoteDataAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
// Send Weave ping over tunnel.
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_Echo,
kEchoMessageType_EchoRequest,
0);
exit:
return err;
}
void
WeaveTunnelOnStatusNotifyHandlerCB(WeaveTunnelConnectionMgr::TunnelConnNotifyReasons reason,
WEAVE_ERROR aErr, void *appCtxt)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *exchangeCtxt = NULL;
WeaveLogDetail(WeaveTunnel, "WeaveTunnelAgent notification reason code is %d", reason);
switch (gCurrTestNum)
{
case kTestNum_TestReceiveStatusReportForTunnelOpen:
case kTestNum_TestStandaloneTunnelSetup:
case kTestNum_TestBackToBackStartStopStart:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
gTestSucceeded = true;
}
else
{
gTestSucceeded = false;
}
break;
case kTestNum_TestTunnelRestrictedRoutingOnTunnelOpen:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
// Check if we got the correct error code and the Fabric tunnel
// route is absent.
if (aErr == WEAVE_ERROR_TUNNEL_ROUTING_RESTRICTED && !Is48BitIPv6FabricRoutePresent())
{
gTestSucceeded = true;
}
else
{
gTestSucceeded = false;
}
}
break;
case kTestNum_TestTunnelOpenCompleteThenStopStart:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
if (gTunUpCount < 1)
{
gTunUpCount++;
gTunAgent.StopServiceTunnel();
gTunAgent.StartServiceTunnel();
}
else
{
gTestSucceeded = true;
}
}
else
{
gTestSucceeded = false;
}
break;
case kTestNum_TestTunnelOpenThenTunnelClose:
case kTestNum_TestTunnelErrorStatusReportOnTunnelClose:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
else if (reason == WeaveTunnelConnectionMgr::kStatus_TunDown)
{
gTestSucceeded = true;
}
break;
#if WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
case kTestNum_TestTunnelStatistics:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
err = SendWeavePingMessage();
SuccessOrExit(err);
}
else if (reason == WeaveTunnelConnectionMgr::kStatus_TunDown)
{
gTestSucceeded = true;
}
break;
#endif // WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
case kTestNum_TestTunnelNoStatusReportReconnect:
case kTestNum_TestTunnelConnectionDownReconnect:
case kTestNum_TestTunnelErrorStatusReportReconnect:
case kTestNum_TestWeaveTunnelAgentConfigure:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryConnError)
{
gTestSucceeded = true;
}
break;
case kTestNum_TestCallTunnelDownAfterMaxReconnects:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunDown)
{
gTestSucceeded = true;
}
else if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryConnError)
{
WeaveLogDetail(WeaveTunnel, "Tun Connection Error");
}
break;
case kTestNum_TestWeavePingOverTunnel:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
err = SendWeavePingMessage();
SuccessOrExit(err);
}
else
{
gTestSucceeded = false;
}
break;
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && WEAVE_CONFIG_TUNNEL_TCP_USER_TIMEOUT_SUPPORTED && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
case kTestNum_TestTCPUserTimeoutOnAddrRemoval:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
char ip[32];
char *strPtr = ip;
bool found = GetIPAddressOfWeaveTCPConnection(&strPtr);
if (!found)
{
gTestSucceeded = false;
break;
}
IPAddress::FromString(strPtr, gLocalIPAddr);
// Configure the TCP User Timeout
err = gTunAgent.ConfigurePrimaryTunnelTimeout(TEST_MAX_TIMEOUT_SECS);
SuccessOrExit(err);
// Get the interface matching the IP
err = Inet.GetInterfaceFromAddr(gLocalIPAddr, gIntf);
SuccessOrExit(err);
// Remove IP Address on interface
if (AddDeleteIPv4Address(gIntf, strPtr, false) < 0)
{
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
}
// Send some data
err = SendWeavePingMessage();
SuccessOrExit(err);
// Mark the time for getting a connection reconnect up call when
// TCP User timeout happens.
gTCPUserTimeoutStartTime = Now();
}
else if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryConnError &&
aErr == INET_ERROR_TCP_USER_TIMEOUT)
{
if ((Now() - gTCPUserTimeoutStartTime > TEST_MAX_TIMEOUT_SECS * nl::Weave::System::kTimerFactor_micro_per_unit) ||
(Now() - gTCPUserTimeoutStartTime < (TEST_MAX_TIMEOUT_SECS + TEST_GRACE_PERIOD_SECS) * nl::Weave::System::kTimerFactor_micro_per_unit))
{
gTestSucceeded = true;
}
}
break;
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS && WEAVE_CONFIG_TUNNEL_TCP_USER_TIMEOUT_SUPPORTED && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
#if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
case kTestNum_TestTunnelLivenessSendAndRecvResponse:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryLiveness &&
aErr == WEAVE_NO_ERROR)
{
gTestSucceeded = true;
}
break;
case kTestNum_TestTunnelLivenessDisconnectOnNoResponse:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
gLivenessTestTunnelUp = true;
}
else if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryConnError &&
(aErr == WEAVE_ERROR_TIMEOUT || aErr == INET_ERROR_TCP_USER_TIMEOUT))
{
if (gLivenessTestTunnelUp)
{
gTestSucceeded = true;
}
else
{
gTestSucceeded = false;
}
}
break;
#endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
case kTestNum_TestWARMRouteAddWhenTunnelEstablished:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
if (Is48BitIPv6FabricRoutePresent())
{
gTestSucceeded = true;
}
else
{
gTestSucceeded = false;
}
}
break;
case kTestNum_TestWARMRouteDeleteWhenTunnelStopped:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
gTunAgent.StopServiceTunnel(WEAVE_NO_ERROR);
}
else if (reason == WeaveTunnelConnectionMgr::kStatus_TunDown)
{
if (Is48BitIPv6FabricRoutePresent())
{
gTestSucceeded = false;
}
else
{
gTestSucceeded = true;
}
}
break;
case kTestNum_TestReceiveReconnectFromService:
WeaveLogDetail(WeaveTunnel, "Tunnel established; Expecting a Reconnect\n");
break;
case kTestNum_TestQueueingOfTunneledPackets:
if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
{
if (!gServiceConnDropSent)
{
IPAddress fakeAddr;
// Configure wrong IP address to generate connection error
IPAddress::FromString("127.0.0.1", fakeAddr);
gTunAgent.SetDestination(gDestNodeId, fakeAddr);
// Now, instruct Service to drop connection to trigger reconnect attempt
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gDestAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_TunnelTest_RequestTunnelConnDrop,
kTestNum_TestQueueingOfTunneledPackets,
0);
SuccessOrExit(err);
gServiceConnDropSent = true;
}
}
else if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryConnError)
{
if (gServiceConnDropSent)
{
// Send Weave ping over tunnel which should get queued.
exchangeCtxt = ExchangeMgr.NewContext(gDestNodeId, gRemoteDataAddr, &gTunAgent);
VerifyOrExit(exchangeCtxt, err = WEAVE_ERROR_NO_MEMORY);
err = SendTunnelTestMessage(exchangeCtxt, kWeaveProfile_Echo,
kEchoMessageType_EchoRequest,
0);
SuccessOrExit(err);
// Set correct Destination address configuration for subsequent successful reconnection
// and delivery of queued ping request.
gTunAgent.SetDestination(gDestNodeId, gDestAddr);
}
else
{
gTestSucceeded = false;
}
}
break;
default:
break;
}
exit:
if (err != WEAVE_NO_ERROR)
{
if (exchangeCtxt)
{
exchangeCtxt->Close();
exchangeCtxt = NULL;
}
gTestSucceeded = false;
}
}
static const nlTest tunnelTests[] = {
NL_TEST_DEF("TestWeaveTunnelAgentInit", TestWeaveTunnelAgentInit),
NL_TEST_DEF("TestWeaveTunnelAgentConfigure", TestWeaveTunnelAgentConfigure),
NL_TEST_DEF("TestWeaveTunnelAgentShutdown", TestWeaveTunnelAgentShutdown),
NL_TEST_DEF("TestStartTunnelWithoutInit", TestStartTunnelWithoutInit),
NL_TEST_DEF("TestBackToBackStartStopStart", TestBackToBackStartStopStart),
NL_TEST_DEF("TestTunnelOpenCompleteThenStopStart", TestTunnelOpenCompleteThenStopStart),
NL_TEST_DEF("TestReceiveStatusReportForTunnelOpen", TestReceiveStatusReportForTunnelOpen),
NL_TEST_DEF("TestTunnelOpenThenTunnelClose", TestTunnelOpenThenTunnelClose),
NL_TEST_DEF("TestStandaloneTunnelSetup", TestStandaloneTunnelSetup),
NL_TEST_DEF("TestTunnelNoStatusReportReconnect", TestTunnelNoStatusReportReconnect),
NL_TEST_DEF("TestTunnelErrorStatusReportReconnect", TestTunnelErrorStatusReportReconnect),
NL_TEST_DEF("TestTunnelErrorStatusReportOnTunnelClose", TestTunnelErrorStatusReportOnTunnelClose),
NL_TEST_DEF("TestTunnelConnectionDownReconnect", TestTunnelConnectionDownReconnect),
NL_TEST_DEF("TestCallTunnelDownAfterMaxReconnects", TestCallTunnelDownAfterMaxReconnects),
NL_TEST_DEF("TestReceiveReconnectFromService", TestReceiveReconnectFromService),
NL_TEST_DEF("TestWARMRouteAddWhenTunnelEstablished", TestWARMRouteAddWhenTunnelEstablished),
NL_TEST_DEF("TestWARMRouteDeleteWhenTunnelStopped", TestWARMRouteDeleteWhenTunnelStopped),
NL_TEST_DEF("TestWeavePingOverTunnel", TestWeavePingOverTunnel),
NL_TEST_DEF("TestQueueingOfTunneledPackets", TestQueueingOfTunneledPackets),
#if WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
NL_TEST_DEF("TestTunnelStatistics", TestTunnelStatistics),
#endif // WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
#if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
NL_TEST_DEF("TestTunnelLivenessSendAndRecvResponse", TestTunnelLivenessSendAndRecvResponse),
NL_TEST_DEF("TestTunnelLivenessDisconnectOnNoResponse", TestTunnelLivenessDisconnectOnNoResponse),
#endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
NL_TEST_DEF("TestTunnelRestrictedRoutingOnTunnelOpen", TestTunnelRestrictedRoutingOnTunnelOpen),
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && WEAVE_CONFIG_TUNNEL_TCP_USER_TIMEOUT_SUPPORTED && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
NL_TEST_DEF("TestTCPUserTimeoutOnAddrRemoval", TestTCPUserTimeoutOnAddrRemoval),
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS && WEAVE_CONFIG_TUNNEL_TCP_USER_TIMEOUT_SUPPORTED && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
NL_TEST_SENTINEL()
};
#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;
nlTestSuite tunnelTestSuite = {
"WeaveTunnel",
&tunnelTests[0]
};
nl_test_set_output_style(OUTPUT_CSV);
UseStdoutLineBuffering();
SetSIGUSR1Handler();
// Set default Remote data IP address to be of the Service Tunnel Endpoint;
IPAddress::FromString("fd00:0:1:5:1ab4:3002:0000:0011", gRemoteDataAddr);
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())
{
WeaveLogError(WeaveTunnel, "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();
}
InitSystemLayer();
InitNetwork();
InitWeaveStack(false, true);
if (gDestAddr == IPAddress::Any)
{
gDestAddr = FabricState.SelectNodeAddress(gDestNodeId);
}
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);
if (gConnectToAddr != NULL)
{
IPAddress::FromString(gConnectToAddr, gDestAddr);
}
#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)
gAuthMode = kWeaveAuthMode_CASE_AnyCert;
// Run all tests in Suite
nlTestRunner(&tunnelTestSuite, NULL);
ShutdownWeaveStack();
ShutdownNetwork();
ShutdownSystemLayer();
return nlTestRunnerStats(&tunnelTestSuite);
#endif // WEAVE_CONFIG_ENABLE_TUNNELING
}