blob: 59d49ee94c628ba88f73efd952c636167d815d4c [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 constants, globals and interfaces common
* to and used by all Weave test applications and tools.
*
* NOTE: These do not comprise a public part of the Weave API and
* are subject to change without notice.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdint.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <new>
#include "ToolCommon.h"
#include "TestGroupKeyStore.h"
#include <Weave/Support/NestCerts.h>
#include <SystemLayer/SystemTimer.h>
#include <SystemLayer/SystemFaultInjection.h>
#include <Weave/Support/WeaveFaultInjection.h>
#include <InetLayer/InetFaultInjection.h>
#include <Weave/Support/crypto/WeaveCrypto.h>
#include <Weave/Support/crypto/WeaveRNG.h>
#if WEAVE_CONFIG_ENABLE_TUNNELING
#include <InetLayer/TunEndPoint.h>
#include <Weave/Profiles/weave-tunneling/WeaveTunnelAgent.h>
#endif // WEAVE_CONFIG_ENABLE_TUNNELING
#include "CASEOptions.h"
#include "KeyExportOptions.h"
#include "TAKEOptions.h"
#include "DeviceDescOptions.h"
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#include <lwip/netif.h>
#include <netif/etharp.h>
#include "TapInterface.h"
#include <lwip/tcpip.h>
#include <lwip/sys.h>
#include <lwip/dns.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if CONFIG_BLE_PLATFORM_BLUEZ
//TODO insert shim for common BLE runloop interface:
#include "BluezBleDelegates.h"
#include "BluezHelperCode.h"
static BleLayer sBle;
static nl::Ble::Platform::BlueZ::BluezBleApplicationDelegate sBleApplicationDelegate;
static nl::Ble::Platform::BlueZ::BluezBlePlatformDelegate sBlePlatformDelegate(&sBle);
nl::Ble::Platform::BlueZ::BluezBlePlatformDelegate *getBluezPlatformDelegate()
{
return &sBlePlatformDelegate;
}
#endif /* CONFIG_BLE_PLATFORM_BLUEZ */
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#include <arpa/inet.h>
#include <sys/select.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
static sys_mbox* sLwIPEventQueue = NULL;
static unsigned int sLwIPAcquireCount = 0;
static void AcquireLwIP(void)
{
if (sLwIPAcquireCount++ == 0) {
sys_mbox_new(&sLwIPEventQueue, 100);
}
}
static void ReleaseLwIP(void)
{
if (sLwIPAcquireCount > 0 && --sLwIPAcquireCount == 0) {
tcpip_finish(NULL, NULL);
}
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
System::Layer SystemLayer;
InetLayer Inet;
#if INET_CONFIG_ENABLE_DNS_RESOLVER
IPAddress DNSServerAddr = IPAddress::Any;
#endif // INET_CONFIG_ENABLE_DNS_RESOLVER
#if WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
TapInterface tapIF;
struct netif netIF; // interface to filter
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
static bool NetworkIsReady();
static void OnLwIPInitComplete(void *arg);
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
char DefaultTapDeviceName[32];
bool Done = false;
bool gSigusr1Received = false;
uint16_t sTestDefaultUDPSessionKeyId = WeaveKeyId::MakeSessionKeyId(1);
uint16_t sTestDefaultTCPSessionKeyId = WeaveKeyId::MakeSessionKeyId(2);
uint16_t sTestDefaultSessionKeyId = WeaveKeyId::MakeSessionKeyId(42);
bool sSuppressAccessControls = false;
// Perform general *non-network* initialization for test applications
void InitToolCommon()
{
WEAVE_ERROR err;
unsigned int randSeed;
// Initialize the platform secure random data source. If the underlying random data source is OpenSSL,
// entropy will be acquired via the standard OpenSSL source and the entropy function argument will be
// ignored. If the underlying random source is the Nest DRBG implementation, or another similar platform
// implementation, entropy will be sourced from /dev/(u)random using the GetDRBGSeedDevRandom() function.
err = nl::Weave::Platform::Security::InitSecureRandomDataSource(NULL, 64, NULL, 0);
FAIL_ERROR(err, "InitSecureRandomDataSource() failed");
// Initialized the rand() generator with a seed from the secure random data source.
err = nl::Weave::Platform::Security::GetSecureRandomData((uint8_t *)&randSeed, sizeof(randSeed));
FAIL_ERROR(err, "Random number generator seeding failed");
srand(randSeed);
UseStdoutLineBuffering();
}
static void ExitOnSIGUSR1Handler(int signum)
{
// exit() allows us a slightly better clean up (gcov data) than SIGINT's exit
exit(0);
}
// We set a hook to exit when we receive SIGUSR1, SIGTERM or SIGHUP
void SetSIGUSR1Handler(void)
{
SetSignalHandler(ExitOnSIGUSR1Handler);
}
void DoneOnHandleSIGUSR1(int signum)
{
Done = true;
gSigusr1Received = true;
}
void SetSignalHandler(SignalHandler handler)
{
struct sigaction sa;
int signals[] = { SIGUSR1 };
size_t i;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler;
for (i = 0; i < sizeof(signals)/sizeof(signals[0]); i++)
{
if (sigaction(signals[i], &sa, NULL) == -1)
{
perror("Can't catch signal");
exit(1);
}
}
}
void UseStdoutLineBuffering()
{
// Set stdout to be line buffered with a buffer of 512 (will flush on new line
// or when the buffer of 512 is exceeded).
setvbuf(stdout, NULL, _IOLBF, 512);
}
#if WEAVE_CONFIG_ENABLE_TUNNELING
#if !WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS
#if !WEAVE_SYSTEM_CONFIG_USE_LWIP
/*
* Some structs are defined redundantly in netinet/in.h and linux/ipv6.h.
* So, cannot include both headers. Define struct in6_ifreq here.
* Copied from linux/ipv6.h
*/
struct in6_ifreq
{
struct in6_addr ifr6_addr;
uint32_t ifr6_prefixlen;
int ifr6_ifindex;
};
#endif
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
static INET_ERROR InterfaceAddAddress_LwIP(InterfaceId tunIf, IPAddress ipAddr, uint8_t prefixLen)
{
INET_ERROR err = INET_NO_ERROR;
// Lock LwIP stack
LOCK_TCPIP_CORE();
int8_t index;
ip6_addr_t ip6_addr;
ip6_addr = ipAddr.ToIPv6();
if (ip6_addr_islinklocal(&ip6_addr))
{
#if LWIP_VERSION_MAJOR > 1
ip6_addr_set(ip_2_ip6(&tunIf->ip6_addr[0]), &ip6_addr);
#else // LWIP_VERSION_MAJOR <= 1
ip6_addr_set(&tunIf->ip6_addr[0], &ip6_addr);
#endif // LWIP_VERSION_MAJOR <= 1
index = 0;
}
else
{
// Add an address with a prefix route into the route table.
err = System::MapErrorLwIP(netif_add_ip6_address_with_route(tunIf, &ip6_addr, prefixLen, &index));
}
// Explicitly set this address as valid to bypass DAD
if (index >= 0)
{
netif_ip6_addr_set_state(tunIf, index, IP6_ADDR_VALID);
}
// UnLock LwIP stack
UNLOCK_TCPIP_CORE();
return err;
}
static INET_ERROR InterfaceRemoveAddress_LwIP(InterfaceId tunIf, IPAddress ipAddr, uint8_t prefixLen)
{
INET_ERROR err = INET_NO_ERROR;
// Lock LwIP stack
LOCK_TCPIP_CORE();
ip6_addr_t ip6_addr;
ip6_addr = ipAddr.ToIPv6();
if (ip6_addr_islinklocal(&ip6_addr))
{
#if LWIP_VERSION_MAJOR > 1
ip6_addr_set_zero(ip_2_ip6(&tunIf->ip6_addr[0]));
#else // LWIP_VERSION_MAJOR <= 1
ip6_addr_set_zero(&tunIf->ip6_addr[0]);
#endif // LWIP_VERSION_MAJOR <= 1
}
else
{
/**
* Look for the address among the list of configured ones on the device
* and delete it.
*/
err = System::MapErrorLwIP(netif_remove_ip6_address_with_route(tunIf, &ip6_addr, prefixLen));
}
// UnLock LwIP stack
UNLOCK_TCPIP_CORE();
return err;
}
static INET_ERROR SetRouteToTunnelInterface_LwIP(InterfaceId tunIf, IPPrefix ipPrefix, nl::Inet::TunEndPoint::RouteOp routeAddDel)
{
INET_ERROR err = INET_NO_ERROR;
struct ip6_prefix ip6_prefix;
// Lock LwIP stack
LOCK_TCPIP_CORE();
ip6_prefix.addr = ipPrefix.IPAddr.ToIPv6();
ip6_prefix.prefix_len = ipPrefix.Length;
if (routeAddDel == nl::Inet::TunEndPoint::kRouteTunIntf_Add)
{
err = System::MapErrorLwIP(ip6_add_route_entry(&ip6_prefix, tunIf, NULL, NULL));
}
else
{
ip6_remove_route_entry(&ip6_prefix);
}
// UnLock LwIP stack
UNLOCK_TCPIP_CORE();
return err;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if !WEAVE_SYSTEM_CONFIG_USE_LWIP
static INET_ERROR InterfaceAddAddress_Linux(InterfaceId tunIf, IPAddress ipAddr, uint8_t prefixLen)
{
INET_ERROR err = INET_NO_ERROR;
int ret = -1;
int sockfd = INET_INVALID_SOCKET_FD;
struct in6_ifreq ifr6;
uint8_t *p = NULL;
p = &(ifr6.ifr6_addr.s6_addr[0]);
for (int i = 0; i < 4; i++)
{
nl::Weave::Encoding::LittleEndian::Write32(p, ipAddr.Addr[i]);
}
sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
if (sockfd < 0)
{
ExitNow(err = System::MapErrorPOSIX(errno));
}
// Copy over the interface index to the in6_ifreq
ifr6.ifr6_ifindex = tunIf;
ifr6.ifr6_prefixlen = prefixLen;
// Set the v6 address on the interface
ret = ioctl(sockfd, SIOCSIFADDR, &ifr6);
if (ret != 0 && errno != EALREADY && errno != EEXIST)
{
ExitNow(err = System::MapErrorPOSIX(errno));
}
exit:
if (sockfd >= 0)
{
close(sockfd);
}
return err;
}
static INET_ERROR InterfaceRemoveAddress_Linux(InterfaceId tunIf, IPAddress ipAddr, uint8_t prefixLen)
{
INET_ERROR err = INET_NO_ERROR;
int ret = -1;
int sockfd = INET_INVALID_SOCKET_FD;
struct in6_ifreq ifr6;
uint8_t *p = NULL;
sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
if (sockfd < 0)
{
ExitNow(err = System::MapErrorPOSIX(errno));
}
p = &(ifr6.ifr6_addr.s6_addr[0]);
for (int i = 0; i < 4; i++)
{
nl::Weave::Encoding::LittleEndian::Write32(p, ipAddr.Addr[i]);
}
// Copy over the interface index to the in6_ifreq
ifr6.ifr6_ifindex = tunIf;
ifr6.ifr6_prefixlen = prefixLen;
ret = ioctl(sockfd, SIOCDIFADDR, &ifr6);
if (ret != 0 && errno != ENOENT)
{
ExitNow(err = System::MapErrorPOSIX(errno));
}
exit:
if (sockfd >= 0)
{
close(sockfd);
}
return err;
}
static INET_ERROR SetRouteToTunnelInterface_Linux(InterfaceId tunIf, IPPrefix ipPrefix, nl::Inet::TunEndPoint::RouteOp routeAddDel)
{
INET_ERROR err = INET_NO_ERROR;
int ret = -1;
int sockfd = INET_INVALID_SOCKET_FD;
struct ::in6_rtmsg route;
memset(&route, 0, sizeof(struct in6_rtmsg));
route.rtmsg_dst = ipPrefix.IPAddr.ToIPv6();
route.rtmsg_dst_len = ipPrefix.Length;
// Fill in in6_rtmsg flags
route.rtmsg_flags = RTF_UP;
if (ipPrefix.Length == NL_INET_IPV6_MAX_PREFIX_LEN)
{
route.rtmsg_flags |= RTF_HOST;
}
route.rtmsg_metric = 1;
sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
if (sockfd < 0)
{
ExitNow(err = System::MapErrorPOSIX(errno));
}
route.rtmsg_ifindex = tunIf;
if (routeAddDel == nl::Inet::TunEndPoint::kRouteTunIntf_Add)
{
// Add the route to the kernel
ret = ioctl(sockfd, SIOCADDRT, &route);
if (ret != 0 && errno != EALREADY && errno != EEXIST)
{
ExitNow(err = System::MapErrorPOSIX(errno));
}
}
else
{
// Delete the route from the kernel
ret = ioctl(sockfd, SIOCDELRT, &route);
if (ret != 0 && errno != EALREADY && errno != ENOENT)
{
ExitNow(err = System::MapErrorPOSIX(errno));
}
}
exit:
if (sockfd >= 0)
{
close(sockfd);
}
return err;
}
#endif // !WEAVE_SYSTEM_CONFIG_USE_LWIP
/**
* Add an IPv6 address to the tunnel interface.
*
* @note
* On some platforms, this method returns \c INET_NO_ERROR even when
* insufficient space is available for another address.
*
* @param[in] tunIf The tunnel interface identifier.
*
* @param[in] ipAddr IPAddress object holding the IPv6 address to be assigned.
*
* @param[in] prefixLen prefix length for on-link route for this address.
*
* @return INET_NO_ERROR success: address is added.
* @retval other another system or platform error
*/
INET_ERROR InterfaceAddAddress(InterfaceId tunIf, IPAddress ipAddr, uint8_t prefixLen)
{
INET_ERROR err = INET_NO_ERROR;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
err = InterfaceAddAddress_LwIP(tunIf, ipAddr, prefixLen);
#else
err = InterfaceAddAddress_Linux(tunIf, ipAddr, prefixLen);
#endif
SuccessOrExit(err);
exit:
return err;
}
/**
* Remove an IPv6 address from the tunnel interface.
*
* @param[in] tunIf The tunnel interface identifier.
*
* @param[in] ipAddr the IPAddress to remove.
*
* @param[in] prefixLen prefix length for on-link route for this address.
*
* @retval INET_NO_ERROR success: address is removed.
* @retval other another system or platform error.
*/
INET_ERROR InterfaceRemoveAddress(InterfaceId tunIf, IPAddress ipAddr, uint8_t prefixLen)
{
INET_ERROR err = INET_NO_ERROR;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
err = InterfaceRemoveAddress_LwIP(tunIf, ipAddr, prefixLen);
#else
err = InterfaceRemoveAddress_Linux(tunIf, ipAddr, prefixLen);
#endif
SuccessOrExit(err);
exit:
return err;
}
/**
* Add/Remove an IPv6 route pointing to the tunnel interface.
*
* @note
* The IP address prefix must be an IPv6 address prefix.
*
* @param[in] tunIf The tunnel interface identifier.
*
* @param[in] ipPrefix IPPrefix to route.
*
* @param[in] routeAddDel Flag indicating route addition or deletion.
*
* @retval INET_NO_ERROR success: route operation performed
* @retval other another system or platform error
*/
INET_ERROR SetRouteToTunnelInterface(InterfaceId tunIf, IPPrefix ipPrefix, nl::Inet::TunEndPoint::RouteOp routeAddDel)
{
INET_ERROR err = INET_NO_ERROR;
// Only allow prefix length up to 64 bits
if (ipPrefix.Length > NL_INET_IPV6_DEFAULT_PREFIX_LEN)
{
ExitNow(err = INET_ERROR_BAD_ARGS);
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
err = SetRouteToTunnelInterface_LwIP(tunIf, ipPrefix, routeAddDel);
#else
err = SetRouteToTunnelInterface_Linux(tunIf, ipPrefix, routeAddDel);
#endif
SuccessOrExit(err);
exit:
return err;
}
/**
* This is a default implementation in a Standalone setting of the Weave Addressing and
* Routing(WARM) platform adaptation when the Tunnel interface is brought up. This may
* be overridden by asserting the preprocessor definition,
* #WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS.
*
* @param[in] tunIf The tunnel interface identifier.
*
*/
void nl::Weave::Profiles::WeaveTunnel::Platform::TunnelInterfaceUp(InterfaceId tunIf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint64_t globalId = 0;
IPAddress tunULAAddr;
/*
* Add the WiFi interface ULA address to the tunnel interface to ensure the selection of
* a Weave ULA as the source address for packets originating on the local node but destined
* for addresses reachable via the tunnel. Without this, the default IPv6 source address
* selection algorithm might choose an inappropriate source address, making it impossible
* for the destination node to respond.
*/
globalId = WeaveFabricIdToIPv6GlobalId(ExchangeMgr.FabricState->FabricId);
tunULAAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi,
nl::Weave::WeaveNodeIdToIPv6InterfaceId(ExchangeMgr.FabricState->LocalNodeId));
err = InterfaceAddAddress(tunIf, tunULAAddr, NL_INET_IPV6_MAX_PREFIX_LEN);
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(WeaveTunnel, "Failed to add host address to Weave tunnel interface\n");
}
}
/**
* This is a default implementation in a Standalone setting of the Weave Addressing and
* Routing(WARM) platform adaptation when the Tunnel interface is brought down. This may
* be overridden by asserting the preprocessor definition,
* #WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS.
*
* @param[in] tunIf The tunnel interface identifier.
*
*/
void nl::Weave::Profiles::WeaveTunnel::Platform::TunnelInterfaceDown(InterfaceId tunIf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint64_t globalId = 0;
IPAddress tunULAAddr;
/*
* Remove the WiFi interface ULA address to the tunnel interface added during
* TunnelInterfaceUp() call.
*/
globalId = WeaveFabricIdToIPv6GlobalId(ExchangeMgr.FabricState->FabricId);
tunULAAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi,
nl::Weave::WeaveNodeIdToIPv6InterfaceId(ExchangeMgr.FabricState->LocalNodeId));
err = InterfaceRemoveAddress(tunIf, tunULAAddr, NL_INET_IPV6_MAX_PREFIX_LEN);
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(WeaveTunnel, "Failed to remove host address from Weave tunnel interface\n");
}
}
/**
* This is a default implementation in a Standalone setting of the Weave Addressing and
* Routing(WARM) platform adaptation when the Service tunnel connection is established.
* This may be overridden by asserting the preprocessor definition,
* #WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS.
*
* @param[in] tunIf The tunnel interface identifier.
*
* @param[in] tunMode The mode(Primary, PrimaryAndBackup, BackupOnly) in which the
* tunnel is established.
*
*/
void nl::Weave::Profiles::WeaveTunnel::Platform::ServiceTunnelEstablished(InterfaceId tunIf,
TunnelAvailabilityMode tunMode)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
IPPrefix prefix;
uint64_t globalId = 0;
IPAddress tunULAAddr;
// Create prefix fd<globalId>::/48 to install route to tunnel interface
globalId = WeaveFabricIdToIPv6GlobalId(ExchangeMgr.FabricState->FabricId);
tunULAAddr = IPAddress::MakeULA(globalId, 0, 0);
prefix.IPAddr = tunULAAddr;
prefix.Length = WEAVE_ULA_FABRIC_DEFAULT_PREFIX_LEN;
// Add route to tunnel interface
err = SetRouteToTunnelInterface(tunIf, prefix, TunEndPoint::kRouteTunIntf_Add);
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(WeaveTunnel, "Failed to add Weave tunnel route\n");
}
}
/**
* This is a default implementation in a Standalone setting of the Weave Addressing and
* Routing(WARM) platform adaptation when the Service tunnel connection is torn down.
* This may be overridden by asserting the preprocessor definition,
* #WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS.
*
* @param[in] tunIf The tunnel interface identifier.
*
*/
void nl::Weave::Profiles::WeaveTunnel::Platform::ServiceTunnelDisconnected(InterfaceId tunIf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
IPPrefix prefix;
uint64_t globalId = 0;
IPAddress tunULAAddr;
// Delete route to tunnel interface for prefix fd<globalId>::/48
globalId = WeaveFabricIdToIPv6GlobalId(ExchangeMgr.FabricState->FabricId);
tunULAAddr = IPAddress::MakeULA(globalId, 0, 0);
prefix.IPAddr = tunULAAddr;
prefix.Length = WEAVE_ULA_FABRIC_DEFAULT_PREFIX_LEN;
// Delete route
err = SetRouteToTunnelInterface(tunIf, prefix, TunEndPoint::kRouteTunIntf_Del);
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(WeaveTunnel, "Failed to remove Weave tunnel route\n");
}
}
/**
* This is a default implementation in a Standalone setting of the Weave Addressing and
* Routing(WARM) platform adaptation when the Service tunnel connection changes mode.
* This may be overridden by asserting the preprocessor definition,
* #WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS.
*
* @param[in] tunIf The tunnel interface identifier.
*
* @param[in] tunMode The mode(Primary, PrimaryAndBackup, BackupOnly) to which the
* tunnel connection has been changed to.
*
*/
void nl::Weave::Profiles::WeaveTunnel::Platform::ServiceTunnelModeChange(InterfaceId tunIf,
TunnelAvailabilityMode tunMode)
{
(void)tunIf;
(void)tunMode;
}
#endif // WEAVE_TUNNEL_CONFIG_WILL_OVERRIDE_ADDR_ROUTING_FUNCS
#endif // WEAVE_CONFIG_ENABLE_TUNNELING
void InitSystemLayer()
{
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
AcquireLwIP();
SystemLayer.Init(sLwIPEventQueue);
#else // !WEAVE_SYSTEM_CONFIG_USE_LWIP
SystemLayer.Init(NULL);
#endif // !WEAVE_SYSTEM_CONFIG_USE_LWIP
}
void ShutdownSystemLayer()
{
SystemLayer.Shutdown();
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
ReleaseLwIP();
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
static void PrintNetworkState()
{
char intfName[10];
GetInterfaceName(&netIF, intfName, sizeof(intfName));
printf("LwIP interface ready\n");
printf(" Interface Name: %s\n", intfName);
printf(" Tap Device: %s\n", gNetworkOptions.TapDeviceName);
printf(" MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", tapIF.macAddr[0], tapIF.macAddr[1], tapIF.macAddr[2], tapIF.macAddr[3], tapIF.macAddr[4], tapIF.macAddr[5]);
#if INET_CONFIG_ENABLE_IPV4
printf(" IPv4 Address: %s\n", ipaddr_ntoa(&netIF.ip_addr));
printf(" IPv4 Mask: %s\n", ipaddr_ntoa(&netIF.netmask));
printf(" IPv4 Gateway: %s\n", ipaddr_ntoa(&netIF.gw));
#endif // INET_CONFIG_ENABLE_IPV4
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
if (!ip6_addr_isany(netif_ip6_addr(&netIF, i)))
{
printf(" IPv6 address: %s, 0x%02x\n", ip6addr_ntoa(netif_ip6_addr(&netIF, i)), netif_ip6_addr_state(&netIF, i));
}
#if INET_CONFIG_ENABLE_DNS_RESOLVER
if (DNSServerAddr != IPAddress::Any)
{
#if LWIP_VERSION_MAJOR > 1
ip_addr_t dnsServerAddr = DNSServerAddr.ToLwIPAddr();
#else // LWIP_VERSION_MAJOR <= 1
#if INET_CONFIG_ENABLE_IPV4
ip_addr_t dnsServerAddr = DNSServerAddr.ToIPv4();
#else // !INET_CONFIG_ENABLE_IPV4
#error "No support for DNS Resolver without IPv4!"
#endif // !INET_CONFIG_ENABLE_IPV4
#endif // LWIP_VERSION_MAJOR <= 1
dns_setserver(0, &dnsServerAddr);
printf(" DNS Server: %s\n", ipaddr_ntoa(&dnsServerAddr));
}
#endif // INET_CONFIG_ENABLE_DNS_RESOLVER
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
void InitNetwork()
{
void* lContext = NULL;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
tcpip_init(NULL, NULL);
#else // !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
// If an tap device name hasn't been specified, derive one from the IPv6 interface id.
if (gNetworkOptions.TapDeviceName == NULL)
{
uint64_t iid = gNetworkOptions.LocalIPv6Addr.InterfaceId();
snprintf(DefaultTapDeviceName, sizeof(DefaultTapDeviceName), "weave-dev-%" PRIx64, iid & 0xFFFF);
DefaultTapDeviceName[ sizeof(DefaultTapDeviceName) - 1] = 0;
gNetworkOptions.TapDeviceName = DefaultTapDeviceName;
}
err_t lwipErr = TapInterface_Init(&tapIF, gNetworkOptions.TapDeviceName, NULL);
if (lwipErr != ERR_OK)
{
printf("Failed to initialize tap device %s: %s\n", gNetworkOptions.TapDeviceName, ErrorStr(System::MapErrorLwIP(lwipErr)));
exit(EXIT_FAILURE);
}
tcpip_init(OnLwIPInitComplete, NULL);
// Lock LwIP stack
LOCK_TCPIP_CORE();
#if INET_CONFIG_ENABLE_IPV4
#if LWIP_VERSION_MAJOR > 1
ip4_addr_t ip4Addr, ip4Netmask, ip4Gateway;
#else // LWIP_VERSION_MAJOR <= 1
ip_addr_t ip4Addr, ip4Netmask, ip4Gateway;
#endif // LWIP_VERSION_MAJOR <= 1
ip4Addr = gNetworkOptions.LocalIPv4Addr.ToIPv4();
IP4_ADDR(&ip4Netmask, 255, 255, 255, 0);
ip4Gateway = gNetworkOptions.IPv4GatewayAddr.ToIPv4();
netif_add(&netIF, &ip4Addr, &ip4Netmask, &ip4Gateway, &tapIF, TapInterface_SetupNetif, tcpip_input);
#endif // INET_CONFIG_ENABLE_IPV4
netif_create_ip6_linklocal_address(&netIF, 1);
netif_set_default(&netIF);
netif_set_up(&netIF);
if (gNetworkOptions.LocalIPv6Addr != IPAddress::Any)
{
ip6_addr_t ip6addr = gNetworkOptions.LocalIPv6Addr.ToIPv6();
s8_t index;
netif_add_ip6_address_with_route(&netIF, &ip6addr, 64, &index);
if (index >= 0)
{
netif_ip6_addr_set_state(&netIF, index, IP6_ADDR_PREFERRED);
}
}
// UnLock LwIP stack
UNLOCK_TCPIP_CORE();
while (!NetworkIsReady())
{
struct timeval lSleepTime;
lSleepTime.tv_sec = 0;
lSleepTime.tv_usec = 100000;
ServiceEvents(lSleepTime);
}
//FIXME: this is kinda nasty :(
// Force new IP address to be ready, bypassing duplicate detection.
if (gNetworkOptions.LocalIPv6Addr != IPAddress::Any) {
netif_ip6_addr_set_state(&netIF, 2, 0x30);
} else {
netif_ip6_addr_set_state(&netIF, 1, 0x30);
}
PrintNetworkState();
#endif // !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
AcquireLwIP();
lContext = sLwIPEventQueue;
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
Inet.Init(SystemLayer, lContext);
}
void ServiceEvents(::timeval& aSleepTime)
{
static bool printed = false;
if (!printed)
{
#if WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (NetworkIsReady())
#endif
{
printf("Weave Node ready to service events; PID: %d; PPID: %d\n", getpid(), getppid());
fflush(stdout);
printed = true;
}
}
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
fd_set readFDs, writeFDs, exceptFDs;
int numFDs = 0;
FD_ZERO(&readFDs);
FD_ZERO(&writeFDs);
FD_ZERO(&exceptFDs);
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (SystemLayer.State() == System::kLayerState_Initialized)
SystemLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (Inet.State == InetLayer::kState_Initialized)
Inet.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &aSleepTime);
if (selectRes < 0)
{
printf("select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno)));
return;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (SystemLayer.State() == System::kLayerState_Initialized)
{
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
static uint32_t sRemainingSystemLayerEventDelay = 0;
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
SystemLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
if (SystemLayer.State() == System::kLayerState_Initialized)
{
if (sRemainingSystemLayerEventDelay == 0)
{
SystemLayer.DispatchEvents();
sRemainingSystemLayerEventDelay = gNetworkOptions.EventDelay;
}
else
sRemainingSystemLayerEventDelay--;
// TODO: Currently timers are delayed by aSleepTime above. A improved solution would have a mechanism to reduce
// aSleepTime according to the next timer.
SystemLayer.HandlePlatformTimer();
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
TapInterface_Select(&tapIF, &netIF, aSleepTime);
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (Inet.State == InetLayer::kState_Initialized)
{
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && WEAVE_SYSTEM_CONFIG_USE_LWIP
static uint32_t sRemainingInetLayerEventDelay = 0;
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
Inet.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && WEAVE_SYSTEM_CONFIG_USE_LWIP
if (Inet.State == InetLayer::kState_Initialized)
{
if (sRemainingInetLayerEventDelay == 0)
{
Inet.DispatchEvents();
sRemainingInetLayerEventDelay = gNetworkOptions.EventDelay;
}
else
sRemainingInetLayerEventDelay--;
// TODO: Currently timers are delayed by aSleepTime above. A improved solution would have a mechanism to reduce
// aSleepTime according to the next timer.
Inet.HandlePlatformTimer();
}
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && WEAVE_SYSTEM_CONFIG_USE_LWIP
}
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
static bool NetworkIsReady()
{
bool ready = true;
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
if (!ip6_addr_isany(netif_ip6_addr(&netIF, i)) && ip6_addr_istentative(netif_ip6_addr_state(&netIF, i)))
{
ready = false;
break;
}
return ready;
}
static void OnLwIPInitComplete(void *arg)
{
printf("Waiting for addresses assignment...\n");
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
void InitWeaveStack(bool listen, bool initExchangeMgr)
{
WEAVE_ERROR res;
WeaveMessageLayer::InitContext initContext;
static DEFINE_ALIGNED_VAR(sTestGroupKeyStore, sizeof(TestGroupKeyStore), void*);
#if CONFIG_BLE_PLATFORM_BLUEZ
// Initialize the BleLayer object.
res = sBle.Init(&sBlePlatformDelegate, &sBleApplicationDelegate, &Inet);
if (res != WEAVE_NO_ERROR)
{
printf("sBle.Init failed: %s\n", ErrorStr(res));
exit(-1);
}
#endif /* CONFIG_BLE_PLATFORM_BLUEZ */
nl::Weave::Stats::SetObjects(&MessageLayer);
// Seed the random number generator
System::Timer::Epoch now = System::Timer::GetCurrentEpoch();
srand((unsigned int) now);
// Initialize the FabricState object.
res = FabricState.Init(new (&sTestGroupKeyStore) TestGroupKeyStore());
if (res != WEAVE_NO_ERROR)
{
printf("FabricState.Init failed: %s\n", ErrorStr(res));
exit(EXIT_FAILURE);
}
FabricState.FabricId = gWeaveNodeOptions.FabricId;
FabricState.LocalNodeId = gWeaveNodeOptions.LocalNodeId;
FabricState.DefaultSubnet = gWeaveNodeOptions.SubnetId;
FabricState.PairingCode = gWeaveNodeOptions.PairingCode;
// When using sockets we must listen on specific addresses, rather than ANY. Otherwise you will only be
// able to run a single Weave application per system.
#if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
#if INET_CONFIG_ENABLE_IPV4
FabricState.ListenIPv4Addr = gNetworkOptions.LocalIPv4Addr;
#endif // INET_CONFIG_ENABLE_IPV4
FabricState.ListenIPv6Addr = gNetworkOptions.LocalIPv6Addr;
#endif
#if WEAVE_CONFIG_SECURITY_TEST_MODE
FabricState.LogKeys = true;
#endif
// Initialize the WeaveMessageLayer object.
// TODO mock-device BLE support?
initContext.systemLayer = &SystemLayer;
initContext.inet = &Inet;
initContext.fabricState = &FabricState;
initContext.listenTCP = listen;
initContext.listenUDP = true;
#if CONFIG_BLE_PLATFORM_BLUEZ
initContext.ble = &sBle;
initContext.listenBLE = true;
#endif /* CONFIG_NETWORK_LAYER_BLE */
res = MessageLayer.Init(&initContext);
if (res != WEAVE_NO_ERROR)
{
printf("WeaveMessageLayer.Init failed: %s\n", ErrorStr(res));
exit(EXIT_FAILURE);
}
if (initExchangeMgr)
{
// Initialize the Exchange Manager object.
res = ExchangeMgr.Init(&MessageLayer);
if (res != WEAVE_NO_ERROR)
{
printf("WeaveExchangeManager.Init failed: %s\n", ErrorStr(res));
exit(EXIT_FAILURE);
}
res = SecurityMgr.Init(ExchangeMgr, SystemLayer);
if (res != WEAVE_NO_ERROR)
{
printf("WeaveSecurityManager.Init failed: %s\n", ErrorStr(res));
exit(EXIT_FAILURE);
}
if (gTAKEOptions.ForceReauth)
{
res = gTAKEOptions.PrepopulateTokenData();
if (res != WEAVE_NO_ERROR)
{
printf("MockTAKEChallengerDelegate::StoreTokenAuthData failed: %s\n", ErrorStr(res));
exit(EXIT_FAILURE);
}
}
SecurityMgr.SetCASEAuthDelegate(&gCASEOptions);
SecurityMgr.SetKeyExportDelegate(&gKeyExportOptions);
SecurityMgr.SetTAKEAuthDelegate(&gMockTAKEChallengerDelegate);
SecurityMgr.SetTAKETokenAuthDelegate(&gMockTAKETokenDelegate);
#if WEAVE_CONFIG_ENABLE_CASE_INITIATOR
if (gCASEOptions.InitiatorCASEConfig != kCASEConfig_NotSpecified)
SecurityMgr.InitiatorCASEConfig = gCASEOptions.InitiatorCASEConfig;
#endif
if (gCASEOptions.AllowedCASEConfigs != 0)
{
#if WEAVE_CONFIG_ENABLE_CASE_INITIATOR
SecurityMgr.InitiatorAllowedCASEConfigs = gCASEOptions.AllowedCASEConfigs;
#endif
#if WEAVE_CONFIG_ENABLE_CASE_RESPONDER
SecurityMgr.ResponderAllowedCASEConfigs = gCASEOptions.AllowedCASEConfigs;
#endif
}
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_RESPONDER
if (gKeyExportOptions.AllowedKeyExportConfigs != 0)
SecurityMgr.ResponderAllowedKeyExportConfigs = gKeyExportOptions.AllowedKeyExportConfigs;
#endif
#if WEAVE_CONFIG_SECURITY_TEST_MODE
SecurityMgr.CASEUseKnownECDHKey = gCASEOptions.UseKnownECDHKey;
#endif
}
}
void PrintNodeConfig()
{
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);
if (MessageLayer.IsListening)
{
printf(" Listening Addresses:");
#if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
char nodeAddrStr[64];
if (FabricState.ListenIPv6Addr == IPAddress::Any
#if INET_CONFIG_ENABLE_IPV4
&& FabricState.ListenIPv4Addr == IPAddress::Any
#endif // INET_CONFIG_ENABLE_IPV4
)
printf(" any\n");
else
{
printf("\n");
if (FabricState.ListenIPv6Addr != IPAddress::Any)
{
FabricState.ListenIPv6Addr.ToString(nodeAddrStr, sizeof(nodeAddrStr));
printf(" %s (ipv6)\n", nodeAddrStr);
}
#if INET_CONFIG_ENABLE_IPV4
if (FabricState.ListenIPv4Addr != IPAddress::Any)
{
FabricState.ListenIPv4Addr.ToString(nodeAddrStr, sizeof(nodeAddrStr));
printf(" %s (ipv4)\n", nodeAddrStr);
}
#endif // INET_CONFIG_ENABLE_IPV4
}
#else // WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
printf(" any\n");
#endif // WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
}
}
void ShutdownNetwork()
{
Inet.Shutdown();
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
ReleaseLwIP();
#endif
}
void ShutdownWeaveStack()
{
SecurityMgr.Shutdown();
ExchangeMgr.Shutdown();
MessageLayer.Shutdown();
FabricState.Shutdown();
}
void DumpMemory(const uint8_t *mem, uint32_t len, const char *prefix, uint32_t rowWidth)
{
(void) DumpMemory;
int indexWidth = snprintf(NULL, 0, "%X", len);
if (indexWidth < 4)
indexWidth = 4;
for (uint32_t i = 0; i < len; i += rowWidth)
{
printf("%s%0*X: ", prefix, indexWidth, i);
uint32_t rowEnd = i + rowWidth;
uint32_t j = i;
for (; j < rowEnd && j < len; j++)
printf("%02X ", mem[j]);
for (; j < rowEnd; j++)
printf(" ");
for (j = i; j < rowEnd && j < len; j++)
if (isprint((char) mem[j]))
printf("%c", mem[j]);
else
printf(".");
printf("\n");
}
}
void DumpMemoryCStyle(const uint8_t *mem, uint32_t len, const char *prefix, uint32_t rowWidth)
{
(void) DumpMemoryCStyle;
for (uint32_t i = 0; i < len; i += rowWidth)
{
printf("%s", prefix);
uint32_t rowEnd = i + rowWidth;
uint32_t j = i;
for (; j < rowEnd && j < len; j++)
printf("0x%02X, ", mem[j]);
printf("\n");
}
}
bool IsZeroBytes(const uint8_t *buf, uint32_t len)
{
for (; len > 0; len--, buf++)
if (*buf != 0)
return false;
return true;
}
void PrintMACAddress(const uint8_t *buf, uint32_t len)
{
for (; len > 0; buf++, len--)
{
if (len != 1)
printf("%02X:", (unsigned)*buf);
else
printf("%02X", (unsigned)*buf);
}
}
void PrintAddresses()
{
InterfaceAddressIterator iterator;
printf("Valid addresses: \n");
for (; iterator.HasCurrent(); iterator.Next()) {
IPAddress addr = iterator.GetAddress();
char buf[80];
addr.ToString(buf, 80);
printf("%s\n", buf);
}
}
uint8_t *ReadFileArg(const char *fileName, uint32_t& len, uint32_t maxLen)
{
FILE *file = NULL;
uint8_t *fileData = NULL;
long fileLen;
size_t fileStatus;
file = fopen(fileName, "r");
if (file == NULL)
{
printf("Unable to open %s\n%s\n", fileName, strerror(errno));
return NULL;
}
fseek(file, 0, SEEK_END);
fileLen = ftell(file);
fseek(file, 0, SEEK_SET);
if (ferror(file))
{
printf("Unable to read %s\n%s\n", fileName, strerror(errno));
fclose(file);
return NULL;
}
len = (uint32_t) fileLen;
if (len > maxLen)
{
printf("File too big: %s\n", fileName);
fclose(file);
return NULL;
}
fileData = (uint8_t *)malloc((size_t)len);
if (fileData == NULL)
{
printf("Out of memory reading %s\n", fileName);
fclose(file);
return NULL;
}
fileStatus = fread(fileData, 1, (size_t)len, file);
if ((fileStatus != len) || ferror(file))
{
printf("Unable to read %s\n%s", fileName, strerror(errno));
fclose(file);
free(fileData);
return NULL;
}
fclose(file);
return fileData;
}
void HandleMessageReceiveError(WeaveMessageLayer *msgLayer, WEAVE_ERROR err, const IPPacketInfo *pktInfo)
{
const char * const default_format = "Error receiving message from %s: %s\n";
if (pktInfo != NULL)
{
char ipAddrStr[INET6_ADDRSTRLEN];
pktInfo->SrcAddress.ToString(ipAddrStr, sizeof(ipAddrStr));
if (err == WEAVE_ERROR_INVALID_DESTINATION_NODE_ID && pktInfo->DestAddress.IsMulticast())
{
printf("Ignoring multicast message from %s addressed to different node id\n", ipAddrStr);
}
else if (err == WEAVE_ERROR_INVALID_ADDRESS && pktInfo->DestAddress.IsMulticast())
{
printf("Ignoring multicast message from %s using non-local source address\n", ipAddrStr);
}
else
{
printf(default_format, ipAddrStr, ErrorStr(err));
}
}
else
{
// general, catch-all error message
printf(default_format, "(unknown)", ErrorStr(err));
}
}
void HandleAcceptConnectionError(WeaveMessageLayer *msgLayer, WEAVE_ERROR err)
{
printf("Error accepting incoming connection: %s\n", ErrorStr(err));
}
#if CONFIG_BLE_PLATFORM_BLUEZ
void *WeaveBleIOLoop(void *arg)
{
Bluez_PeripheralArgs *peripheralArgs = (Bluez_PeripheralArgs *)arg;
if (!nl::Ble::Platform::BlueZ::RunBluezIOThread(peripheralArgs->BleName, peripheralArgs->BleAddress))
{
exit(EXIT_FAILURE);
}
return NULL;
}
#endif /* CONFIG_BLE_PLATFORM_BLUEZ */
void PrintStatsCounters(nl::Weave::System::Stats::count_t *counters, const char *aPrefix)
{
size_t i;
const char **strings = nl::Weave::System::Stats::GetStrings();
const char *prefix = aPrefix ? aPrefix : "";
for (i = 0; i < nl::Weave::System::Stats::kNumEntries; i++)
{
printf("%s%s:\t\t%d\n", prefix, strings[i], counters[i]);
}
}
bool ProcessStats(nl::Weave::System::Stats::Snapshot &aBefore, nl::Weave::System::Stats::Snapshot &aAfter, bool aPrint, const char *aPrefix)
{
bool leak = false;
nl::Weave::System::Stats::Snapshot delta;
const char *prefix = aPrefix ? aPrefix : "";
struct timeval sleepTime;
uint64_t nowUsec;
uint64_t upperBoundUsec;
// If the current snapshot shows a leak when compared to the first one,
// we service the network for a few more rounds, to give the system
// a chance to release any "delayed-release" object that might have events
// pending. There might also be one last WRM ACK in flight after the last
// message between two nodes.
// Note that the test harnesses we have been using give the process one
// second to quit after sending a SIGUSR1, so this loop needs to complete
// well before a second in case there is a leak. If the process takes too
// long to quit, the test harness usually kills it. That causes
// the output to be truncated which in turn makes the test failure harder to
// understand.
// The loop runs for 800 milliseconds in case there is an actual leak.
// Fault-injection tests can require a longer time, since sometimes an EC is
// freed only after the max number of retransmissions.
// To allow extra time in this loop, see gFaultInjectionOptions.ExtraCleanupTimeMsec.
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
nl::Weave::Stats::UpdateSnapshot(aAfter);
nowUsec = Now();
upperBoundUsec = nowUsec + 800000 + (gFaultInjectionOptions.ExtraCleanupTimeMsec * 1000);
while (Now() < upperBoundUsec)
{
leak = nl::Weave::System::Stats::Difference(delta, aAfter, aBefore);
if (leak == false)
{
break;
}
ServiceNetwork(sleepTime);
nl::Weave::Stats::UpdateSnapshot(aAfter);
}
if (aPrint)
{
if (gFaultInjectionOptions.DebugResourceUsage)
{
printf("\n%sResources in use before:\n", prefix);
PrintStatsCounters(aBefore.mResourcesInUse, prefix);
printf("\n%sResources in use after:\n", prefix);
PrintStatsCounters(aAfter.mResourcesInUse, prefix);
}
printf("\n%sResource leak %sdetected\n", prefix, (leak ? "" : "not "));
if (leak)
{
printf("%sDelta resources in use:\n", prefix);
PrintStatsCounters(delta.mResourcesInUse, prefix);
printf("%sEnd of delta resources in use\n", prefix);
}
if (gFaultInjectionOptions.DebugResourceUsage)
{
printf("\nHigh watermarks:\n");
PrintStatsCounters(aAfter.mHighWatermarks, prefix);
}
}
return leak;
}
void PrintFaultInjectionCounters(void)
{
size_t i;
nl::FaultInjection::Identifier faultId;
nl::FaultInjection::GetManagerFn faultMgrTable[] = {
nl::Weave::FaultInjection::GetManager,
nl::Inet::FaultInjection::GetManager,
nl::Weave::System::FaultInjection::GetManager
};
if (!gFaultInjectionOptions.PrintFaultCounters)
{
return;
}
printf("\nFaultInjection counters:\n");
for (i = 0; i < sizeof(faultMgrTable) / sizeof(faultMgrTable[0]); i++)
{
nl::FaultInjection::Manager &mgr = faultMgrTable[i]();
for (faultId = 0; faultId < mgr.GetNumFaults(); faultId++)
{
printf("%s_%s: %u\n", mgr.GetName(), mgr.GetFaultNames()[faultId],
mgr.GetFaultRecords()[faultId].mNumTimesChecked);
}
}
printf("End of FaultInjection counters\n");
}
struct RestartCallbackContext {
int mArgc;
char **mArgv;
};
static struct RestartCallbackContext gRestartCallbackCtx;
static void RebootCallbackFn(void)
{
char *lArgv[gRestartCallbackCtx.mArgc +2];
int i;
int j = 0;
if (gSigusr1Received)
{
printf("** skipping restart case after SIGUSR1 **\n");
ExitNow();
}
for (i = 0; gRestartCallbackCtx.mArgv[i] != NULL; i++)
{
if (strcmp(gRestartCallbackCtx.mArgv[i], "--faults") == 0)
{
// Skip the --faults argument for now
i++;
continue;
}
lArgv[j++] = gRestartCallbackCtx.mArgv[i];
}
lArgv[j] = NULL;
for (i = 0; lArgv[i] != NULL; i++)
{
printf("argv[%d]: %s\n", i, lArgv[i]);
}
// Need to close any open file descriptor above stdin/out/err.
// There is no portable way to get the max fd number.
// Given that Weave's test apps don't open a large number of files,
// FD_SETSIZE should be a reasonable upper bound (see the documentation
// of select).
for (i = 3; i < FD_SETSIZE; i++)
{
close(i);
}
printf("********** Restarting *********\n");
fflush(stdout);
execvp(lArgv[0], lArgv);
exit:
return;
}
static void PostInjectionCallbackFn(nl::FaultInjection::Manager *aManager,
nl::FaultInjection::Identifier aId,
nl::FaultInjection::Record *aFaultRecord)
{
uint16_t numargs = aFaultRecord->mNumArguments;
uint16_t i;
printf("***** Injecting fault %s_%s, instance number: %u; reboot: %s",
aManager->GetName(), aManager->GetFaultNames()[aId],
aFaultRecord->mNumTimesChecked, aFaultRecord->mReboot ? "yes" : "no");
if (numargs)
{
printf(" with %u args:", numargs);
for (i = 0; i < numargs; i++)
{
printf(" %d", aFaultRecord->mArguments[i]);
}
}
printf("\n");
}
static nl::FaultInjection::GlobalContext gFaultInjectionGlobalContext = {
{
RebootCallbackFn,
PostInjectionCallbackFn
}
};
static nl::FaultInjection::Callback sFuzzECHeaderCb;
static nl::FaultInjection::Callback sAsyncEventCb;
static bool PrintFaultInjectionMaxArgCbFn(nl::FaultInjection::Manager &mgr, nl::FaultInjection::Identifier aId, nl::FaultInjection::Record *aFaultRecord, void *aContext)
{
const char *faultName = mgr.GetFaultNames()[aId];
if (gFaultInjectionOptions.PrintFaultCounters && aFaultRecord->mNumArguments)
{
printf("FI_instance_params: %s_%s_s%u maxArg: %u;\n", mgr.GetName(), faultName, aFaultRecord->mNumTimesChecked,
aFaultRecord->mArguments[0]);
}
return false;
}
static bool PrintWeaveFaultInjectionMaxArgCbFn(nl::FaultInjection::Identifier aId, nl::FaultInjection::Record *aFaultRecord, void *aContext)
{
nl::FaultInjection::Manager &mgr = nl::Weave::FaultInjection::GetManager();
return PrintFaultInjectionMaxArgCbFn(mgr, aId, aFaultRecord, aContext);
}
static bool PrintSystemFaultInjectionMaxArgCbFn(nl::FaultInjection::Identifier aId, nl::FaultInjection::Record *aFaultRecord, void *aContext)
{
nl::FaultInjection::Manager &mgr = nl::Weave::System::FaultInjection::GetManager();
return PrintFaultInjectionMaxArgCbFn(mgr, aId, aFaultRecord, aContext);
}
void SetupFaultInjectionContext(int argc, char *argv[])
{
SetupFaultInjectionContext(argc, argv, NULL, NULL);
}
void SetupFaultInjectionContext(int argc, char *argv[], int32_t (*aNumEventsAvailable)(void), void (*aInjectAsyncEvents)(int32_t index))
{
nl::FaultInjection::Manager &weavemgr = nl::Weave::FaultInjection::GetManager();
nl::FaultInjection::Manager &systemmgr = nl::Weave::System::FaultInjection::GetManager();
gRestartCallbackCtx.mArgc = argc;
gRestartCallbackCtx.mArgv = argv;
nl::FaultInjection::SetGlobalContext(&gFaultInjectionGlobalContext);
memset(&sFuzzECHeaderCb, 0, sizeof(sFuzzECHeaderCb));
sFuzzECHeaderCb.mCallBackFn = PrintWeaveFaultInjectionMaxArgCbFn;
weavemgr.InsertCallbackAtFault(nl::Weave::FaultInjection::kFault_FuzzExchangeHeaderTx, &sFuzzECHeaderCb);
if (aNumEventsAvailable && aInjectAsyncEvents)
{
memset(&sAsyncEventCb, 0, sizeof(sAsyncEventCb));
sAsyncEventCb.mCallBackFn = PrintSystemFaultInjectionMaxArgCbFn;
systemmgr.InsertCallbackAtFault(nl::Weave::System::FaultInjection::kFault_AsyncEvent, &sAsyncEventCb);
nl::Weave::System::FaultInjection::SetAsyncEventCallbacks(aNumEventsAvailable, aInjectAsyncEvents);
}
}
/**
* Process network events until a given boolean becomes true and
* a given amount of time has elapsed. Both conditions are optional.
*
* @param[in] aDone pointer to the boolean; if NULL, the parameter is ignored.
*
* @param[in] aIntervalMs pointer to the number of milliseconds. The function will
* process events until at least this number of milliseconds
* has elapsed. If NULL, the parameter is ignored.
*/
void ServiceNetworkUntil(const bool *aDone, const uint32_t *aIntervalMs)
{
uint64_t startTimeMs = NowMs();
uint64_t elapsedMs = 0;
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
while (((aDone != NULL) && !(*aDone)) ||
((aIntervalMs != NULL) && (elapsedMs < *aIntervalMs)))
{
ServiceNetwork(sleepTime);
elapsedMs = NowMs() - startTimeMs;
}
}