mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00: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:
+2
-2
@@ -55,11 +55,11 @@ DerivePointerAlignment: false
|
|||||||
DisableFormat: false
|
DisableFormat: false
|
||||||
ExperimentalAutoDetectBinPacking: false
|
ExperimentalAutoDetectBinPacking: false
|
||||||
FixNamespaceComments: true
|
FixNamespaceComments: true
|
||||||
ForEachMacros:
|
ForEachMacros:
|
||||||
- foreach
|
- foreach
|
||||||
- Q_FOREACH
|
- Q_FOREACH
|
||||||
- BOOST_FOREACH
|
- BOOST_FOREACH
|
||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
- Regex: '^<openthread/.*/'
|
- Regex: '^<openthread/.*/'
|
||||||
Priority: 4
|
Priority: 4
|
||||||
- Regex: '^<openthread/'
|
- Regex: '^<openthread/'
|
||||||
|
|||||||
@@ -364,6 +364,34 @@ jobs:
|
|||||||
path: tmp/coverage.info
|
path: tmp/coverage.info
|
||||||
retention-days: 1
|
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:
|
upload-coverage:
|
||||||
needs:
|
needs:
|
||||||
- packet-verification
|
- packet-verification
|
||||||
|
|||||||
@@ -29,12 +29,15 @@
|
|||||||
#include "simul_utils.h"
|
#include "simul_utils.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <net/if.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include "utils/code_utils.h"
|
#include "utils/code_utils.h"
|
||||||
|
|
||||||
#define UTILS_SOCKET_LOCAL_HOST_ADDR "127.0.0.1"
|
#define UTILS_SOCKET_LOCAL_HOST_ADDR "127.0.0.1"
|
||||||
#define UTILS_SOCKET_GROUP_ADDR "224.0.0.116"
|
#define UTILS_SOCKET_GROUP_ADDR "224.0.0.116"
|
||||||
|
#define UTILS_SOCKET_GROUP_ADDR6 "ff02::116"
|
||||||
|
|
||||||
const char *gLocalHost = UTILS_SOCKET_LOCAL_HOST_ADDR;
|
const char *gLocalHost = UTILS_SOCKET_LOCAL_HOST_ADDR;
|
||||||
|
|
||||||
@@ -56,17 +59,127 @@ exit:
|
|||||||
return;
|
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 fd;
|
||||||
int one = 1;
|
int one = 1;
|
||||||
int rval;
|
int rval;
|
||||||
struct sockaddr_in sockaddr;
|
struct sockaddr_in sockaddr;
|
||||||
struct ip_mreqn mreq;
|
|
||||||
|
|
||||||
aSocket->mInitialized = false;
|
|
||||||
aSocket->mPortBase = aPortBase;
|
|
||||||
aSocket->mPort = (uint16_t)(aSocket->mPortBase + gNodeId);
|
|
||||||
|
|
||||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// Prepare `mTxFd`
|
// Prepare `mTxFd`
|
||||||
@@ -75,9 +188,9 @@ void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase)
|
|||||||
otEXPECT_ACTION(fd != -1, perror("socket(TxFd)"));
|
otEXPECT_ACTION(fd != -1, perror("socket(TxFd)"));
|
||||||
|
|
||||||
memset(&sockaddr, 0, sizeof(sockaddr));
|
memset(&sockaddr, 0, sizeof(sockaddr));
|
||||||
sockaddr.sin_family = AF_INET;
|
sockaddr.sin_family = AF_INET;
|
||||||
sockaddr.sin_port = htons(aSocket->mPort);
|
sockaddr.sin_port = htons(aSocket->mPort);
|
||||||
sockaddr.sin_addr.s_addr = inet_addr(gLocalHost);
|
sockaddr.sin_addr = *aAddress;
|
||||||
|
|
||||||
rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr));
|
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)"));
|
otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IP_MULTICAST_IF)"));
|
||||||
@@ -90,43 +203,158 @@ void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase)
|
|||||||
|
|
||||||
aSocket->mTxFd = fd;
|
aSocket->mTxFd = fd;
|
||||||
|
|
||||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
exit:
|
||||||
// Prepare `mRxFd`
|
if (aSocket->mTxFd == -1)
|
||||||
|
{
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
static bool TryInitSocketIfname(utilsSocket *aSocket, const char *aLocalHost)
|
||||||
otEXPECT_ACTION(fd != -1, perror("socket(RxFd)"));
|
{
|
||||||
|
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((ifIndex = if_nametoindex(aLocalHost)));
|
||||||
otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEADDR)"));
|
|
||||||
|
|
||||||
rval = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
|
if (getifaddrs(&ifaddr) == -1)
|
||||||
otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEPORT)"));
|
{
|
||||||
|
perror("getifaddrs");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
memset(&mreq, 0, sizeof(mreq));
|
for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
|
||||||
inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &mreq.imr_multiaddr);
|
{
|
||||||
|
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));
|
// Prefer
|
||||||
otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_MULTICAST_IF)"));
|
// 1. IPv6 link local address
|
||||||
|
// 2. IPv4 addresses
|
||||||
rval = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
|
// 3. IPv6 addresses
|
||||||
otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_ADD_MEMBERSHIP)"));
|
if (addr6ll)
|
||||||
|
{
|
||||||
sockaddr.sin_family = AF_INET;
|
InitTxSocketIp6(aSocket, addr6ll, ifIndex);
|
||||||
sockaddr.sin_port = htons(aSocket->mPortBase);
|
addr6 = addr6ll;
|
||||||
sockaddr.sin_addr.s_addr = inet_addr(UTILS_SOCKET_GROUP_ADDR);
|
}
|
||||||
|
else if (addr4)
|
||||||
rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
|
{
|
||||||
otEXPECT_ACTION(rval != -1, perror("bind(RxFd)"));
|
InitTxSocketIp4(aSocket, addr4);
|
||||||
|
addr6 = NULL;
|
||||||
aSocket->mRxFd = fd;
|
}
|
||||||
|
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->mInitialized = true;
|
||||||
|
aSocket->mUseIp6 = (addr6 != NULL);
|
||||||
|
|
||||||
exit:
|
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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,10 +402,14 @@ uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket,
|
|||||||
uint16_t aBufferSize,
|
uint16_t aBufferSize,
|
||||||
uint16_t *aSenderNodeId)
|
uint16_t *aSenderNodeId)
|
||||||
{
|
{
|
||||||
struct sockaddr_in sockaddr;
|
ssize_t rval;
|
||||||
socklen_t socklen = sizeof(sockaddr);
|
uint16_t len = 0;
|
||||||
ssize_t rval;
|
union
|
||||||
uint16_t len = 0;
|
{
|
||||||
|
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));
|
memset(&sockaddr, 0, sizeof(sockaddr));
|
||||||
|
|
||||||
@@ -185,7 +417,7 @@ uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket,
|
|||||||
|
|
||||||
if (rval > 0)
|
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)
|
if (aSenderNodeId != NULL)
|
||||||
{
|
{
|
||||||
@@ -209,16 +441,11 @@ uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket,
|
|||||||
|
|
||||||
void utilsSendOverSocket(const utilsSocket *aSocket, const void *aBuffer, uint16_t aBufferLength)
|
void utilsSendOverSocket(const utilsSocket *aSocket, const void *aBuffer, uint16_t aBufferLength)
|
||||||
{
|
{
|
||||||
ssize_t rval;
|
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);
|
|
||||||
|
|
||||||
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)
|
if (rval < 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,10 +40,16 @@
|
|||||||
typedef struct utilsSocket
|
typedef struct utilsSocket
|
||||||
{
|
{
|
||||||
bool mInitialized; ///< Whether or not initialized.
|
bool mInitialized; ///< Whether or not initialized.
|
||||||
|
bool mUseIp6; ///< Whether IPv6 or IPv4.
|
||||||
int mTxFd; ///< RX file descriptor.
|
int mTxFd; ///< RX file descriptor.
|
||||||
int mRxFd; ///< TX file descriptor.
|
int mRxFd; ///< TX file descriptor.
|
||||||
uint16_t mPortBase; ///< Base port number value.
|
uint16_t mPortBase; ///< Base port number value.
|
||||||
uint16_t mPort; ///< The port number used by this node
|
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;
|
} utilsSocket;
|
||||||
|
|
||||||
extern const char *gLocalHost; ///< Local host address to use for sockets
|
extern const char *gLocalHost; ///< Local host address to use for sockets
|
||||||
|
|||||||
@@ -40,9 +40,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -55,7 +53,6 @@
|
|||||||
#include <openthread/platform/radio.h>
|
#include <openthread/platform/radio.h>
|
||||||
|
|
||||||
#include "simul_utils.h"
|
#include "simul_utils.h"
|
||||||
#include "utils/code_utils.h"
|
|
||||||
|
|
||||||
uint32_t gNodeId = 1;
|
uint32_t gNodeId = 1;
|
||||||
|
|
||||||
@@ -105,56 +102,6 @@ static void PrintUsage(const char *aProgramName, int aExitCode)
|
|||||||
exit(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[])
|
void otSysInit(int aArgCount, char *aArgVector[])
|
||||||
{
|
{
|
||||||
char *endptr;
|
char *endptr;
|
||||||
@@ -210,8 +157,7 @@ void otSysInit(int aArgCount, char *aArgVector[])
|
|||||||
gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX;
|
gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX;
|
||||||
break;
|
break;
|
||||||
case OT_SIM_OPT_LOCAL_HOST:
|
case OT_SIM_OPT_LOCAL_HOST:
|
||||||
gLocalHost = GetLocalHostAddress(optarg);
|
gLocalHost = optarg;
|
||||||
fprintf(stderr, "Simulate on %s\n", gLocalHost);
|
|
||||||
break;
|
break;
|
||||||
case OT_SIM_OPT_TIME_SPEED:
|
case OT_SIM_OPT_TIME_SPEED:
|
||||||
speedUpFactor = (uint32_t)strtol(optarg, &endptr, 10);
|
speedUpFactor = (uint32_t)strtol(optarg, &endptr, 10);
|
||||||
|
|||||||
+53
-13
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/expect -f
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Copyright (c) 2024, The OpenThread Authors.
|
# Copyright (c) 2024, The OpenThread Authors.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
@@ -27,18 +27,58 @@
|
|||||||
# POSSIBILITY OF SUCH DAMAGE.
|
# 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"
|
IFACE_NAME="dummy116"
|
||||||
send "factoryreset\n"
|
EXPECT_TEST=tests/scripts/expect/cli-ping.exp
|
||||||
wait_for "state" "disabled"
|
|
||||||
setup_default_network
|
|
||||||
attach
|
|
||||||
|
|
||||||
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"
|
cleanup()
|
||||||
send "factoryreset\n"
|
{
|
||||||
wait_for "state" "disabled"
|
echo "Cleaning up..."
|
||||||
setup_default_network
|
sudo ip link set dev "$IFACE_NAME" down
|
||||||
attach child
|
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"
|
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} {
|
switch -regexp ${type} {
|
||||||
{rcp|rcp-cli} {
|
{rcp|rcp-cli} {
|
||||||
# Sleep 0.2 seconds to ensure that the ot-rcp in the previous test has exited to
|
# 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"
|
expect_line "Done"
|
||||||
}
|
}
|
||||||
cli {
|
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"
|
send "factoryreset\n"
|
||||||
wait_for "state" "disabled"
|
wait_for "state" "disabled"
|
||||||
expect_line "Done"
|
expect_line "Done"
|
||||||
@@ -105,7 +113,8 @@ proc spawn_node {id {type ""} {radio_url ""}} {
|
|||||||
expect_line "Done"
|
expect_line "Done"
|
||||||
}
|
}
|
||||||
mtd {
|
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"
|
send "factoryreset\n"
|
||||||
wait_for "state" "disabled"
|
wait_for "state" "disabled"
|
||||||
expect_line "Done"
|
expect_line "Done"
|
||||||
|
|||||||
Reference in New Issue
Block a user