mirror of
https://github.com/espressif/openthread.git
synced 2026-08-05 18:37:45 +00:00
[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.
This commit is contained in:
@@ -72,6 +72,7 @@ add_library(openthread-simulation
|
||||
misc.c
|
||||
multipan.c
|
||||
radio.c
|
||||
simul_utils.c
|
||||
spi-stubs.c
|
||||
system.c
|
||||
trel.c
|
||||
|
||||
@@ -41,14 +41,12 @@
|
||||
#include <openthread/platform/radio.h>
|
||||
#include <openthread/platform/time.h>
|
||||
|
||||
#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)
|
||||
|
||||
@@ -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 <errno.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"
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <openthread/random_noncrypto.h>
|
||||
#include <openthread/platform/trel.h>
|
||||
|
||||
#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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
#include <openthread/platform/debug_uart.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user