mirror of
https://github.com/espressif/openthread.git
synced 2025-05-17 07:16:08 +08:00
[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:
@ -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/'
|
||||
|
28
.github/workflows/simulation-1.1.yml
vendored
28
.github/workflows/simulation-1.1.yml
vendored
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 "$@"
|
@ -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"
|
||||
|
Reference in New Issue
Block a user