[simulation] support simulating radio over IPv6 (#10194)

This commit adds support to simulate Thread radio over IPv6.

With this commit, a simulation will be simulated over either IPv6 or IPv4.
If it's simulated on IPv6, it communicates with other simulation nodes
in IPv6 group `ff02::116`. And if it's simulated on IPv4, it communicates
with other simulation nodes in IPv4 group `224.0.0.116`.

Note that simulating virtual time is not included in this commit.
This commit is contained in:
Yakun Xu
2024-05-14 00:27:54 +08:00
committed by GitHub
parent 7cc636ccce
commit 39ce8117a5
7 changed files with 376 additions and 120 deletions

View File

@ -55,11 +55,11 @@ DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
IncludeCategories:
- Regex: '^<openthread/.*/'
Priority: 4
- Regex: '^<openthread/'

View File

@ -364,6 +364,34 @@ jobs:
path: tmp/coverage.info
retention-days: 1
simulation-local-host:
runs-on: ubuntu-20.04
env:
COVERAGE: 1
steps:
- name: Harden Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y expect ninja-build lcov
- name: Run
run: |
./script/check-simulation-local-host
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-simulation-local-host
path: tmp/coverage.info
retention-days: 1
upload-coverage:
needs:
- packet-verification

View File

@ -29,12 +29,15 @@
#include "simul_utils.h"
#include <errno.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/time.h>
#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 @@ exit:
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)"));
rval = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_ADD_MEMBERSHIP)"));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(aSocket->mPortBase);
sockaddr.sin_addr.s_addr = inet_addr(UTILS_SOCKET_GROUP_ADDR);
rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
otEXPECT_ACTION(rval != -1, perror("bind(RxFd)"));
aSocket->mRxFd = fd;
// 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);
}
InitRxSocket(aSocket, (addr6 ? NULL : addr4), ifIndex);
aSocket->mInitialized = true;
aSocket->mUseIp6 = (addr6 != NULL);
exit:
if (!aSocket->mInitialized)
freeifaddrs(ifaddr);
return aSocket->mInitialized;
}
static bool TryInitSocketIp4(utilsSocket *aSocket, const char *aLocalHost)
{
struct in_addr addr4;
otEXPECT(inet_pton(AF_INET, aLocalHost, &addr4));
InitTxSocketIp4(aSocket, &addr4);
InitRxSocket(aSocket, &addr4, 0);
aSocket->mInitialized = true;
aSocket->mUseIp6 = false;
exit:
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)
{

View File

@ -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

View File

@ -40,9 +40,7 @@
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <ifaddrs.h>
#include <libgen.h>
#include <netinet/in.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@ -55,7 +53,6 @@
#include <openthread/platform/radio.h>
#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);

View File

@ -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 "$@"

View File

@ -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"