diff --git a/.clang-format b/.clang-format index b384cc6d396..3f3dd2aabb8 100644 --- a/.clang-format +++ b/.clang-format @@ -55,11 +55,11 @@ DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true -ForEachMacros: +ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH -IncludeCategories: +IncludeCategories: - Regex: '^ +#include +#include #include #include "utils/code_utils.h" #define UTILS_SOCKET_LOCAL_HOST_ADDR "127.0.0.1" #define UTILS_SOCKET_GROUP_ADDR "224.0.0.116" +#define UTILS_SOCKET_GROUP_ADDR6 "ff02::116" const char *gLocalHost = UTILS_SOCKET_LOCAL_HOST_ADDR; @@ -56,17 +59,127 @@ void utilsAddFdToFdSet(int aFd, fd_set *aFdSet, int *aMaxFd) return; } -void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase) +static bool IsAddressLinkLocal(const struct in6_addr *aAddress) +{ + return ((aAddress->s6_addr[0] & 0xff) == 0xfe) && ((aAddress->s6_addr[1] & 0xc0) == 0x80); +} + +static void InitRxSocket(utilsSocket *aSocket, const struct in_addr *aIp4Address, unsigned int aIfIndex) +{ + int fd; + int one = 1; + int rval; + + fd = socket(aIp4Address ? AF_INET : AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + otEXPECT_ACTION(fd != -1, perror("socket(RxFd)")); + + rval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEADDR)")); + + rval = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEPORT)")); + + if (aIp4Address) + { + struct ip_mreqn mreq; + struct sockaddr_in *sockaddr = &aSocket->mGroupAddr.mSockAddr4; + + rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, aIp4Address, sizeof(*aIp4Address)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_MULTICAST_IF)")); + + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->sin_family = AF_INET; + sockaddr->sin_port = htons(aSocket->mPortBase); + otEXPECT_ACTION(inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &sockaddr->sin_addr), perror("inet_pton(AF_INET)")); + + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = sockaddr->sin_addr; + mreq.imr_address = *aIp4Address; // This address is used to identify the network interface + + rval = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_ADD_MEMBERSHIP)")); + + rval = bind(fd, (struct sockaddr *)sockaddr, sizeof(*sockaddr)); + otEXPECT_ACTION(rval != -1, perror("bind(RxFd)")); + } + else + { + struct ipv6_mreq mreq; + struct sockaddr_in6 *sockaddr = &aSocket->mGroupAddr.mSockAddr6; + + rval = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &aIfIndex, sizeof(aIfIndex)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IPV6_MULTICAST_IF)")); + + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->sin6_family = AF_INET6; + sockaddr->sin6_port = htons(aSocket->mPortBase); + sockaddr->sin6_scope_id = aIfIndex; // This specifies network interface for link local scope + otEXPECT_ACTION(inet_pton(AF_INET6, UTILS_SOCKET_GROUP_ADDR6, &sockaddr->sin6_addr), + perror("inet_pton(AF_INET6)")); + + memset(&mreq, 0, sizeof(mreq)); + mreq.ipv6mr_multiaddr = sockaddr->sin6_addr; + mreq.ipv6mr_interface = aIfIndex; + + rval = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IPV6_JOIN_GROUP)")); + + rval = bind(fd, (struct sockaddr *)sockaddr, sizeof(*sockaddr)); + otEXPECT_ACTION(rval != -1, perror("bind(RxFd)")); + } + + aSocket->mRxFd = fd; + +exit: + if (aSocket->mRxFd == -1) + { + exit(EXIT_FAILURE); + } +} + +void InitTxSocketIp6(utilsSocket *aSocket, const struct in6_addr *aAddress, unsigned int aIfIndex) +{ + int fd; + int one = 1; + int rval; + struct sockaddr_in6 sockaddr; + + fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + otEXPECT_ACTION(fd != -1, perror("socket(TxFd)")); + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_addr = *aAddress; + sockaddr.sin6_port = htons(aSocket->mPort); + if (IsAddressLinkLocal(aAddress)) + { + sockaddr.sin6_scope_id = aIfIndex; + } + + rval = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &aIfIndex, sizeof(aIfIndex)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IPV6_MULTICAST_IF)")); + + rval = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IPV6_MULTICAST_LOOP)")); + + rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); + otEXPECT_ACTION(rval != -1, perror("bind(TxFd)")); + + aSocket->mTxFd = fd; + +exit: + if (aSocket->mTxFd == -1) + { + exit(EXIT_FAILURE); + } +} + +static void InitTxSocketIp4(utilsSocket *aSocket, const struct in_addr *aAddress) { int fd; int one = 1; int rval; struct sockaddr_in sockaddr; - struct ip_mreqn mreq; - - aSocket->mInitialized = false; - aSocket->mPortBase = aPortBase; - aSocket->mPort = (uint16_t)(aSocket->mPortBase + gNodeId); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Prepare `mTxFd` @@ -75,9 +188,9 @@ void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase) otEXPECT_ACTION(fd != -1, perror("socket(TxFd)")); memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(aSocket->mPort); - sockaddr.sin_addr.s_addr = inet_addr(gLocalHost); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(aSocket->mPort); + sockaddr.sin_addr = *aAddress; rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)); otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IP_MULTICAST_IF)")); @@ -90,43 +203,158 @@ void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase) aSocket->mTxFd = fd; - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Prepare `mRxFd` +exit: + if (aSocket->mTxFd == -1) + { + exit(EXIT_FAILURE); + } +} - fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - otEXPECT_ACTION(fd != -1, perror("socket(RxFd)")); +static bool TryInitSocketIfname(utilsSocket *aSocket, const char *aLocalHost) +{ + const struct in6_addr *addr6 = NULL; + const struct in6_addr *addr6ll = NULL; + const struct in_addr *addr4 = NULL; + struct ifaddrs *ifaddr = NULL; + unsigned int ifIndex = 0; - rval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEADDR)")); + otEXPECT((ifIndex = if_nametoindex(aLocalHost))); - rval = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); - otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEPORT)")); + if (getifaddrs(&ifaddr) == -1) + { + perror("getifaddrs"); + exit(EXIT_FAILURE); + } - memset(&mreq, 0, sizeof(mreq)); - inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &mreq.imr_multiaddr); + for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL || strcmp(ifa->ifa_name, aLocalHost) != 0) + { + continue; + } - mreq.imr_address.s_addr = inet_addr(gLocalHost); + if (ifa->ifa_addr->sa_family == AF_INET) + { + addr4 = &((const struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + addr6 = &((const struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + if (IsAddressLinkLocal(addr6)) + { + addr6ll = addr6; + } + } + } - rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)); - otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_MULTICAST_IF)")); + // Prefer + // 1. IPv6 link local address + // 2. IPv4 addresses + // 3. IPv6 addresses + if (addr6ll) + { + InitTxSocketIp6(aSocket, addr6ll, ifIndex); + addr6 = addr6ll; + } + else if (addr4) + { + InitTxSocketIp4(aSocket, addr4); + addr6 = NULL; + } + else if (addr6) + { + InitTxSocketIp6(aSocket, addr6, ifIndex); + } + else + { + fprintf(stderr, "No sock address for TX socket!\n"); + exit(EXIT_FAILURE); + } - rval = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); - otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_ADD_MEMBERSHIP)")); + InitRxSocket(aSocket, (addr6 ? NULL : addr4), ifIndex); + aSocket->mInitialized = true; + aSocket->mUseIp6 = (addr6 != NULL); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(aSocket->mPortBase); - sockaddr.sin_addr.s_addr = inet_addr(UTILS_SOCKET_GROUP_ADDR); +exit: + freeifaddrs(ifaddr); + return aSocket->mInitialized; +} - rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - otEXPECT_ACTION(rval != -1, perror("bind(RxFd)")); +static bool TryInitSocketIp4(utilsSocket *aSocket, const char *aLocalHost) +{ + struct in_addr addr4; - aSocket->mRxFd = fd; + otEXPECT(inet_pton(AF_INET, aLocalHost, &addr4)); + InitTxSocketIp4(aSocket, &addr4); + InitRxSocket(aSocket, &addr4, 0); aSocket->mInitialized = true; + aSocket->mUseIp6 = false; exit: - if (!aSocket->mInitialized) + return aSocket->mInitialized; +} + +static bool TryInitSocketIp6(utilsSocket *aSocket, const char *aLocalHost) +{ + struct in6_addr addr6; + struct ifaddrs *ifaddr = NULL; + + otEXPECT(inet_pton(AF_INET6, aLocalHost, &addr6)); + + if (getifaddrs(&ifaddr) == -1) + { + perror("getifaddrs"); + exit(EXIT_FAILURE); + } + + for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + const struct sockaddr_in6 *sockaddr6; + unsigned int ifIndex; + + if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET6) + { + continue; + } + + sockaddr6 = (const struct sockaddr_in6 *)ifa->ifa_addr; + if (memcmp(&sockaddr6->sin6_addr, &addr6, sizeof(addr6))) + { + continue; + } + + ifIndex = if_nametoindex(ifa->ifa_name); + if (ifIndex == 0) + { + perror("if_nametoindex"); + exit(EXIT_FAILURE); + } + + InitTxSocketIp6(aSocket, &addr6, ifIndex); + InitRxSocket(aSocket, NULL, ifIndex); + aSocket->mInitialized = true; + aSocket->mUseIp6 = true; + break; + } + +exit: + freeifaddrs(ifaddr); + return aSocket->mInitialized; +} + +void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase) +{ + aSocket->mInitialized = false; + aSocket->mPortBase = aPortBase; + aSocket->mTxFd = -1; + aSocket->mRxFd = -1; + aSocket->mPort = (uint16_t)(aSocket->mPortBase + gNodeId); + + if (!TryInitSocketIfname(aSocket, gLocalHost) && !TryInitSocketIp4(aSocket, gLocalHost) && + !TryInitSocketIp6(aSocket, gLocalHost)) { + fprintf(stderr, "Failed to simulate node %d on %s\n", gNodeId, gLocalHost); exit(EXIT_FAILURE); } } @@ -174,10 +402,14 @@ uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket, uint16_t aBufferSize, uint16_t *aSenderNodeId) { - struct sockaddr_in sockaddr; - socklen_t socklen = sizeof(sockaddr); - ssize_t rval; - uint16_t len = 0; + ssize_t rval; + uint16_t len = 0; + union + { + struct sockaddr_in sockaddr4; + struct sockaddr_in6 sockaddr6; + } sockaddr; + socklen_t socklen = aSocket->mUseIp6 ? sizeof(sockaddr.sockaddr6) : sizeof(sockaddr.sockaddr4); memset(&sockaddr, 0, sizeof(sockaddr)); @@ -185,7 +417,7 @@ uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket, if (rval > 0) { - uint16_t senderPort = ntohs(sockaddr.sin_port); + uint16_t senderPort = ntohs(aSocket->mUseIp6 ? sockaddr.sockaddr6.sin6_port : sockaddr.sockaddr4.sin_port); if (aSenderNodeId != NULL) { @@ -209,16 +441,11 @@ uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket, void utilsSendOverSocket(const utilsSocket *aSocket, const void *aBuffer, uint16_t aBufferLength) { - ssize_t rval; - struct sockaddr_in sockaddr; - - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(aSocket->mPortBase); - inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &sockaddr.sin_addr); + ssize_t rval; rval = - sendto(aSocket->mTxFd, (const char *)aBuffer, aBufferLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); + sendto(aSocket->mTxFd, (const char *)aBuffer, aBufferLength, 0, (const struct sockaddr *)&aSocket->mGroupAddr, + (aSocket->mUseIp6 ? sizeof(aSocket->mGroupAddr.mSockAddr6) : sizeof(aSocket->mGroupAddr.mSockAddr4))); if (rval < 0) { diff --git a/examples/platforms/simulation/simul_utils.h b/examples/platforms/simulation/simul_utils.h index 0d70f2dd350..c3d1fb3867c 100644 --- a/examples/platforms/simulation/simul_utils.h +++ b/examples/platforms/simulation/simul_utils.h @@ -40,10 +40,16 @@ typedef struct utilsSocket { bool mInitialized; ///< Whether or not initialized. + bool mUseIp6; ///< Whether IPv6 or IPv4. int mTxFd; ///< RX file descriptor. int mRxFd; ///< TX file descriptor. uint16_t mPortBase; ///< Base port number value. uint16_t mPort; ///< The port number used by this node + union + { + struct sockaddr_in mSockAddr4; ///< The IPv4 group sock address. + struct sockaddr_in6 mSockAddr6; ///< The IPv4 group sock address. + } mGroupAddr; ///< The group sock address for simulating radio. } utilsSocket; extern const char *gLocalHost; ///< Local host address to use for sockets diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index ddf1bb2101f..8f3e9d359d3 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -40,9 +40,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -55,7 +53,6 @@ #include #include "simul_utils.h" -#include "utils/code_utils.h" uint32_t gNodeId = 1; @@ -105,56 +102,6 @@ static void PrintUsage(const char *aProgramName, int aExitCode) exit(aExitCode); } -static const char *GetLocalHostAddress(const char *aLocalHost) -{ - struct ifaddrs *ifaddr; - static char ipstr[INET_ADDRSTRLEN] = {0}; - const char *rval = NULL; - - { - struct in_addr addr; - - otEXPECT_ACTION(inet_aton(aLocalHost, &addr) == 0, rval = aLocalHost); - } - - if (getifaddrs(&ifaddr) == -1) - { - perror("getifaddrs"); - exit(EXIT_FAILURE); - } - - for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET) - { - continue; - } - - if (strcmp(ifa->ifa_name, aLocalHost) == 0) - { - struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr; - - if (inet_ntop(AF_INET, &addr->sin_addr, ipstr, sizeof(ipstr))) - { - break; - } - } - } - - freeifaddrs(ifaddr); - - if (ipstr[0] == '\0') - { - fprintf(stderr, "Local host address not found!\n"); - exit(EXIT_FAILURE); - } - - rval = ipstr; - -exit: - return rval; -} - void otSysInit(int aArgCount, char *aArgVector[]) { char *endptr; @@ -210,8 +157,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX; break; case OT_SIM_OPT_LOCAL_HOST: - gLocalHost = GetLocalHostAddress(optarg); - fprintf(stderr, "Simulate on %s\n", gLocalHost); + gLocalHost = optarg; break; case OT_SIM_OPT_TIME_SPEED: speedUpFactor = (uint32_t)strtol(optarg, &endptr, 10); diff --git a/tests/scripts/expect/posix-rcp-local-host.exp b/script/check-simulation-local-host similarity index 50% rename from tests/scripts/expect/posix-rcp-local-host.exp rename to script/check-simulation-local-host index e8c820135bf..7c375685db7 100755 --- a/tests/scripts/expect/posix-rcp-local-host.exp +++ b/script/check-simulation-local-host @@ -1,4 +1,4 @@ -#!/usr/bin/expect -f +#!/bin/bash # # Copyright (c) 2024, The OpenThread Authors. # All rights reserved. @@ -27,18 +27,58 @@ # POSSIBILITY OF SUCH DAMAGE. # -source "tests/scripts/expect/_common.exp" +set -euxo pipefail -spawn_node 1 rcp "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=-Llo&forkpty-arg=1" -send "factoryreset\n" -wait_for "state" "disabled" -setup_default_network -attach +IFACE_NAME="dummy116" +EXPECT_TEST=tests/scripts/expect/cli-ping.exp -spawn_node 2 rcp "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=--local-host=127.0.0.1&forkpty-arg=2" -send "factoryreset\n" -wait_for "state" "disabled" -setup_default_network -attach child +cleanup() +{ + echo "Cleaning up..." + sudo ip link set dev "$IFACE_NAME" down + sudo ip link del dev "$IFACE_NAME" +} -dispose_all +setup_dummy116() +{ + ip -V >/dev/null 2>&1 || { + echo "Error: iproute2 is required but not installed. Please install it (e.g., 'sudo apt install iproute2' or similar) and try again." + exit 1 + } + + ip link show "$IFACE_NAME" >/dev/null 2>&1 || sudo ip link add "$IFACE_NAME" type dummy + + sudo ip link set dev "$IFACE_NAME" up + + IP6ADDR="$(ip addr show $IFACE_NAME | grep fe80:: | awk '{print $2}' | cut -d/ -f1)" + echo "Simulated interface $IFACE_NAME created with IPv6 address $IP6ADDR" + + trap cleanup EXIT +} + +test_ipv6() +{ + setup_dummy116 + OT_SIMULATION_LOCAL_HOST=$IFACE_NAME ./script/test build expect $EXPECT_TEST + OT_SIMULATION_LOCAL_HOST=$IP6ADDR ./script/test build expect $EXPECT_TEST + + OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=$IFACE_NAME ./script/test build expect $EXPECT_TEST + OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=$IP6ADDR ./script/test build expect $EXPECT_TEST +} + +test_ipv4() +{ + OT_SIMULATION_LOCAL_HOST=lo ./script/test build expect $EXPECT_TEST + OT_SIMULATION_LOCAL_HOST=127.0.0.1 ./script/test build expect $EXPECT_TEST + + OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=lo ./script/test build expect $EXPECT_TEST + OT_NODE_TYPE=rcp OT_SIMULATION_LOCAL_HOST=127.0.0.1 ./script/test build expect $EXPECT_TEST +} + +main() +{ + test_ipv4 + test_ipv6 +} + +main "$@" diff --git a/tests/scripts/expect/_common.exp b/tests/scripts/expect/_common.exp index 3835de8d719..a94799e2d1e 100644 --- a/tests/scripts/expect/_common.exp +++ b/tests/scripts/expect/_common.exp @@ -84,6 +84,13 @@ proc spawn_node {id {type ""} {radio_url ""}} { set gcov_prefix "ot-run/$argv0/ot-gcda.$id" } + if {[info exists ::env(OT_SIMULATION_LOCAL_HOST)]} { + set ot_simulation_local_host $::env(OT_SIMULATION_LOCAL_HOST) + set radio_url "$radio_url&forkpty-arg=-L$ot_simulation_local_host" + } else { + set ot_simulation_local_host "127.0.0.1" + } + switch -regexp ${type} { {rcp|rcp-cli} { # Sleep 0.2 seconds to ensure that the ot-rcp in the previous test has exited to @@ -97,7 +104,8 @@ proc spawn_node {id {type ""} {radio_url ""}} { expect_line "Done" } cli { - spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $::env(OT_SIMULATION_APPS)/cli/ot-cli-ftd $id + spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $::env(OT_SIMULATION_APPS)/cli/ot-cli-ftd \ + -L$ot_simulation_local_host $id send "factoryreset\n" wait_for "state" "disabled" expect_line "Done" @@ -105,7 +113,8 @@ proc spawn_node {id {type ""} {radio_url ""}} { expect_line "Done" } mtd { - spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $::env(OT_SIMULATION_APPS)/cli/ot-cli-mtd $id + spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $::env(OT_SIMULATION_APPS)/cli/ot-cli-mtd \ + -L$ot_simulation_local_host $id send "factoryreset\n" wait_for "state" "disabled" expect_line "Done"