blob: 2a3032d51f61b4058a2b9e0e374965326e67f0d1 [file] [log] [blame]
/*
*
* Copyright (c) 2013-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 a command line tool, weave-ping, for the
* Weave Echo Profile.
*
* The Weave Echo Profile implements two simple methods, in the
* style of ICMP ECHO REQUEST and ECHO REPLY, in which a sent
* payload is turned around by the responder and echoed back to
* the originator.
*
* The weave-ping tool implements a facility for acting as either
* the originator or responder for the Echo Profile, with a
* variety of options.
*
*/
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#include <inttypes.h>
#include <limits.h>
#include <signal.h>
#include "ToolCommon.h"
#include <Weave/WeaveVersion.h>
#include <Weave/Core/WeaveSecurityMgr.h>
#include <Weave/Profiles/security/WeaveSecurity.h>
#include <Weave/Profiles/service-directory/ServiceDirectory.h>
#include <Weave/Support/crypto/WeaveCrypto.h>
#include <Weave/Support/TimeUtils.h>
#include <Weave/Support/WeaveFaultInjection.h>
using nl::StatusReportStr;
using namespace nl::Weave::Profiles::Security;
#define TOOL_NAME "weave-ping"
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 DriveSending();
static void HandleEchoRequestReceived(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload);
static void HandleEchoResponseReceived(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload);
static void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con);
static void StartClientConnection();
static void StartSecureSession();
static void HandleConnectionComplete(WeaveConnection *con, WEAVE_ERROR conErr);
static void HandleConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr);
static void HandleSecureSessionEstablished(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, uint16_t sessionKeyId, uint64_t peerNodeId, uint8_t encType);
static void HandleSecureSessionError(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, WEAVE_ERROR localErr, uint64_t peerNodeId, StatusReport *statusReport);
static void ParseDestAddress();
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
static WEAVE_ERROR GetRootDirectoryEntry(uint8_t *, uint16_t);
static void HandleServiceMgrStatus(void *appState, WEAVE_ERROR anError, StatusReport *aReport);
#endif
bool Listening = false;
int32_t MaxEchoCount = -1;
int32_t EchoInterval = 1000000;
int32_t EchoLength = -1;
bool UseTCP = true;
bool Debug = false;
uint64_t DestNodeId;
const char *DestAddr = NULL;
IPAddress DestIPAddr; // only used for UDP
uint16_t DestPort; // only used for UDP
InterfaceId DestIntf = INET_NULL_INTERFACEID; // only used for UDP
uint64_t LastEchoTime = 0;
bool WaitingForEchoResp = false;
uint64_t EchoCount = 0;
uint64_t EchoRespCount = 0;
WeaveEchoClient EchoClient;
WeaveEchoServer EchoServer;
WeaveConnection *Con = NULL;
bool ClientConInProgress = false;
bool ClientConEstablished = false;
bool ClientSecureSessionInProgress = false;
bool ClientSecureSessionEstablished = false;
WeaveAuthMode AuthMode = kWeaveAuthMode_Unauthenticated;
// The server should not reply a StatusReport with kStatus_Busy for more than 30 seconds.
// See WeaveSecurityManager::StartSessionTimer()
uint32_t SenderBusyRespCount = 0;
const uint32_t MaxSenderBusyRespCount = 10;
// In case of SenderBusy, wait 10 seconds before trying again to establish a secure session
const uint64_t SenderBusyRespDelay = 10*nl::kMicrosecondsPerSecond;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
bool UseServiceDir = false;
ServiceDirectory::WeaveServiceManager ServiceMgr;
uint8_t ServiceDirCache[300];
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
bool UseWRMP = false;
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
enum NameResolutionStateEnum
{
kNameResolutionState_NotStarted,
kNameResolutionState_InProgress,
kNameResolutionState_Complete
} NameResolutionState = kNameResolutionState_NotStarted;
enum
{
kToolOpt_UseServiceDir = 1000,
};
static OptionDef gToolOptionDefs[] =
{
{ "listen", kNoArgument, 'L' },
{ "dest-addr", kArgumentRequired, 'D' },
{ "count", kArgumentRequired, 'c' },
{ "length", kArgumentRequired, 'l' },
{ "interval", kArgumentRequired, 'i' },
{ "tcp", kNoArgument, 't' },
{ "udp", kNoArgument, 'u' },
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
{ "wrmp", kNoArgument, 'w' },
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
{ "service-dir", kNoArgument, kToolOpt_UseServiceDir },
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
{ NULL }
};
static const char *const gToolOptionHelp =
" -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"
" -L, --listen\n"
" Listen and respond to Echo Requests sent from another node.\n"
"\n"
" -c, --count <num>\n"
" Send the specified number of Echo Requests and exit.\n"
"\n"
" -l, --length <num>\n"
" Send Echo Requests with the specified number of bytes in the payload.\n"
"\n"
" -i, --interval <ms>\n"
" Send Echo Requests at the specified interval in milliseconds.\n"
"\n"
" -t, --tcp\n"
" Use TCP to send Echo Requests. This is the default.\n"
"\n"
" -u, --udp\n"
" Use UDP to send Echo Requests.\n"
"\n"
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
" -w, --wrmp\n"
" Use UDP with Weave reliable messaging to send Echo requests.\n"
"\n"
#endif
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
" --service-dir\n"
" Use service directory to lookup the destination node address.\n"
"\n"
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
;
static OptionSet gToolOptions =
{
HandleOption,
gToolOptionDefs,
"GENERAL OPTIONS",
gToolOptionHelp
};
static HelpOptions gHelpOptions(
TOOL_NAME,
"Usage: " TOOL_NAME " [<options...>] <dest-node-id>[@<dest-host>[:<dest-port>][%<interface>]]\n"
" " TOOL_NAME " [<options...>] --listen\n",
WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT,
"Send and receive Weave Echo profile messages.\n"
);
static OptionSet *gToolOptionSets[] =
{
&gToolOptions,
&gNetworkOptions,
&gWeaveNodeOptions,
&gWRMPOptions,
&gWeaveSecurityMode,
&gCASEOptions,
&gTAKEOptions,
&gGroupKeyEncOptions,
&gDeviceDescOptions,
&gServiceDirClientOptions,
&gFaultInjectionOptions,
&gHelpOptions,
NULL
};
static void ResetTestContext(void)
{
Done = false;
WaitingForEchoResp = false;
EchoCount = 0;
EchoRespCount = 0;
SenderBusyRespCount = 0;
}
int main(int argc, char *argv[])
{
WEAVE_ERROR err;
nl::Weave::System::Stats::Snapshot before;
nl::Weave::System::Stats::Snapshot after;
const bool printStats = true;
uint32_t iter;
InitToolCommon();
#if WEAVE_CONFIG_TEST
SetupFaultInjectionContext(argc, argv);
SetSignalHandler(DoneOnHandleSIGUSR1);
#endif
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 (WeaveSecurityMode::kGroupEnc == gWeaveSecurityMode.SecurityMode && gGroupKeyEncOptions.GetEncKeyId() == WeaveKeyId::kNone)
{
PrintArgError("%s: Please specify a group encryption key id using the --group-enc-... options.\n", TOOL_NAME);
exit(EXIT_FAILURE);
}
// TODO (arg clean up): generalize code that infers node ids from local address
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();
}
InitSystemLayer();
InitNetwork();
InitWeaveStack(Listening || !UseTCP, true);
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
err = ServiceMgr.init(&ExchangeMgr, ServiceDirCache, sizeof(ServiceDirCache),
GetRootDirectoryEntry, kWeaveAuthMode_CASE_ServiceEndPoint);
if (err != WEAVE_NO_ERROR)
{
printf("ServiceMgr.init() failed with error: %s\n", ErrorStr(err));
exit(EXIT_FAILURE);
}
#endif
#if WEAVE_CONFIG_TEST
nl::Weave::Stats::UpdateSnapshot(before);
#endif
// Arrange to get called for various activities in the message layer.
MessageLayer.OnConnectionReceived = HandleConnectionReceived;
MessageLayer.OnReceiveError = HandleMessageReceiveError;
MessageLayer.OnAcceptError = HandleAcceptConnectionError;
if (!Listening)
{
// Initialize the EchoClient application.
err = EchoClient.Init(&ExchangeMgr);
if (err != WEAVE_NO_ERROR)
{
printf("WeaveEchoClient.Init failed: %s\n", ErrorStr(err));
exit(EXIT_FAILURE);
}
// Arrange to get a callback whenever an Echo Response is received.
EchoClient.OnEchoResponseReceived = HandleEchoResponseReceived;
if (!UseTCP && (WeaveSecurityMode::kPASE == gWeaveSecurityMode.SecurityMode || WeaveSecurityMode::kTAKE == gWeaveSecurityMode.SecurityMode))
{
printf("PASE/TAKE not supported for UDP.\n");
exit(EXIT_FAILURE);
}
#if !WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (!UseTCP && (WeaveSecurityMode::kCASE == gWeaveSecurityMode.SecurityMode ||
WeaveSecurityMode::kCASEShared == gWeaveSecurityMode.SecurityMode))
{
printf("CASE not supported for UDP without WRMP support.\n");
exit(EXIT_FAILURE);
}
#endif
if (WeaveSecurityMode::kPASE == gWeaveSecurityMode.SecurityMode)
AuthMode = kWeaveAuthMode_PASE_PairingCode;
else if (WeaveSecurityMode::kCASE == gWeaveSecurityMode.SecurityMode ||
WeaveSecurityMode::kCASEShared == gWeaveSecurityMode.SecurityMode)
AuthMode = kWeaveAuthMode_CASE_AnyCert;
else if (WeaveSecurityMode::kTAKE == gWeaveSecurityMode.SecurityMode)
AuthMode = kWeaveAuthMode_TAKE_IdentificationKey;
else
AuthMode = kWeaveAuthMode_Unauthenticated;
}
else
{
// Initialize the EchoServer application.
err = EchoServer.Init(&ExchangeMgr);
if (err)
{
printf("WeaveEchoServer.Init failed: %s\n", ErrorStr(err));
exit(EXIT_FAILURE);
}
// Arrange to get a callback whenever an Echo Request is received.
EchoServer.OnEchoRequestReceived = HandleEchoRequestReceived;
SecurityMgr.OnSessionEstablished = HandleSecureSessionEstablished;
SecurityMgr.OnSessionError = HandleSecureSessionError;
}
PrintNodeConfig();
if (!Listening)
{
if (!UseTCP && DestAddr != NULL)
ParseDestAddress();
if (DestNodeId == 0)
printf("Sending Echo requests to node at %s\n", DestAddr);
else if (DestAddr == NULL)
printf("Sending Echo requests to node %" PRIX64 "\n", DestNodeId);
else
printf("Sending Echo requests to node %" PRIX64 " at %s\n", DestNodeId, DestAddr);
}
else
{
printf("Listening for Echo requests...\n");
}
#if WEAVE_CONFIG_TEST
for (iter = 0; iter < gFaultInjectionOptions.TestIterations; iter++)
{
printf("Iteration %u\n", iter);
#endif // WEAVE_CONFIG_TEST
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
ServiceNetwork(sleepTime);
if (!Listening && !Done)
DriveSending();
fflush(stdout);
}
ResetTestContext();
#if WEAVE_CONFIG_TEST
if (gSigusr1Received)
{
printf("Sigusr1Received\n");
break;
}
}
#endif // WEAVE_CONFIG_TEST
EchoClient.Shutdown();
EchoServer.Shutdown();
#if WEAVE_CONFIG_TEST
ProcessStats(before, after, printStats, NULL);
PrintFaultInjectionCounters();
#endif // WEAVE_CONFIG_TEST
ShutdownWeaveStack();
ShutdownNetwork();
ShutdownSystemLayer();
return EXIT_SUCCESS;
}
bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
{
switch (id)
{
case 't':
UseTCP = true;
break;
case 'u':
UseTCP = false;
break;
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
case 'w':
UseTCP = false;
UseWRMP = true;
break;
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
case kToolOpt_UseServiceDir:
UseServiceDir = true;
break;
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
case 'L':
Listening = true;
break;
case 'c':
if (!ParseInt(arg, MaxEchoCount) || MaxEchoCount < 0)
{
PrintArgError("%s: Invalid value specified for send count: %s\n", progName, arg);
return false;
}
break;
case 'l':
if (!ParseInt(arg, EchoLength) || EchoLength < 0 || EchoLength > UINT16_MAX)
{
PrintArgError("%s: Invalid value specified for data length: %s\n", progName, arg);
return false;
}
break;
case 'i':
if (!ParseInt(arg, EchoInterval) || EchoInterval < 0 || EchoInterval > (INT32_MAX / 1000))
{
PrintArgError("%s: Invalid value specified for send interval: %s\n", progName, arg);
return false;
}
EchoInterval = EchoInterval * 1000;
break;
case 'D':
DestAddr = arg;
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 (Listening)
{
PrintArgError("%s: Please specify either a node id or --listen\n", progName);
return false;
}
// TODO (arg clean up): generalize parsing of destination node ids and addresses.
const char *nodeId = argv[0];
char *p = (char *)strchr(nodeId, '@');
if (p != NULL)
{
*p = 0;
DestAddr = p+1;
}
if (!ParseNodeId(nodeId, DestNodeId))
{
PrintArgError("%s: Invalid value specified for destination node-id: %s\n", progName, nodeId);
return false;
}
}
else
{
if (!Listening)
{
PrintArgError("%s: Please specify either a node id or --listen\n", progName);
return false;
}
}
return true;
}
void DriveSending()
{
WEAVE_ERROR err;
if (Now() < LastEchoTime + EchoInterval)
return;
if (WaitingForEchoResp)
{
printf("No response received\n");
WaitingForEchoResp = false;
// Rescan interfaces to see if we got any new IP addresses
if (!UseTCP)
{
printf("Refreshing endpoints\n");
err = MessageLayer.RefreshEndpoints();
if (err != WEAVE_NO_ERROR)
printf("WeaveMessageLayer.RefreshEndpoints() failed: %s\n", ErrorStr(err));
}
}
if (MaxEchoCount != -1 && EchoCount >= (uint64_t) MaxEchoCount)
{
if (Con != NULL)
{
printf("Connection closed\n");
Con->Close();
Con = NULL;
ClientConEstablished = false;
ClientConInProgress = false;
}
Done = true;
return;
}
if (UseTCP)
{
if (!ClientConEstablished)
{
StartClientConnection();
return;
}
}
else if (WeaveSecurityMode::kPASE == gWeaveSecurityMode.SecurityMode ||
WeaveSecurityMode::kCASE == gWeaveSecurityMode.SecurityMode ||
WeaveSecurityMode::kCASEShared == gWeaveSecurityMode.SecurityMode)
{
if (!ClientSecureSessionEstablished)
{
StartSecureSession();
return;
}
}
PacketBuffer *payloadBuf = PacketBuffer::New();
if (payloadBuf == NULL)
{
printf("Unable to allocate PacketBuffer\n");
LastEchoTime = Now();
return;
}
char *p = (char *) payloadBuf->Start();
int32_t len = sprintf(p, "Echo Message %" PRIu64 "\n", EchoCount);
if (EchoLength > payloadBuf->MaxDataLength())
EchoLength = payloadBuf->MaxDataLength();
if (EchoLength != -1)
{
if (len > EchoLength)
len = EchoLength;
else
while (len < EchoLength)
{
int32_t copyLen = EchoLength - len;
if (copyLen > len)
copyLen = len;
memcpy(p + len, p, copyLen);
len += copyLen;
}
}
payloadBuf->SetDataLength((uint16_t) len);
LastEchoTime = Now();
if (UseTCP)
{
VerifyOrDie(Con != NULL && ClientConEstablished);
}
else if (WeaveSecurityMode::kCASE == gWeaveSecurityMode.SecurityMode || WeaveSecurityMode::kPASE == gWeaveSecurityMode.SecurityMode)
{
VerifyOrDie(ClientSecureSessionEstablished);
}
if (Con != NULL)
{
err = EchoClient.SendEchoRequest(Con, payloadBuf);
payloadBuf = NULL;
}
else
{
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
EchoClient.SetRequestAck(UseWRMP);
EchoClient.SetWRMPACKDelay(gWRMPOptions.ACKDelay);
EchoClient.SetWRMPRetransInterval(gWRMPOptions.RetransInterval);
EchoClient.SetWRMPRetransCount(gWRMPOptions.RetransCount);
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (WeaveSecurityMode::kGroupEnc == gWeaveSecurityMode.SecurityMode)
{
EchoClient.EncryptionType = kWeaveEncryptionType_AES128CTRSHA1;
EchoClient.KeyId = gGroupKeyEncOptions.GetEncKeyId();
}
err = EchoClient.SendEchoRequest(DestNodeId, DestIPAddr, DestPort, DestIntf, payloadBuf);
payloadBuf = NULL;
}
if (err == WEAVE_NO_ERROR)
{
WaitingForEchoResp = true;
EchoCount++;
}
else
{
printf("WeaveEchoClient.SendEchoRequest() failed: %s\n", ErrorStr(err));
if (err == WEAVE_ERROR_KEY_NOT_FOUND)
{
ClientSecureSessionEstablished = false;
}
}
}
void HandleEchoRequestReceived(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload)
{
if (Listening)
{
char ipAddrStr[64];
nodeAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
printf("Echo Request from node %" PRIX64 " (%s): len=%u ... sending response.\n", nodeId, ipAddrStr,
payload->DataLength());
if (Debug)
DumpMemory(payload->Start(), payload->DataLength(), " ", 16);
}
}
void HandleEchoResponseReceived(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload)
{
uint32_t respTime = Now();
uint32_t transitTime = respTime - LastEchoTime;
WaitingForEchoResp = false;
EchoRespCount++;
char ipAddrStr[64];
nodeAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
printf("Echo Response from node %" PRIX64 " (%s): %" PRIu64 "/%" PRIu64 "(%.2f%%) len=%u time=%.3fms\n", nodeId, ipAddrStr,
EchoRespCount, EchoCount, ((double) EchoRespCount) * 100 / EchoCount, payload->DataLength(),
((double) transitTime) / 1000);
if (Debug)
DumpMemory(payload->Start(), payload->DataLength(), " ", 16);
}
void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con)
{
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
printf("Connection received from node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
con->OnConnectionClosed = HandleConnectionClosed;
}
void StartSecureSession()
{
WEAVE_ERROR err;
IPAddress coreRouterAddress;
// Do nothing if a secure session attempt is already in progress.
if (ClientSecureSessionInProgress)
{
return;
}
ClientSecureSessionEstablished = false;
// Set the InProgress flag to true now, because
// StartSecureSession can invoke HandleSecureSessionError, which clears
// the InProgress flag.
ClientSecureSessionInProgress = true;
switch (gWeaveSecurityMode.SecurityMode)
{
case WeaveSecurityMode::kPASE:
err = SecurityMgr.StartPASESession(Con, AuthMode, NULL,
HandleSecureSessionEstablished, HandleSecureSessionError);
break;
case WeaveSecurityMode::kCASE:
err = SecurityMgr.StartCASESession(Con, DestNodeId, DestIPAddr, WEAVE_PORT, AuthMode,
NULL, HandleSecureSessionEstablished, HandleSecureSessionError);
break;
case WeaveSecurityMode::kCASEShared:
coreRouterAddress = IPAddress::MakeULA(WeaveFabricIdToIPv6GlobalId(FabricState.FabricId),
nl::Weave::kWeaveSubnetId_Service,
nl::Weave::WeaveNodeIdToIPv6InterfaceId(kServiceEndpoint_CoreRouter));
err = SecurityMgr.StartCASESession(Con, DestNodeId, coreRouterAddress, WEAVE_PORT, AuthMode,
NULL, HandleSecureSessionEstablished, HandleSecureSessionError,
NULL, kServiceEndpoint_CoreRouter);
break;
default:
err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE;
}
if (err != WEAVE_NO_ERROR)
{
printf("SecurityMgr.StartSecureSession() failed: %s\n", ErrorStr(err));
LastEchoTime = Now();
ClientSecureSessionInProgress = false;
return;
}
}
void StartClientConnection()
{
WEAVE_ERROR err;
if (Con != NULL && Con->State == WeaveConnection::kState_Closed)
{
Con->Close();
Con = NULL;
}
// Do nothing if a connect attempt is already in progress.
if (ClientConInProgress)
return;
ClientConEstablished = false;
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
if (UseServiceDir)
{
err = ServiceMgr.connect(DestNodeId,
AuthMode,
NULL,
HandleServiceMgrStatus,
HandleConnectionComplete);
if (err != WEAVE_NO_ERROR)
{
printf("WeaveServiceManager.Connect(): failed: %s\n", ErrorStr(err));
LastEchoTime = Now();
return;
}
}
else
#endif
{
Con = MessageLayer.NewConnection();
if (Con == NULL)
{
printf("WeaveConnection.Connect failed: %s\n", ErrorStr(WEAVE_ERROR_NO_MEMORY));
LastEchoTime = Now();
Done = true;
return;
}
Con->OnConnectionComplete = HandleConnectionComplete;
Con->OnConnectionClosed = HandleConnectionClosed;
err = Con->Connect(DestNodeId, AuthMode, DestAddr);
if (err != WEAVE_NO_ERROR)
{
printf("WeaveConnection.Connect failed: %s\n", ErrorStr(err));
Con->Close();
Con = NULL;
LastEchoTime = Now();
Done = true;
return;
}
}
ClientConInProgress = true;
}
void HandleConnectionComplete(WeaveConnection *con, WEAVE_ERROR conErr)
{
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
if (conErr != WEAVE_NO_ERROR)
{
printf("Connection FAILED to node %" PRIX64 " (%s): %s\n", con->PeerNodeId, ipAddrStr, ErrorStr(conErr));
con->Close();
Con = NULL;
LastEchoTime = Now();
ClientConEstablished = false;
ClientConInProgress = false;
Done = true;
return;
}
printf("Connection established to node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
Con = con;
con->OnConnectionClosed = HandleConnectionClosed;
EchoClient.EncryptionType = con->DefaultEncryptionType;
EchoClient.KeyId = con->DefaultKeyId;
ClientConEstablished = true;
ClientConInProgress = false;
}
void HandleConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr)
{
char ipAddrStr[64];
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
if (conErr == WEAVE_NO_ERROR)
printf("Connection closed to node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
else
printf("Connection ABORTED to node %" PRIX64 " (%s): %s\n", con->PeerNodeId, ipAddrStr, ErrorStr(conErr));
if (Listening)
con->Close();
else if (con == Con)
{
con->Close();
Con = NULL;
}
WaitingForEchoResp = false;
ClientConEstablished = false;
ClientConInProgress = false;
}
void HandleSecureSessionEstablished(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, uint16_t sessionKeyId, uint64_t peerNodeId, uint8_t encType)
{
char ipAddrStr[64];
if (con != NULL)
{
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
}
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
else
{
DestIPAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
EchoClient.EncryptionType = encType;
EchoClient.KeyId = sessionKeyId;
ClientSecureSessionEstablished = true;
ClientSecureSessionInProgress = false;
}
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
printf("Secure session established with node %" PRIX64 " (%s)\n", peerNodeId, ipAddrStr);
}
void HandleSecureSessionError(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState, WEAVE_ERROR localErr, uint64_t peerNodeId, StatusReport *statusReport)
{
char ipAddrStr[64];
bool isSenderBusy = false;
if (con != NULL)
{
con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
}
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
else
{
DestIPAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
ClientSecureSessionInProgress = false;
ClientSecureSessionEstablished = false;
}
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (localErr == WEAVE_ERROR_STATUS_REPORT_RECEIVED && statusReport != NULL)
printf("FAILED to establish secure session to node %" PRIX64 " (%s): %s\n", peerNodeId, ipAddrStr, StatusReportStr(statusReport->mProfileId, statusReport->mStatusCode));
else
printf("FAILED to establish secure session to node %" PRIX64 " (%s): %s\n", peerNodeId, ipAddrStr, ErrorStr(localErr));
isSenderBusy = (localErr == WEAVE_ERROR_STATUS_REPORT_RECEIVED &&
statusReport != NULL &&
statusReport->mProfileId == nl::Weave::Profiles::kWeaveProfile_Common &&
statusReport->mStatusCode == nl::Weave::Profiles::Common::kStatus_Busy);
if (isSenderBusy)
{
// Force the main loop not to retry too soon
LastEchoTime = Now() + SenderBusyRespDelay;
SenderBusyRespCount++;
}
if (!Listening && (!isSenderBusy || SenderBusyRespCount > MaxSenderBusyRespCount))
{
Done = true;
}
}
void ParseDestAddress()
{
// NOTE: This function is only used when communicating over UDP. Code in the WeaveConnection object handles
// parsing the destination node address for TCP connections.
WEAVE_ERROR err;
const char *addr;
uint16_t addrLen;
const char *intfName;
uint16_t intfNameLen;
err = ParseHostPortAndInterface(DestAddr, strlen(DestAddr), addr, addrLen, DestPort, intfName, intfNameLen);
if (err != INET_NO_ERROR)
{
printf("Invalid destination address: %s\n", DestAddr);
exit(EXIT_FAILURE);
}
if (!IPAddress::FromString(addr, DestIPAddr))
{
printf("Invalid destination address: %s\n", DestAddr);
exit(EXIT_FAILURE);
}
if (intfName != NULL)
{
err = InterfaceNameToId(intfName, DestIntf);
if (err != INET_NO_ERROR)
{
printf("Invalid interface name: %s\n", intfName);
exit(EXIT_FAILURE);
}
}
}
#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
WEAVE_ERROR GetRootDirectoryEntry(uint8_t *buf, uint16_t bufSize)
{
return gServiceDirClientOptions.GetRootDirectoryEntry(buf, bufSize);
}
void HandleServiceMgrStatus(void* anAppState, WEAVE_ERROR anError, StatusReport *aReport)
{
if (aReport)
printf("service directory status report [%" PRIx32 ", %" PRIx32 "]", aReport->mProfileId, aReport->mStatusCode);
else
printf("service directory error %" PRIx32 "", static_cast<uint32_t>(anError));
LastEchoTime = Now();
ClientConEstablished = false;
ClientConInProgress = false;
}
#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY