diff options
author | Yakun Xu <xyk@google.com> | 2024-05-14 00:27:54 +0800 |
---|---|---|
committer | Yakun Xu <xyk@google.com> | 2024-05-14 17:42:26 +0800 |
commit | c3ecbcde4070b6ab98213f9a18bb456976cfa26c (patch) | |
tree | bd91cf64e4cece9e48c2ae4177fcf9b4bdd14307 | |
parent | 70829a996f34c2c1aabe7977517c46bb6b860def (diff) | |
download | openthread-master.tar.gz |
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-format | 4 | ||||
-rw-r--r-- | .github/workflows/simulation-1.1.yml | 28 | ||||
-rw-r--r-- | examples/platforms/simulation/simul_utils.c | 315 | ||||
-rw-r--r-- | examples/platforms/simulation/simul_utils.h | 6 | ||||
-rw-r--r-- | examples/platforms/simulation/system.c | 56 | ||||
-rwxr-xr-x | script/check-simulation-local-host (renamed from tests/scripts/expect/posix-rcp-local-host.exp) | 66 | ||||
-rw-r--r-- | tests/scripts/expect/_common.exp | 13 |
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" |