diff --git a/examples/platforms/simulation/simul_utils.c b/examples/platforms/simulation/simul_utils.c index 7b5540616..36b69af5d 100644 --- a/examples/platforms/simulation/simul_utils.c +++ b/examples/platforms/simulation/simul_utils.c @@ -36,6 +36,8 @@ #define UTILS_SOCKET_LOCAL_HOST_ADDR "127.0.0.1" #define UTILS_SOCKET_GROUP_ADDR "224.0.0.116" +const char *gLocalHost = UTILS_SOCKET_LOCAL_HOST_ADDR; + void utilsAddFdToFdSet(int aFd, fd_set *aFdSet, int *aMaxFd) { otEXPECT(aFd >= 0); @@ -75,7 +77,7 @@ void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase) memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(aSocket->mPort); - sockaddr.sin_addr.s_addr = inet_addr(UTILS_SOCKET_LOCAL_HOST_ADDR); + sockaddr.sin_addr.s_addr = inet_addr(gLocalHost); 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)")); @@ -103,7 +105,7 @@ void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase) memset(&mreq, 0, sizeof(mreq)); inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &mreq.imr_multiaddr); - mreq.imr_address.s_addr = inet_addr(UTILS_SOCKET_LOCAL_HOST_ADDR); + mreq.imr_address.s_addr = inet_addr(gLocalHost); 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)")); diff --git a/examples/platforms/simulation/simul_utils.h b/examples/platforms/simulation/simul_utils.h index 39496d56e..0d70f2dd3 100644 --- a/examples/platforms/simulation/simul_utils.h +++ b/examples/platforms/simulation/simul_utils.h @@ -46,6 +46,8 @@ typedef struct utilsSocket uint16_t mPort; ///< The port number used by this node } utilsSocket; +extern const char *gLocalHost; ///< Local host address to use for sockets + /** * Adds a file descriptor (FD) to a given FD set. * diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index bb648b5a3..07af5891b 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -36,19 +36,27 @@ #if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 +#include #include #include #include +#include #include +#include #include #include #include #include +#include +#include #include #include #include +#include "simul_utils.h" +#include "utils/code_utils.h" + uint32_t gNodeId = 1; extern bool gPlatformPseudoResetWasRequested; @@ -71,6 +79,7 @@ enum { OT_SIM_OPT_HELP = 'h', OT_SIM_OPT_ENABLE_ENERGY_SCAN = 'E', + OT_SIM_OPT_LOCAL_HOST = 'L', OT_SIM_OPT_SLEEP_TO_TX = 't', OT_SIM_OPT_TIME_SPEED = 's', OT_SIM_OPT_LOG_FILE = 'l', @@ -96,6 +105,56 @@ 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; @@ -106,6 +165,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) {"enable-energy-scan", no_argument, 0, OT_SIM_OPT_ENABLE_ENERGY_SCAN}, {"sleep-to-tx", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX}, {"time-speed", required_argument, 0, OT_SIM_OPT_TIME_SPEED}, + {"local-host", required_argument, 0, OT_SIM_OPT_LOCAL_HOST}, #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) {"log-file", required_argument, 0, OT_SIM_OPT_LOG_FILE}, #endif @@ -113,9 +173,9 @@ void otSysInit(int aArgCount, char *aArgVector[]) }; #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) - static const char options[] = "Ehts:l:"; + static const char options[] = "Ehts:L:l:"; #else - static const char options[] = "Ehts:"; + static const char options[] = "Ehts:L:"; #endif if (gPlatformPseudoResetWasRequested) @@ -149,6 +209,10 @@ void otSysInit(int aArgCount, char *aArgVector[]) case OT_SIM_OPT_SLEEP_TO_TX: gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX; break; + case OT_SIM_OPT_LOCAL_HOST: + gLocalHost = GetLocalHostAddress(optarg); + fprintf(stderr, "Simulate on %s\n", gLocalHost); + break; case OT_SIM_OPT_TIME_SPEED: speedUpFactor = (uint32_t)strtol(optarg, &endptr, 10); if (*endptr != '\0' || speedUpFactor == 0) diff --git a/tests/scripts/expect/posix-rcp-local-host.exp b/tests/scripts/expect/posix-rcp-local-host.exp new file mode 100755 index 000000000..e8c820135 --- /dev/null +++ b/tests/scripts/expect/posix-rcp-local-host.exp @@ -0,0 +1,44 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2024, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +source "tests/scripts/expect/_common.exp" + +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 + +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 + +dispose_all