From 59602155083f2414d787d4f848471d84b7d13f55 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 28 Feb 2024 11:28:00 -0800 Subject: [PATCH] [simulation] add `simul_utils.h` for socket operation helpers (#9879) This commit introduces a new module, `simul_utils.h`, under `simulation` platform. This module provides common utility functions, primarily related to socket operations (for emulation of radio or TREL interface). The new functions are used by `radio.c` and `trel.c`, consolidating code and preventing repetition. --- examples/platforms/simulation/CMakeLists.txt | 1 + examples/platforms/simulation/radio.c | 182 +++------------ examples/platforms/simulation/simul_utils.c | 226 +++++++++++++++++++ examples/platforms/simulation/simul_utils.h | 149 ++++++++++++ examples/platforms/simulation/trel.c | 151 ++----------- examples/platforms/simulation/uart.c | 34 +-- 6 files changed, 432 insertions(+), 311 deletions(-) create mode 100644 examples/platforms/simulation/simul_utils.c create mode 100644 examples/platforms/simulation/simul_utils.h diff --git a/examples/platforms/simulation/CMakeLists.txt b/examples/platforms/simulation/CMakeLists.txt index 41a1dbff6..12580e2d1 100644 --- a/examples/platforms/simulation/CMakeLists.txt +++ b/examples/platforms/simulation/CMakeLists.txt @@ -72,6 +72,7 @@ add_library(openthread-simulation misc.c multipan.c radio.c + simul_utils.c spi-stubs.c system.c trel.c diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 03808b5af..960d8c66c 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -41,14 +41,12 @@ #include #include +#include "simul_utils.h" #include "utils/code_utils.h" #include "utils/link_metrics.h" #include "utils/mac_frame.h" #include "utils/soft_source_match_table.h" -// The IPv4 group for receiving packets of radio simulation -#define OT_RADIO_GROUP "224.0.0.116" - #define MS_PER_S 1000 #define US_PER_MS 1000 @@ -75,11 +73,9 @@ extern int sSockFd; extern uint16_t sPortBase; extern uint16_t sPortOffset; #else -static int sTxFd = -1; -static int sRxFd = -1; -static uint16_t sPortBase = 9000; -static uint16_t sPortOffset = 0; -static uint16_t sPort = 0; +static utilsSocket sSocket; +static uint16_t sPortBase = 9000; +static uint16_t sPortOffset = 0; #endif static int8_t sEnergyScanResult = OT_RADIO_RSSI_INVALID; @@ -190,6 +186,8 @@ static bool NodeIdFilterIsConnectable(uint16_t aNodeId) { bool isConnectable = true; + otEXPECT_ACTION(aNodeId != gNodeId, isConnectable = false); + switch (sFilterMode) { case kFilterOff: @@ -202,6 +200,7 @@ static bool NodeIdFilterIsConnectable(uint16_t aNodeId) break; } +exit: return isConnectable; } @@ -407,82 +406,15 @@ void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) sPromiscuous = aEnable; } -#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 -static void initFds(void) -{ - int fd; - int one = 1; - struct sockaddr_in sockaddr; - - memset(&sockaddr, 0, sizeof(sockaddr)); - - otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)")); - - sPort = (uint16_t)(sPortBase + sPortOffset + gNodeId); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(sPort); - sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); - - otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)) != -1, - perror("setsockopt(sTxFd, IP_MULTICAST_IF)")); - - otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)) != -1, - perror("setsockopt(sRxFd, IP_MULTICAST_LOOP)")); - - otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sTxFd)")); - - // Tx fd is successfully initialized. - sTxFd = fd; - - otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sRxFd)")); - - otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1, - perror("setsockopt(sRxFd, SO_REUSEADDR)")); - otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1, - perror("setsockopt(sRxFd, SO_REUSEPORT)")); - - { - struct ip_mreqn mreq; - - memset(&mreq, 0, sizeof(mreq)); - inet_pton(AF_INET, OT_RADIO_GROUP, &mreq.imr_multiaddr); - - // Always use loopback device to send simulation packets. - mreq.imr_address.s_addr = inet_addr("127.0.0.1"); - - otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)) != -1, - perror("setsockopt(sRxFd, IP_MULTICAST_IF)")); - otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1, - perror("setsockopt(sRxFd, IP_ADD_MEMBERSHIP)")); - } - - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset)); - sockaddr.sin_addr.s_addr = inet_addr(OT_RADIO_GROUP); - - otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)")); - - // Rx fd is successfully initialized. - sRxFd = fd; - -exit: - if (sRxFd == -1 || sTxFd == -1) - { - exit(EXIT_FAILURE); - } -} -#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 - void platformRadioInit(void) { -#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 +#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME parseFromEnvAsUint16("PORT_BASE", &sPortBase); - parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset); sPortOffset *= (MAX_NETWORK_SIZE + 1); - initFds(); -#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 + utilsInitSocket(&sSocket, sPortBase + sPortOffset); +#endif sReceiveFrame.mPsdu = sReceiveMessage.mPsdu; sTransmitFrame.mPsdu = sTransmitMessage.mPsdu; @@ -859,24 +791,14 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen #else void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd) { - if (aReadFdSet != NULL && (sState != OT_RADIO_STATE_TRANSMIT || sTxWait)) + if (sState != OT_RADIO_STATE_TRANSMIT || sTxWait) { - FD_SET(sRxFd, aReadFdSet); - - if (aMaxFd != NULL && *aMaxFd < sRxFd) - { - *aMaxFd = sRxFd; - } + utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd); } - if (aWriteFdSet != NULL && platformRadioIsTransmitPending()) + if (platformRadioIsTransmitPending()) { - FD_SET(sTxFd, aWriteFdSet); - - if (aMaxFd != NULL && *aMaxFd < sTxFd) - { - *aMaxFd = sTxFd; - } + utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd); } if (sEnergyScanning) @@ -900,18 +822,7 @@ void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct ti } // no need to close in virtual time mode. -void platformRadioDeinit(void) -{ - if (sRxFd != -1) - { - close(sRxFd); - } - - if (sTxFd != -1) - { - close(sTxFd); - } -} +void platformRadioDeinit(void) { utilsDeinitSocket(&sSocket); } #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet) @@ -919,41 +830,22 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const OT_UNUSED_VARIABLE(aReadFdSet); OT_UNUSED_VARIABLE(aWriteFdSet); -#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 - if (FD_ISSET(sRxFd, aReadFdSet)) +#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME + if (utilsCanSocketReceive(&sSocket, aReadFdSet)) { - struct sockaddr_in sockaddr; - socklen_t len = sizeof(sockaddr); - ssize_t rval; + uint16_t senderNodeId; + uint16_t len; - memset(&sockaddr, 0, sizeof(sockaddr)); - rval = - recvfrom(sRxFd, (char *)&sReceiveMessage, sizeof(sReceiveMessage), 0, (struct sockaddr *)&sockaddr, &len); + len = utilsReceiveFromSocket(&sSocket, &sReceiveMessage, sizeof(sReceiveMessage), &senderNodeId); - if (rval > 0) + if (NodeIdFilterIsConnectable(senderNodeId)) { - uint16_t srcPort = ntohs(sockaddr.sin_port); - uint16_t srcNodeId = srcPort - sPortOffset - sPortBase; - - if (NodeIdFilterIsConnectable(srcNodeId) && srcPort != sPort) - { - sReceiveFrame.mLength = (uint16_t)(rval - 1); - - radioReceive(aInstance); - } - } - else if (rval == 0) - { - // socket is closed, which should not happen - assert(false); - } - else if (errno != EINTR && errno != EAGAIN) - { - perror("recvfrom(sRxFd)"); - exit(EXIT_FAILURE); + sReceiveFrame.mLength = len - 1; + radioReceive(aInstance); } } -#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 +#endif + if (platformRadioIsTransmitPending()) { radioSendMessage(aInstance); @@ -968,33 +860,17 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame) { -#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 - ssize_t rval; - struct sockaddr_in sockaddr; - - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - inet_pton(AF_INET, OT_RADIO_GROUP, &sockaddr.sin_addr); - - sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset)); - rval = - sendto(sTxFd, (const char *)aMessage, 1 + aFrame->mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - - if (rval < 0) - { - perror("sendto(sTxFd)"); - exit(EXIT_FAILURE); - } -#else // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 +#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME + utilsSendOverSocket(&sSocket, aMessage, aFrame->mLength + 1); // + 1 is for `mChannel` +#else struct Event event; event.mDelay = 1; // 1us for now event.mEvent = OT_SIM_EVENT_RADIO_RECEIVED; event.mDataLength = 1 + aFrame->mLength; // include channel in first byte memcpy(event.mData, aMessage, event.mDataLength); - otSimSendEvent(&event); -#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 +#endif } void radioSendAck(void) diff --git a/examples/platforms/simulation/simul_utils.c b/examples/platforms/simulation/simul_utils.c new file mode 100644 index 000000000..7b5540616 --- /dev/null +++ b/examples/platforms/simulation/simul_utils.c @@ -0,0 +1,226 @@ +/* + * 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. + */ + +#include "simul_utils.h" + +#include +#include + +#include "utils/code_utils.h" + +#define UTILS_SOCKET_LOCAL_HOST_ADDR "127.0.0.1" +#define UTILS_SOCKET_GROUP_ADDR "224.0.0.116" + +void utilsAddFdToFdSet(int aFd, fd_set *aFdSet, int *aMaxFd) +{ + otEXPECT(aFd >= 0); + otEXPECT(aFdSet != NULL); + + FD_SET(aFd, aFdSet); + + otEXPECT(aMaxFd != NULL); + + if (*aMaxFd < aFd) + { + *aMaxFd = aFd; + } + +exit: + return; +} + +void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase) +{ + 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` + + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + 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(UTILS_SOCKET_LOCAL_HOST_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)")); + + rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); + otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IP_MULTICAST_LOOP)")); + + rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); + otEXPECT_ACTION(rval != -1, perror("bind(TxFd)")); + + aSocket->mTxFd = fd; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Prepare `mRxFd` + + fd = socket(AF_INET, 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)")); + + 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); + + 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; + + aSocket->mInitialized = true; + +exit: + if (!aSocket->mInitialized) + { + exit(EXIT_FAILURE); + } +} + +void utilsDeinitSocket(utilsSocket *aSocket) +{ + if (aSocket->mInitialized) + { + close(aSocket->mRxFd); + close(aSocket->mTxFd); + aSocket->mInitialized = false; + } +} + +void utilsAddSocketRxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd) +{ + otEXPECT(aSocket->mInitialized); + utilsAddFdToFdSet(aSocket->mRxFd, aFdSet, aMaxFd); + +exit: + return; +} + +void utilsAddSocketTxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd) +{ + otEXPECT(aSocket->mInitialized); + utilsAddFdToFdSet(aSocket->mTxFd, aFdSet, aMaxFd); + +exit: + return; +} + +bool utilsCanSocketReceive(const utilsSocket *aSocket, const fd_set *aReadFdSet) +{ + return aSocket->mInitialized && FD_ISSET(aSocket->mRxFd, aReadFdSet); +} + +bool utilsCanSocketSend(const utilsSocket *aSocket, const fd_set *aWriteFdSet) +{ + return aSocket->mInitialized && FD_ISSET(aSocket->mTxFd, aWriteFdSet); +} + +uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket, + void *aBuffer, + uint16_t aBufferSize, + uint16_t *aSenderNodeId) +{ + struct sockaddr_in sockaddr; + socklen_t socklen = sizeof(sockaddr); + ssize_t rval; + uint16_t len = 0; + + memset(&sockaddr, 0, sizeof(sockaddr)); + + rval = recvfrom(aSocket->mRxFd, (char *)aBuffer, aBufferSize, 0, (struct sockaddr *)&sockaddr, &socklen); + + if (rval > 0) + { + uint16_t senderPort = ntohs(sockaddr.sin_port); + + if (aSenderNodeId != NULL) + { + *aSenderNodeId = (uint16_t)(senderPort - aSocket->mPortBase); + } + + len = (uint16_t)rval; + } + else if (rval == 0) + { + assert(false); + } + else if (errno != EINTR && errno != EAGAIN) + { + perror("recvfrom(RxFd)"); + exit(EXIT_FAILURE); + } + + return len; +} + +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); + + rval = + sendto(aSocket->mTxFd, (const char *)aBuffer, aBufferLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); + + if (rval < 0) + { + perror("sendto(sTxFd)"); + exit(EXIT_FAILURE); + } +} diff --git a/examples/platforms/simulation/simul_utils.h b/examples/platforms/simulation/simul_utils.h new file mode 100644 index 000000000..39496d56e --- /dev/null +++ b/examples/platforms/simulation/simul_utils.h @@ -0,0 +1,149 @@ +/* + * 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. + */ + +#ifndef PLATFORM_SIMULATION_SOCKET_UTILS_H_ +#define PLATFORM_SIMULATION_SOCKET_UTILS_H_ + +#include "platform-simulation.h" + +/** + * Represents a socket for communication with other simulation node. + * + * This is used for emulation of 15.4 radio or other interfaces. + * + */ +typedef struct utilsSocket +{ + bool mInitialized; ///< Whether or not initialized. + 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 +} utilsSocket; + +/** + * Adds a file descriptor (FD) to a given FD set. + * + * @param[in] aFd The FD to add. + * @param[in] aFdSet The FD set to add to. + * @param[in] aMaxFd A pointer to track maximum FD in @p aFdSet (can be NULL). + * + */ +void utilsAddFdToFdSet(int aFd, fd_set *aFdSet, int *aMaxFd); + +/** + * Initializes the socket. + * + * @param[in] aSocket The socket to initialize. + * @param[in] aPortBase The base port number value. Nodes will determine their port as `aPortBased + gNodeId`. + * + */ +void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase); + +/** + * De-initializes the socket. + * + * @param[in] aSocket The socket to de-initialize. + * + */ +void utilsDeinitSocket(utilsSocket *aSocket); + +/** + * Adds sockets RX FD to a given FD set. + * + * @param[in] aSocket The socket. + * @param[in] aFdSet The (read) FD set to add to. + * @param[in] aMaxFd A pointer to track maximum FD in @p aFdSet (can be NULL). + * + */ +void utilsAddSocketRxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd); + +/** + * Adds sockets TX FD to a given FD set. + * + * @param[in] aSocket The socket. + * @param[in] aFdSet The (write) FD set to add to. + * @param[in] aMaxFd A pointer to track maximum FD in @p aFdSet (can be NULL). + * + */ +void utilsAddSocketTxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd); + +/** + * Indicates whether the socket can receive. + * + * @param[in] aSocket The socket. + * @param[in] aReadFdSet The read FD set. + * + * @retval TRUE The socket RX FD is in @p aReadFdSet, and socket can receive. + * @retval FALSE The socket RX FD is not in @p aReadFdSet. Socket is not ready to receive. + * + */ +bool utilsCanSocketReceive(const utilsSocket *aSocket, const fd_set *aReadFdSet); + +/** + * Indicates whether the socket can send. + * + * @param[in] aSocket The socket. + * @param[in] aFdSet The write FD set. + * + * @retval TRUE The socket TX FD is in @p aWriteFdSet, and socket can send. + * @retval FALSE The socket TX FD is not in @p aWriteFdSet. Socket is not ready to send. + * + */ +bool utilsCanSocketSend(const utilsSocket *aSocket, const fd_set *aWriteFdSet); + +/** + * Receives data from socket. + * + * MUST be used when `utilsCanSocketReceive()` returns `TRUE. + * + * @param[in] aSocket The socket. + * @param[out] aBuffer The buffer to output the read content. + * @param[in] aBufferSize Maximum size of buffer in bytes. + * @param[out] aSenderNodeId A pointer to return the Node ID of the sender (derived from the port number). + * Can be NULL if not needed. + * + * @returns The number of received bytes written into @p aBuffer. + * + */ +uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket, + void *aBuffer, + uint16_t aBufferSize, + uint16_t *aSenderNodeId); + +/** + * Sends data over the socket. + * + * @param[in] aSocket The socket. + * @param[in] aBuffer The buffer containing the bytes to sent. + * @param[in] aBufferSize Size of data in @p buffer in bytes. + * + */ +void utilsSendOverSocket(const utilsSocket *aSocket, const void *aBuffer, uint16_t aBufferLength); + +#endif // PLATFORM_SIMULATION_SOCKET_UTILS_H_ diff --git a/examples/platforms/simulation/trel.c b/examples/platforms/simulation/trel.c index cfc34ac9b..696b1a59c 100644 --- a/examples/platforms/simulation/trel.c +++ b/examples/platforms/simulation/trel.c @@ -31,6 +31,7 @@ #include #include +#include "simul_utils.h" #include "utils/code_utils.h" #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE @@ -38,8 +39,6 @@ // Change DEBUG_LOG to all extra logging #define DEBUG_LOG 0 -// The IPv4 group for receiving -#define TREL_SIM_GROUP "224.0.0.116" #define TREL_SIM_PORT 9200 #define TREL_MAX_PACKET_SIZE 1800 @@ -67,11 +66,9 @@ typedef struct Message static uint8_t sNumPendingTx = 0; static Message sPendingTx[TREL_MAX_PENDING_TX]; -static int sTxFd = -1; -static int sRxFd = -1; -static uint16_t sPortOffset = 0; -static bool sEnabled = false; -static uint16_t sUdpPort; +static utilsSocket sSocket; +static uint16_t sPortOffset = 0; +static bool sEnabled = false; static bool sServiceRegistered = false; static uint16_t sServicePort; @@ -117,80 +114,6 @@ static const char *messageTypeToString(MessageType aType) } #endif -static void initFds(void) -{ - int fd; - int one = 1; - struct sockaddr_in sockaddr; - struct ip_mreqn mreq; - - memset(&sockaddr, 0, sizeof(sockaddr)); - - otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)")); - - sUdpPort = (uint16_t)(TREL_SIM_PORT + sPortOffset + gNodeId); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(sUdpPort); - sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); - - otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)) != -1, - perror("setsockopt(sTxFd, IP_MULTICAST_IF)")); - - otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)) != -1, - perror("setsockopt(sTxFd, IP_MULTICAST_LOOP)")); - - otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sTxFd)")); - - // Tx fd is successfully initialized. - sTxFd = fd; - - otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sRxFd)")); - - otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1, - perror("setsockopt(sRxFd, SO_REUSEADDR)")); - otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1, - perror("setsockopt(sRxFd, SO_REUSEPORT)")); - - memset(&mreq, 0, sizeof(mreq)); - inet_pton(AF_INET, TREL_SIM_GROUP, &mreq.imr_multiaddr); - - // Always use loopback device to send simulation packets. - mreq.imr_address.s_addr = inet_addr("127.0.0.1"); - - otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)) != -1, - perror("setsockopt(sRxFd, IP_MULTICAST_IF)")); - otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1, - perror("setsockopt(sRxFd, IP_ADD_MEMBERSHIP)")); - - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset)); - sockaddr.sin_addr.s_addr = inet_addr(TREL_SIM_GROUP); - - otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)")); - - // Rx fd is successfully initialized. - sRxFd = fd; - -exit: - if (sRxFd == -1 || sTxFd == -1) - { - exit(EXIT_FAILURE); - } -} - -static void deinitFds(void) -{ - if (sRxFd != -1) - { - close(sRxFd); - } - - if (sTxFd != -1) - { - close(sTxFd); - } -} - static uint16_t getMessageSize(const Message *aMessage) { return (uint16_t)(&aMessage->mData[aMessage->mDataLength] - (const uint8_t *)aMessage); @@ -198,31 +121,13 @@ static uint16_t getMessageSize(const Message *aMessage) static void sendPendingTxMessages(void) { - ssize_t rval; - struct sockaddr_in sockaddr; - - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - inet_pton(AF_INET, TREL_SIM_GROUP, &sockaddr.sin_addr); - - sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset)); - for (uint8_t i = 0; i < sNumPendingTx; i++) { - uint16_t size = getMessageSize(&sPendingTx[i]); - #if DEBUG_LOG fprintf(stderr, "\r\n[trel-sim] Sending message (num:%d, type:%s, port:%u)\r\n", i, messageTypeToString(sPendingTx[i].mType), sPendingTx[i].mSockAddr.mPort); #endif - - rval = sendto(sTxFd, &sPendingTx[i], size, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - - if (rval < 0) - { - perror("sendto(sTxFd)"); - exit(EXIT_FAILURE); - } + utilsSendOverSocket(&sSocket, &sPendingTx[i], getMessageSize(&sPendingTx[i])); } sNumPendingTx = 0; @@ -279,7 +184,7 @@ static void processMessage(otInstance *aInstance, Message *aMessage, uint16_t aL switch (aMessage->mType) { case TREL_DATA_MESSAGE: - otEXPECT(aMessage->mSockAddr.mPort == sUdpPort); + otEXPECT(aMessage->mSockAddr.mPort == sSocket.mPort); otPlatTrelHandleReceived(aInstance, aMessage->mData, aMessage->mDataLength); break; @@ -309,7 +214,7 @@ void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort) { OT_UNUSED_VARIABLE(aInstance); - *aUdpPort = sUdpPort; + *aUdpPort = sSocket.mPort; #if DEBUG_LOG fprintf(stderr, "\r\n[trel-sim] otPlatTrelEnable() *aUdpPort=%u\r\n", *aUdpPort); @@ -417,62 +322,46 @@ void platformTrelInit(uint32_t aSpeedUpFactor) sPortOffset *= (MAX_NETWORK_SIZE + 1); } - initFds(); + utilsInitSocket(&sSocket, TREL_SIM_PORT + sPortOffset); OT_UNUSED_VARIABLE(aSpeedUpFactor); } -void platformTrelDeinit(void) { deinitFds(); } +void platformTrelDeinit(void) { utilsDeinitSocket(&sSocket); } void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd) { OT_UNUSED_VARIABLE(aTimeout); // Always ready to receive - if (aReadFdSet != NULL) + utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd); + + if (sNumPendingTx > 0) { - FD_SET(sRxFd, aReadFdSet); - - if (aMaxFd != NULL && *aMaxFd < sRxFd) - { - *aMaxFd = sRxFd; - } - } - - if ((aWriteFdSet != NULL) && (sNumPendingTx > 0)) - { - FD_SET(sTxFd, aWriteFdSet); - - if (aMaxFd != NULL && *aMaxFd < sTxFd) - { - *aMaxFd = sTxFd; - } + utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd); } } void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet) { - if (FD_ISSET(sTxFd, aWriteFdSet) && (sNumPendingTx > 0)) + if ((sNumPendingTx > 0) && utilsCanSocketSend(&sSocket, aWriteFdSet)) { sendPendingTxMessages(); } - if (FD_ISSET(sRxFd, aReadFdSet)) + if (utilsCanSocketReceive(&sSocket, aReadFdSet)) { - Message message; - ssize_t rval; + Message message; + uint16_t len; message.mDataLength = 0; - rval = recvfrom(sRxFd, (char *)&message, sizeof(message), 0, NULL, NULL); + len = utilsReceiveFromSocket(&sSocket, &message, sizeof(message), NULL); - if (rval < 0) + if (len > 0) { - perror("recvfrom(sRxFd)"); - exit(EXIT_FAILURE); + processMessage(aInstance, &message, len); } - - processMessage(aInstance, &message, (uint16_t)(rval)); } } diff --git a/examples/platforms/simulation/uart.c b/examples/platforms/simulation/uart.c index fe3fcebe8..70d387118 100644 --- a/examples/platforms/simulation/uart.c +++ b/examples/platforms/simulation/uart.c @@ -40,6 +40,7 @@ #include +#include "simul_utils.h" #include "utils/code_utils.h" #include "utils/uart.h" @@ -172,34 +173,13 @@ exit: void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd) { - if (aReadFdSet != NULL) + utilsAddFdToFdSet(s_in_fd, aReadFdSet, aMaxFd); + utilsAddFdToFdSet(s_in_fd, aErrorFdSet, aMaxFd); + + if ((s_write_length > 0)) { - FD_SET(s_in_fd, aReadFdSet); - - if (aErrorFdSet != NULL) - { - FD_SET(s_in_fd, aErrorFdSet); - } - - if (aMaxFd != NULL && *aMaxFd < s_in_fd) - { - *aMaxFd = s_in_fd; - } - } - - if ((aWriteFdSet != NULL) && (s_write_length > 0)) - { - FD_SET(s_out_fd, aWriteFdSet); - - if (aErrorFdSet != NULL) - { - FD_SET(s_out_fd, aErrorFdSet); - } - - if (aMaxFd != NULL && *aMaxFd < s_out_fd) - { - *aMaxFd = s_out_fd; - } + utilsAddFdToFdSet(s_out_fd, aWriteFdSet, aMaxFd); + utilsAddFdToFdSet(s_out_fd, aErrorFdSet, aMaxFd); } }