[posix-app] adding support for TREL over IPv6/UDP (#4440)

This commit implements TREL IPv6/UDP platform under POSIX App. The
implementation uses datagram sockets to exchange packets over a given
network interface (netif). The interface name can hard-coded in the
build using `OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME` config
or be specified as part of the input program arguments (using
`--trel-interface` or `-t` option).
This commit is contained in:
Abtin Keshavarzian
2020-12-10 11:46:07 -08:00
committed by Jonathan Hui
parent 2d36853fe6
commit 0829514edc
8 changed files with 654 additions and 1 deletions
+7 -1
View File
@@ -107,6 +107,7 @@ enum
OT_POSIX_OPT_HELP = 'h',
OT_POSIX_OPT_INTERFACE_NAME = 'I',
OT_POSIX_OPT_TIME_SPEED = 's',
OT_POSIX_OPT_TREL_INTERFACE = 't',
OT_POSIX_OPT_VERBOSE = 'v',
OT_POSIX_OPT_SHORT_MAX = 128,
@@ -124,6 +125,7 @@ static const struct option kOptions[] = {
{"radio-version", no_argument, NULL, OT_POSIX_OPT_RADIO_VERSION},
{"real-time-signal", required_argument, NULL, OT_POSIX_OPT_REAL_TIME_SIGNAL},
{"time-speed", required_argument, NULL, OT_POSIX_OPT_TIME_SPEED},
{"trel-interface", required_argument, NULL, OT_POSIX_OPT_TREL_INTERFACE},
{"verbose", no_argument, NULL, OT_POSIX_OPT_VERBOSE},
{0, 0, 0, 0}};
@@ -140,6 +142,7 @@ static void PrintUsage(const char *aProgramName, FILE *aStream, int aExitCode)
" -n --dry-run Just verify if arguments is valid and radio spinel is compatible.\n"
" --radio-version Print radio firmware version.\n"
" -s --time-speed factor Time speed up factor.\n"
" -t --trel-interface name Interface name for TREL platform (e.g., wlan0 netif).\n"
" -v --verbose Also log to stderr.\n",
aProgramName);
#ifdef __linux__
@@ -167,7 +170,7 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig)
while (true)
{
int index = 0;
int option = getopt_long(aArgCount, aArgVector, "B:d:hI:ns:v", kOptions, &index);
int option = getopt_long(aArgCount, aArgVector, "B:d:hI:t:ns:v", kOptions, &index);
if (option == -1)
{
@@ -188,6 +191,9 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig)
case OT_POSIX_OPT_BACKBONE_INTERFACE_NAME:
aConfig->mPlatformConfig.mBackboneInterfaceName = optarg;
break;
case OT_POSIX_OPT_TREL_INTERFACE:
aConfig->mPlatformConfig.mTrelInterface = optarg;
break;
case OT_POSIX_OPT_DRY_RUN:
aConfig->mIsDryRun = true;
break;
+1
View File
@@ -73,6 +73,7 @@ add_library(openthread-posix
settings.cpp
spi_interface.cpp
system.cpp
trel_udp6.cpp
uart.cpp
udp.cpp
virtual_time.cpp
+1
View File
@@ -57,6 +57,7 @@ libopenthread_posix_a_SOURCES = \
settings.cpp \
spi_interface.cpp \
system.cpp \
trel_udp6.cpp \
uart.cpp \
udp.cpp \
virtual_time.cpp \
@@ -76,6 +76,7 @@ typedef struct otPlatformConfig
const char *mRadioUrl; ///< Radio url.
int mRealTimeSignal; ///< The real-time signal for microsecond timer.
uint32_t mSpeedUpFactor; ///< Speed up factor.
const char *mTrelInterface; ///< Interface name used by TREL radio link (can be NULL to use default).
} otPlatformConfig;
/**
@@ -47,6 +47,16 @@
#define OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE 1
#endif
/**
* @def OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME
*
* Defines the default interface name used for TREL UDP6 platform.
*
*/
#ifndef OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME
#define OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME "trel"
#endif
/**
* @def OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME
*
+35
View File
@@ -389,6 +389,41 @@ enum SocketBlockOption
kSocketNonBlock,
};
/**
* This function initializes platform TREL UDP6 driver.
*
* @param[in] aInterfaceName The name of network interface.
*
*/
void platformTrelInit(const char *aInterfaceName);
/**
* This function shuts down the platform TREL UDP6 platform driver.
*
*/
void platformTrelDeinit(void);
/**
* This function updates the file descriptor sets with file descriptors used by the TREL driver.
*
* @param[inout] aReadFdSet A pointer to the read file descriptors.
* @param[inout] aWriteFdSet A pointer to the write file descriptors.
* @param[inout] aMaxFd A pointer to the max file descriptor.
* @param[inout] aTimeout A pointer to the timeout.
*
*/
void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout);
/**
* This function performs TREL driver processing.
*
* @param[in] aInstance A pointer to the OpenThread instance.
* @param[in] aReadFdSet A pointer to the read file descriptors.
* @param[in] aWriteFdSet A pointer to the write file descriptors.
*
*/
void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet);
/**
* This function creates a socket with SOCK_CLOEXEC flag set.
*
+12
View File
@@ -85,6 +85,9 @@ otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
VerifyOrDie(radioUrl.GetPath() != nullptr, OT_EXIT_INVALID_ARGUMENTS);
platformAlarmInit(aPlatformConfig->mSpeedUpFactor, aPlatformConfig->mRealTimeSignal);
platformRadioInit(&radioUrl);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelInit(aPlatformConfig->mTrelInterface);
#endif
platformRandomInit();
instance = otInstanceInitSingle();
@@ -115,6 +118,9 @@ void otSysDeinit(void)
platformRadioDeinit();
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
platformNetifDeinit();
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelDeinit();
#endif
IgnoreError(otPlatUartDisable());
}
@@ -173,6 +179,9 @@ void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop)
#else
platformRadioUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mMaxFd, &aMainloop->mTimeout);
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mMaxFd, &aMainloop->mTimeout);
#endif
if (otTaskletsArePending(aInstance))
{
@@ -230,6 +239,9 @@ void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMa
virtualTimeProcess(aInstance, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet);
#else
platformRadioProcess(aInstance, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet);
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelProcess(aInstance, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet);
#endif
platformUartProcess(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet);
platformAlarmProcess(aInstance);
+587
View File
@@ -0,0 +1,587 @@
/*
* Copyright (c) 2019, 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.
*/
/**
* @file
* This file implements platform for TREL using IPv6/UDP socket under POSIX.
*/
#include "openthread-posix-config.h"
#include "platform-posix.h"
#include <arpa/inet.h>
#include <assert.h>
#include <fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <openthread/platform/trel-udp6.h>
#include "common/code_utils.hpp"
#include "common/logging.hpp"
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
#define TREL_MAX_PACKET_SIZE 1400
#define TREL_PACKET_POOL_SIZE 5
#define USEC_PER_MSEC 1000u
#define TREL_SOCKET_BIND_MAX_WAIT_TIME_MSEC 4000u
typedef struct TxPacket
{
struct TxPacket *mNext;
uint8_t mBuffer[TREL_MAX_PACKET_SIZE];
uint16_t mLength;
otIp6Address mDestAddress;
} TxPacket;
static uint8_t sRxPacketBuffer[TREL_MAX_PACKET_SIZE];
static uint16_t sRxPacketLength;
static TxPacket sTxPacketPool[TREL_PACKET_POOL_SIZE];
static TxPacket * sFreeTxPacketHead; // A singly linked list of free/available `TxPacket` from pool.
static TxPacket * sTxPacketQueueTail; // A circular linked list for queued tx packets.
static char sInterfaceName[IFNAMSIZ + 1];
static int sInterfaceIndex = -1;
static int sMulticastSocket = -1;
static int sSocket = -1;
static uint16_t sUdpPort = 0;
static otIp6Address sInterfaceAddress;
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG) && OPENTHREAD_CONFIG_LOG_PLATFORM
static const char *Ip6AddrToString(const void *aAddress)
{
static char string[INET6_ADDRSTRLEN];
return inet_ntop(AF_INET6, aAddress, string, sizeof(string));
}
static const char *BufferToString(const uint8_t *aBuffer, uint16_t aLength)
{
const uint16_t kMaxWrite = 16;
static char string[1600];
uint16_t num = 0;
char * cur = &string[0];
char * end = &string[sizeof(string) - 1];
cur += snprintf(cur, end - cur, "[(len:%d) ", aLength);
VerifyOrExit(cur < end);
while (aLength-- && (num < kMaxWrite))
{
cur += snprintf(cur, end - cur, "%02x ", *aBuffer++);
VerifyOrExit(cur < end);
num++;
}
if (aLength != 0)
{
cur += snprintf(cur, end - cur, "... ");
VerifyOrExit(cur < end);
}
*cur++ = ']';
VerifyOrExit(cur < end);
*cur++ = '\0';
exit:
*end = '\0';
return string;
}
#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG) && OPENTHREAD_CONFIG_LOG_PLATFORM
static void AddUnicastAddress(const otIp6Address *aUnicastAddress)
{
int mgmtFd;
int ret;
struct in6_ifreq
{
struct in6_addr ifr6_addr;
uint32_t ifr6_prefixlen;
int ifr6_ifindex;
} ifr6;
otLogDebgPlat("[trel-plat] AddUnicastAddress(%s)", Ip6AddrToString(aUnicastAddress));
mgmtFd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
VerifyOrDie(mgmtFd >= 0, OT_EXIT_ERROR_ERRNO);
memcpy(&ifr6.ifr6_addr, aUnicastAddress, sizeof(otIp6Address));
ifr6.ifr6_prefixlen = 64;
ifr6.ifr6_ifindex = sInterfaceIndex;
ret = ioctl(mgmtFd, SIOCSIFADDR, &ifr6);
VerifyOrDie((ret == 0) || (errno == EALREADY) || (errno == EEXIST), OT_EXIT_ERROR_ERRNO);
close(mgmtFd);
}
static void RemoveUnicastAddress(const otIp6Address *aUnicastAddress)
{
int mgmtFd;
int ret;
struct in6_ifreq
{
struct in6_addr ifr6_addr;
uint32_t ifr6_prefixlen;
int ifr6_ifindex;
} ifr6;
otLogDebgPlat("[trel-plat] RemoveUnicastAddress(%s)", Ip6AddrToString(aUnicastAddress));
mgmtFd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
VerifyOrDie(mgmtFd >= 0, OT_EXIT_ERROR_ERRNO);
memcpy(&ifr6.ifr6_addr, aUnicastAddress, sizeof(otIp6Address));
ifr6.ifr6_prefixlen = 64;
ifr6.ifr6_ifindex = sInterfaceIndex;
ret = ioctl(mgmtFd, SIOCDIFADDR, &ifr6);
VerifyOrDie(ret == 0, OT_EXIT_ERROR_ERRNO);
close(mgmtFd);
}
static void PrepareSocket(void)
{
int val;
struct sockaddr_in6 sockAddr;
uint64_t startTime;
bool isSocketBound = false;
otLogDebgPlat("[trel-plat] PrepareSocket()");
sSocket = socket(AF_INET6, SOCK_DGRAM, 0);
VerifyOrDie(sSocket >= 0, OT_EXIT_ERROR_ERRNO);
// Set the multicast interface index (for tx), disable loop back
// of multicast tx and set the multicast hop limit to 1 to reach
// a single sub-net.
val = sInterfaceIndex;
VerifyOrDie(setsockopt(sSocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) == 0, OT_EXIT_ERROR_ERRNO);
val = 0;
VerifyOrDie(setsockopt(sSocket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) == 0, OT_EXIT_ERROR_ERRNO);
val = 1;
VerifyOrDie(setsockopt(sSocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) == 0, OT_EXIT_ERROR_ERRNO);
VerifyOrDie(setsockopt(sSocket, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) == 0, OT_EXIT_ERROR_ERRNO);
val = 1;
VerifyOrDie(setsockopt(sSocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) == 0, OT_EXIT_ERROR_ERRNO);
// Make the socket non-blocking to allow immediate tx attempt.
val = fcntl(sSocket, F_GETFL, 0);
VerifyOrDie(val != -1, OT_EXIT_ERROR_ERRNO);
val = val | O_NONBLOCK;
VerifyOrDie(fcntl(sSocket, F_SETFL, val) == 0, OT_EXIT_ERROR_ERRNO);
// Bind the socket. The address to which we want to bind the
// socket, is itself added earlier above. The address therefore
// may not be immediately available/ready on the interface and the
// socket `bind()` call may fail with `EADDRNOTAVAIL` error. In
// such a case, we keep trying up to a maximum wait time.
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin6_family = AF_INET6;
sockAddr.sin6_port = htons(sUdpPort);
memcpy(&sockAddr.sin6_addr, &sInterfaceAddress, sizeof(otIp6Address));
sockAddr.sin6_scope_id = (uint32_t)sInterfaceIndex;
startTime = otPlatTimeGet();
while (otPlatTimeGet() - startTime < TREL_SOCKET_BIND_MAX_WAIT_TIME_MSEC * USEC_PER_MSEC)
{
if (bind(sSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) == -1)
{
VerifyOrDie(errno == EADDRNOTAVAIL, OT_EXIT_ERROR_ERRNO);
}
else
{
isSocketBound = true;
break;
}
}
VerifyOrDie(isSocketBound, OT_EXIT_ERROR_ERRNO);
}
static otError SendPacket(const uint8_t *aBuffer, uint16_t aLength, const otIp6Address *aDestAddress)
{
otError error = OT_ERROR_NONE;
struct sockaddr_in6 sockAddr;
ssize_t ret;
VerifyOrExit(sSocket >= 0, error = OT_ERROR_INVALID_STATE);
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin6_family = AF_INET6;
sockAddr.sin6_port = htons(sUdpPort);
memcpy(&sockAddr.sin6_addr, aDestAddress, sizeof(otIp6Address));
ret = sendto(sSocket, aBuffer, aLength, 0, (struct sockaddr *)&sockAddr, sizeof(sockAddr));
if (ret != aLength)
{
otLogDebgPlat("[trel-plat] SendPacket() -- sendto() failed errno %d", errno);
switch (errno)
{
case ENETUNREACH:
case ENETDOWN:
case EHOSTUNREACH:
error = OT_ERROR_ABORT;
break;
default:
error = OT_ERROR_INVALID_STATE;
}
}
exit:
otLogDebgPlat("[trel-plat] SendPacket(%s) err:%s pkt:%s", Ip6AddrToString(aDestAddress),
otThreadErrorToString(error), BufferToString(aBuffer, aLength));
return error;
}
static void ReceivePacket(int aSocket, otInstance *aInstance)
{
struct sockaddr_in6 sockAddr;
socklen_t sockAddrLen = sizeof(sockAddr);
ssize_t ret;
memset(&sockAddr, 0, sizeof(sockAddr));
ret = recvfrom(aSocket, (char *)sRxPacketBuffer, sizeof(sRxPacketBuffer), 0, (struct sockaddr *)&sockAddr,
&sockAddrLen);
VerifyOrDie(ret >= 0, OT_EXIT_ERROR_ERRNO);
sRxPacketLength = (uint16_t)(ret);
if (sRxPacketLength > sizeof(sRxPacketBuffer))
{
sRxPacketLength = sizeof(sRxPacketLength);
}
otLogDebgPlat("[trel-plat] ReceivePacket() - received from %s port:%d, id:%d, pkt:%s",
Ip6AddrToString(&sockAddr.sin6_addr), ntohs(sockAddr.sin6_port), sockAddr.sin6_scope_id,
BufferToString(sRxPacketBuffer, sRxPacketLength));
otPlatTrelUdp6HandleReceived(aInstance, sRxPacketBuffer, sRxPacketLength);
}
static void InitPacketQueue(void)
{
sTxPacketQueueTail = NULL;
// Chain all the packets in pool in the free linked list.
sFreeTxPacketHead = NULL;
for (uint16_t index = 0; index < OT_ARRAY_LENGTH(sTxPacketPool); index++)
{
TxPacket *packet = &sTxPacketPool[index];
packet->mNext = sFreeTxPacketHead;
sFreeTxPacketHead = packet;
}
}
static void SendQueuedPackets(void)
{
while (sTxPacketQueueTail != NULL)
{
TxPacket *packet = sTxPacketQueueTail->mNext; // tail->mNext is the head of the list.
if (SendPacket(packet->mBuffer, packet->mLength, &packet->mDestAddress) == OT_ERROR_INVALID_STATE)
{
otLogDebgPlat("[trel-plat] SendQueuedPackets() - SendPacket() would block");
break;
}
// Remove the `packet` from the packet queue (circular
// linked list).
if (packet == sTxPacketQueueTail)
{
sTxPacketQueueTail = NULL;
}
else
{
sTxPacketQueueTail->mNext = packet->mNext;
}
// Add the `packet` to the free packet singly linked list.
packet->mNext = sFreeTxPacketHead;
sFreeTxPacketHead = packet;
}
}
static otError EnqueuePacket(const uint8_t *aBuffer, uint16_t aLength, const otIp6Address *aDestAddress)
{
otError error = OT_ERROR_NONE;
TxPacket *packet;
// Allocate an available packet entry (from the free packet list)
// and copy the packet content into it.
VerifyOrExit(sFreeTxPacketHead != NULL, error = OT_ERROR_NO_BUFS);
packet = sFreeTxPacketHead;
sFreeTxPacketHead = sFreeTxPacketHead->mNext;
memcpy(packet->mBuffer, aBuffer, aLength);
packet->mLength = aLength;
packet->mDestAddress = *aDestAddress;
// Add packet to the tail of TxPacketQueue circular linked-list.
if (sTxPacketQueueTail == NULL)
{
packet->mNext = packet;
sTxPacketQueueTail = packet;
}
else
{
packet->mNext = sTxPacketQueueTail->mNext;
sTxPacketQueueTail->mNext = packet;
sTxPacketQueueTail = packet;
}
otLogDebgPlat("[trel-plat] EnqueuePacket(%s) - %s", Ip6AddrToString(aDestAddress),
BufferToString(aBuffer, aLength));
exit:
return error;
}
//---------------------------------------------------------------------------------------------------------------------
// otPlatTrelUdp6
void otPlatTrelUdp6Init(otInstance *aInstance, const otIp6Address *aUnicastAddress, uint16_t aUdpPort)
{
int val;
struct sockaddr_in6 sockAddr;
otLogDebgPlat("[trel-plat] otPlatTrelUdp6Init(%s, port:%d)", Ip6AddrToString(aUnicastAddress), aUdpPort);
sUdpPort = aUdpPort;
sInterfaceAddress = *aUnicastAddress;
sInterfaceIndex = (int)if_nametoindex(sInterfaceName);
VerifyOrDie(sInterfaceIndex > 0, OT_EXIT_ERROR_ERRNO);
AddUnicastAddress(aUnicastAddress);
sMulticastSocket = socket(AF_INET6, SOCK_DGRAM, 0);
VerifyOrDie(sMulticastSocket >= 0, OT_EXIT_ERROR_ERRNO);
val = 1;
VerifyOrDie(setsockopt(sMulticastSocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) == 0, OT_EXIT_ERROR_ERRNO);
VerifyOrDie(setsockopt(sMulticastSocket, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) == 0, OT_EXIT_ERROR_ERRNO);
// To receive from multicast addresses, the socket need to be
// bound to `in6addr_any` address.
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin6_family = AF_INET6;
sockAddr.sin6_port = htons(sUdpPort);
sockAddr.sin6_addr = in6addr_any;
sockAddr.sin6_scope_id = (uint32_t)sInterfaceIndex;
VerifyOrDie(bind(sMulticastSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) != -1, OT_EXIT_ERROR_ERRNO);
PrepareSocket();
OT_UNUSED_VARIABLE(aInstance);
}
void otPlatTrelUdp6UpdateAddress(otInstance *aInstance, const otIp6Address *aUnicastAddress)
{
assert(sSocket >= 0);
otLogDebgPlat("[trel-plat] otPlatTrelUdp6UpdateAddress(%s)", Ip6AddrToString(aUnicastAddress));
VerifyOrExit(memcmp(aUnicastAddress, &sInterfaceAddress, sizeof(otIp6Address)) != 0);
close(sSocket);
RemoveUnicastAddress(&sInterfaceAddress);
sInterfaceAddress = *aUnicastAddress;
AddUnicastAddress(aUnicastAddress);
PrepareSocket();
exit:
OT_UNUSED_VARIABLE(aInstance);
}
void otPlatTrelUdp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aMulticastAddress)
{
struct ipv6_mreq mr;
OT_UNUSED_VARIABLE(aInstance);
assert(sMulticastSocket != -1);
memcpy(&mr.ipv6mr_multiaddr, aMulticastAddress, sizeof(otIp6Address));
mr.ipv6mr_interface = (unsigned int)sInterfaceIndex;
VerifyOrDie(setsockopt(sMulticastSocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr, sizeof(mr)) == 0, OT_EXIT_ERROR_ERRNO);
otLogDebgPlat("[trel-plat] otPlatTrelUdp6SubscribeMulticastAddress(%s)", Ip6AddrToString(aMulticastAddress));
}
otError otPlatTrelUdp6SendTo(otInstance * aInstance,
const uint8_t * aBuffer,
uint16_t aLength,
const otIp6Address *aDestAddress)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NONE;
assert(aLength <= TREL_MAX_PACKET_SIZE);
otLogDebgPlat("[trel-plat] otPlatTrelUdp6SendTo(%s) %s", Ip6AddrToString(aDestAddress),
BufferToString(aBuffer, aLength));
// We try to send the packet immediately. If it fails (e.g.,
// network is down) `SendPacket()` returns `OT_ERROR_ABORT`. If
// the send operation would block (e.g., socket is not yet ready
// or is out of buffer) we get `OT_ERROR_INVALID_STATE`. In that
// case we enqueue the packet to send it later when socket becomes
// ready.
error = SendPacket(aBuffer, aLength, aDestAddress);
if (error == OT_ERROR_INVALID_STATE)
{
error = EnqueuePacket(aBuffer, aLength, aDestAddress);
if (error != OT_ERROR_NONE)
{
error = OT_ERROR_ABORT;
}
}
return error;
}
//---------------------------------------------------------------------------------------------------------------------
// platformTrel system
void platformTrelInit(const char *aInterfaceName)
{
if (aInterfaceName != NULL)
{
strncpy(sInterfaceName, aInterfaceName, sizeof(sInterfaceName));
}
else
{
strncpy(sInterfaceName, OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME, sizeof(sInterfaceName));
}
sInterfaceName[sizeof(sInterfaceName) - 1] = 0;
otLogDebgPlat("[trel-plat] platformTrelInit(InterfaceName:\"%s\")", sInterfaceName);
InitPacketQueue();
}
void platformTrelDeinit(void)
{
if (sSocket != -1)
{
close(sSocket);
}
if (sMulticastSocket != -1)
{
close(sMulticastSocket);
}
otLogDebgPlat("[trel-plat] platformTrelDeinit()");
}
void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout)
{
assert((aReadFdSet != NULL) && (aWriteFdSet != NULL) && (aMaxFd != NULL) && (aTimeout != NULL));
VerifyOrExit((sSocket >= 0) && (sMulticastSocket >= 0));
FD_SET(sMulticastSocket, aReadFdSet);
FD_SET(sSocket, aReadFdSet);
if (sTxPacketQueueTail != NULL)
{
FD_SET(sSocket, aWriteFdSet);
}
if (*aMaxFd < sMulticastSocket)
{
*aMaxFd = sMulticastSocket;
}
if (*aMaxFd < sSocket)
{
*aMaxFd = sSocket;
}
exit:
OT_UNUSED_VARIABLE(aTimeout);
return;
}
void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
{
VerifyOrExit((sSocket >= 0) && (sMulticastSocket >= 0));
if (FD_ISSET(sSocket, aWriteFdSet))
{
SendQueuedPackets();
}
if (FD_ISSET(sSocket, aReadFdSet))
{
ReceivePacket(sSocket, aInstance);
}
if (FD_ISSET(sMulticastSocket, aReadFdSet))
{
ReceivePacket(sMulticastSocket, aInstance);
}
exit:
return;
}
#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE