aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYakun Xu <xyk@google.com>2024-05-14 00:27:54 +0800
committerYakun Xu <xyk@google.com>2024-05-14 17:42:26 +0800
commitc3ecbcde4070b6ab98213f9a18bb456976cfa26c (patch)
treebd91cf64e4cece9e48c2ae4177fcf9b4bdd14307
parent70829a996f34c2c1aabe7977517c46bb6b860def (diff)
downloadopenthread-master.tar.gz
[simulation] support simulating radio over IPv6 (#10194)HEADmastermain
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. (cherry picked from commit 39ce8117a5f7e09ae4d27fa626387127f819ecdc) Bug: 329188649 Test: presubmit Change-Id: I2d9c6a84254f2fff2773ddff25a88c985685fd00
-rw-r--r--.clang-format4
-rw-r--r--.github/workflows/simulation-1.1.yml28
-rw-r--r--examples/platforms/simulation/simul_utils.c315
-rw-r--r--examples/platforms/simulation/simul_utils.h6
-rw-r--r--examples/platforms/simulation/system.c56
-rwxr-xr-xscript/check-simulation-local-host (renamed from tests/scripts/expect/posix-rcp-local-host.exp)66
-rw-r--r--tests/scripts/expect/_common.exp13
7 files changed, 372 insertions, 116 deletions
diff --git a/.clang-format b/.clang-format
index b384cc6d3..3f3dd2aab 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: '^<openthread/.*/'
Priority: 4
- Regex: '^<openthread/'
diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml
index ee08f3954..4706331b2 100644
--- a/.github/workflows/simulation-1.1.yml
+++ b/.github/workflows/simulation-1.1.yml
@@ -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
diff --git a/examples/platforms/simulation/simul_utils.c b/examples/platforms/simulation/simul_utils.c
index 36b69af5d..df0600727 100644
--- a/examples/platforms/simulation/simul_utils.c
+++ b/examples/platforms/simulation/simul_utils.c
@@ -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)"));
+ // 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 0d70f2dd3..c3d1fb386 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 2abe892e3..3991e67ff 100644
--- a/examples/platforms/simulation/system.c
+++ b/examples/platforms/simulation/system.c
@@ -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);
diff --git a/tests/scripts/expect/posix-rcp-local-host.exp b/script/check-simulation-local-host
index e8c820135..7c375685d 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 3835de8d7..a94799e2d 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"