From 69ad96675f010869bbff79d822eac962dd3941dd Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 20 Oct 2021 17:54:25 -0700 Subject: [PATCH] [trel] implement new TREL model using DNS-SD (#7125) This commit implements the new TREL model which uses DNS-SD to discover TREL peers on the network. This implementation replaces the previous model which relied on link-local multicast. This commit adds a new set of `otPlatTrel` APIs and callbacks that are then used by an updated `Trel::Interface` implementation. The `Trel::Interface` maintains a TREL peer table which is populated from DNS-SD discovered services. A device supporting TREL registers a new service to be advertised using DNS-SD with the service name `_trel._udp`. It also initiates an ongoing DNS-SD browse for the same service name within the local browsing domain to discover other devices supporting TREL. `Trel::Interface` encapsulates and send unicast frames as a unicast UDP message between TREL peers. Broadcast frames are sent as a group of UDP unicast transmission to a sub-set of TREL peers. This commit also adds a new set of public APIs for TREL along with support for them in CLI: - APIs for enabling/disabling of TREL operation at run-time. - Filter mode API which when enabled temporarily drops all the traffic on the TREL interface (is mainly intended for testing). - APIs to get the TREL peer table entries. This commit adds an implementation of the new `otPlatTrel` APIs under `simulation` platform. This is used for testing. This implementation emulates a simplified version of DNS-SD mechanism. A basic implementation of the `otPlatTrel` is also provided under `posix` platform. However certain functions are tied to mDNS or DNS-SD library being used on a device and need to be implemented per project/platform. A set of weak empty functions `trelDnssd{}` are defined (along with a description of the their expected behavior) which can be overridden during project/platform integration. --- Android.mk | 3 +- doc/ot_api_doc.h | 1 + examples/platforms/simulation/trel.c | 350 ++++--- include/Makefile.am | 3 +- include/openthread/BUILD.gn | 3 +- include/openthread/instance.h | 2 +- include/openthread/platform/trel-udp6.h | 155 --- include/openthread/platform/trel.h | 209 ++++ include/openthread/trel.h | 168 ++++ src/cli/README.md | 73 +- src/cli/cli.cpp | 88 +- src/core/BUILD.gn | 1 + src/core/CMakeLists.txt | 1 + src/core/Makefile.am | 1 + src/core/api/trel_api.cpp | 81 ++ src/core/common/notifier.cpp | 3 + src/core/radio/trel_interface.cpp | 397 +++++++- src/core/radio/trel_interface.hpp | 204 +++- src/core/radio/trel_link.cpp | 41 +- src/core/radio/trel_link.hpp | 3 + src/core/radio/trel_packet.cpp | 10 +- src/core/radio/trel_packet.hpp | 29 +- src/ncp/ncp_base.cpp | 3 - src/ncp/ncp_base.hpp | 4 - src/ncp/ncp_base_mtd.cpp | 14 +- src/posix/platform/CMakeLists.txt | 2 +- src/posix/platform/Makefile.am | 2 +- src/posix/platform/openthread-posix-config.h | 40 +- src/posix/platform/platform-posix.h | 4 +- src/posix/platform/trel.cpp | 559 +++++++++++ src/posix/platform/trel_udp6.cpp | 935 ------------------ .../openthread-core-toranj-config-posix.h | 23 - tests/unit/test_platform.cpp | 16 +- 33 files changed, 2019 insertions(+), 1409 deletions(-) delete mode 100644 include/openthread/platform/trel-udp6.h create mode 100644 include/openthread/platform/trel.h create mode 100644 include/openthread/trel.h create mode 100644 src/core/api/trel_api.cpp create mode 100644 src/posix/platform/trel.cpp delete mode 100644 src/posix/platform/trel_udp6.cpp diff --git a/Android.mk b/Android.mk index c484408ff..dba15249f 100644 --- a/Android.mk +++ b/Android.mk @@ -205,6 +205,7 @@ LOCAL_SRC_FILES := \ src/core/api/tcp_api.cpp \ src/core/api/thread_api.cpp \ src/core/api/thread_ftd_api.cpp \ + src/core/api/trel_api.cpp \ src/core/api/udp_api.cpp \ src/core/backbone_router/backbone_tmf.cpp \ src/core/backbone_router/bbr_leader.cpp \ @@ -383,7 +384,7 @@ LOCAL_SRC_FILES := \ src/posix/platform/settings.cpp \ src/posix/platform/spi_interface.cpp \ src/posix/platform/system.cpp \ - src/posix/platform/trel_udp6.cpp \ + src/posix/platform/trel.cpp \ src/posix/platform/udp.cpp \ src/posix/platform/utils.cpp \ third_party/mbedtls/repo/library/aes.c \ diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index c826e68e2..e67489c71 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -84,6 +84,7 @@ * @defgroup api-message Message * * @defgroup api-multi-radio Multi Radio Link + * @defgroup api-trel TREL * * @defgroup api-thread Thread * diff --git a/examples/platforms/simulation/trel.c b/examples/platforms/simulation/trel.c index 8e5396235..20c6b3057 100644 --- a/examples/platforms/simulation/trel.c +++ b/examples/platforms/simulation/trel.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, The OpenThread Authors. + * Copyright (c) 2019-21, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ #include "platform-simulation.h" #include -#include +#include #include "utils/code_utils.h" @@ -44,38 +44,39 @@ #define TREL_MAX_PACKET_SIZE 1800 -#define TREL_MAX_PENDING_TX 5 +#define TREL_MAX_PENDING_TX 64 -OT_TOOL_PACKED_BEGIN -struct PacketHeader +#define TREL_MAX_SERVICE_TXT_DATA_LEN 128 + +typedef enum MessageType { - otIp6Address mSrcIp6Address; - otIp6Address mDestIp6Address; - uint16_t mPort; -} OT_TOOL_PACKED_END; + TREL_DATA_MESSAGE, + TREL_DNSSD_BROWSE_MESSAGE, + TREL_DNSSD_ADD_SERVICE_MESSAGE, + TREL_DNSSD_REMOVE_SERVICE_MESSAGE, +} MessageType; -typedef struct PacketHeader PacketHeader; - -OT_TOOL_PACKED_BEGIN -struct Packet +typedef struct Message { - PacketHeader mHeader; - uint8_t mPayload[TREL_MAX_PACKET_SIZE]; - uint16_t mLength; // Total packet length including the header. -} OT_TOOL_PACKED_END; - -typedef struct Packet Packet; + MessageType mType; + otSockAddr mSockAddr; // Destination (when TREL_DATA_MESSAGE), or peer addr (when DNS-SD service) + uint16_t mDataLength; // mData length + uint8_t mData[TREL_MAX_PACKET_SIZE]; // TREL UDP packet (when TREL_DATA_MESSAGE), or service TXT data. +} Message; static uint8_t sNumPendingTx = 0; -static Packet sPendingTx[TREL_MAX_PENDING_TX]; +static Message sPendingTx[TREL_MAX_PENDING_TX]; -static int sTxFd = -1; -static int sRxFd = -1; -static uint16_t sPortOffset = 0; -static bool sEnabled = true; -static otIp6Address sUnicastAddress; -static otIp6Address sMulticastAddress; -static uint16_t sUdpPort; +static int sTxFd = -1; +static int sRxFd = -1; +static uint16_t sPortOffset = 0; +static bool sEnabled = false; +static uint16_t sUdpPort; + +static bool sServiceRegistered = false; +static uint16_t sServicePort; +static uint8_t sServiceTxtLength; +static char sServiceTxtData[TREL_MAX_SERVICE_TXT_DATA_LEN]; #if DEBUG_LOG static void dumpBuffer(const void *aBuffer, uint16_t aLength) @@ -91,19 +92,30 @@ static void dumpBuffer(const void *aBuffer, uint16_t aLength) fprintf(stderr, "]"); } -static const char *ip6AddrToString(const void *aAddress) +static const char *messageTypeToString(MessageType aType) { - static char string[INET6_ADDRSTRLEN]; + const char *str = "unknown"; - return inet_ntop(AF_INET6, aAddress, string, sizeof(string)); + switch (aType) + { + case TREL_DATA_MESSAGE: + str = "data"; + break; + case TREL_DNSSD_BROWSE_MESSAGE: + str = "browse"; + break; + case TREL_DNSSD_ADD_SERVICE_MESSAGE: + str = "add-service"; + break; + case TREL_DNSSD_REMOVE_SERVICE_MESSAGE: + str = "remove-service"; + break; + } + + return str; } #endif -static bool ip6AddrsAreEqual(const otIp6Address *aFirst, const otIp6Address *aSecond) -{ - return (memcmp(aFirst, aSecond, sizeof(otIp6Address)) == 0); -} - static void initFds(void) { int fd; @@ -115,8 +127,9 @@ static void initFds(void) 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((uint16_t)(TREL_SIM_PORT + sPortOffset + gNodeId)); + 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, @@ -177,7 +190,12 @@ static void deinitFds(void) } } -static void sendPendingTxPackets(void) +static uint16_t getMessageSize(const Message *aMessage) +{ + return (uint16_t)(&aMessage->mData[aMessage->mDataLength] - (const uint8_t *)aMessage); +} + +static void sendPendingTxMessages(void) { ssize_t rval; struct sockaddr_in sockaddr; @@ -190,13 +208,14 @@ static void sendPendingTxPackets(void) for (uint8_t i = 0; i < sNumPendingTx; i++) { + uint16_t size = getMessageSize(&sPendingTx[i]); + #if DEBUG_LOG - fprintf(stderr, "\n[trel-udp] Sending packet (num:%d)", i); - dumpBuffer(&sPendingTx[i], sPendingTx[i].mLength); - fprintf(stderr, "\n"); + 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], sPendingTx[i].mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); + rval = sendto(sTxFd, &sPendingTx[i], size, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (rval < 0) { @@ -208,83 +227,167 @@ static void sendPendingTxPackets(void) sNumPendingTx = 0; } +static void sendBrowseMessage(void) +{ + Message *message; + + assert(sNumPendingTx < TREL_MAX_PENDING_TX); + message = &sPendingTx[sNumPendingTx++]; + + message->mType = TREL_DNSSD_BROWSE_MESSAGE; + message->mDataLength = 0; + +#if DEBUG_LOG + fprintf(stderr, "\r\n[trel-sim] sendBrowseMessage()\r\n"); +#endif +} + +static void sendServiceMessage(MessageType aType) +{ + Message *message; + + assert((aType == TREL_DNSSD_ADD_SERVICE_MESSAGE) || (aType == TREL_DNSSD_REMOVE_SERVICE_MESSAGE)); + + assert(sNumPendingTx < TREL_MAX_PENDING_TX); + message = &sPendingTx[sNumPendingTx++]; + + message->mType = aType; + memset(&message->mSockAddr, 0, sizeof(otSockAddr)); + message->mSockAddr.mPort = sServicePort; + message->mDataLength = sServiceTxtLength; + memcpy(message->mData, sServiceTxtData, sServiceTxtLength); + +#if DEBUG_LOG + fprintf(stderr, "\r\n[trel-sim] sendServiceMessage(%s): service-port:%u, txt-len:%u\r\n", + aType == TREL_DNSSD_ADD_SERVICE_MESSAGE ? "add" : "remove", sServicePort, sServiceTxtLength); +#endif +} + +static void processMessage(otInstance *aInstance, Message *aMessage, uint16_t aLength) +{ + otPlatTrelPeerInfo peerInfo; + +#if DEBUG_LOG + fprintf(stderr, "\r\n[trel-sim] processMessage(len:%u, type:%s, port:%u)\r\n", aLength, + messageTypeToString(aMessage->mType), aMessage->mSockAddr.mPort); +#endif + + otEXPECT(aLength > 0); + otEXPECT(getMessageSize(aMessage) == aLength); + + switch (aMessage->mType) + { + case TREL_DATA_MESSAGE: + otEXPECT(aMessage->mSockAddr.mPort == sUdpPort); + otPlatTrelHandleReceived(aInstance, aMessage->mData, aMessage->mDataLength); + break; + + case TREL_DNSSD_BROWSE_MESSAGE: + sendServiceMessage(TREL_DNSSD_ADD_SERVICE_MESSAGE); + break; + + case TREL_DNSSD_ADD_SERVICE_MESSAGE: + case TREL_DNSSD_REMOVE_SERVICE_MESSAGE: + memset(&peerInfo, 0, sizeof(peerInfo)); + peerInfo.mRemoved = (aMessage->mType == TREL_DNSSD_REMOVE_SERVICE_MESSAGE); + peerInfo.mTxtData = aMessage->mData; + peerInfo.mTxtLength = (uint8_t)(aMessage->mDataLength); + peerInfo.mSockAddr = aMessage->mSockAddr; + otPlatTrelHandleDiscoveredPeerInfo(aInstance, &peerInfo); + break; + } + +exit: + return; +} + //--------------------------------------------------------------------------------------------------------------------- // otPlatTrel -void otPlatTrelUdp6Init(otInstance *aInstance, const otIp6Address *aUnicastAddress, uint16_t aUdpPort) +void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort) { OT_UNUSED_VARIABLE(aInstance); - sUnicastAddress = *aUnicastAddress; - sUdpPort = aUdpPort; + *aUdpPort = sUdpPort; #if DEBUG_LOG - fprintf(stderr, "\n[trel-udp6] otPlatTrelUdp6Init(aUnicastAddress:%s, aUdpPort:%d)\n", - ip6AddrToString(aUnicastAddress), aUdpPort); + fprintf(stderr, "\r\n[trel-sim] otPlatTrelEnable() *aUdpPort=%u\r\n", *aUdpPort); #endif -} - -void otPlatTrelUdp6UpdateAddress(otInstance *aInstance, const otIp6Address *aUnicastAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - sUnicastAddress = *aUnicastAddress; - -#if DEBUG_LOG - fprintf(stderr, "\n[trel-udp6] otPlatTrelUdp6UpdateAddress(aUnicastAddress:%s)\n", - ip6AddrToString(aUnicastAddress)); -#endif -} - -void otPlatTrelUdp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aMulticastAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - sMulticastAddress = *aMulticastAddress; - -#if DEBUG_LOG - fprintf(stderr, "\n[trel-udp6] otPlatTrelUdp6SubscribeMulticastAddress(aMulticastAddress:%s)\n", - ip6AddrToString(aMulticastAddress)); -#endif -} - -otError otPlatTrelUdp6SendTo(otInstance * aInstance, - const uint8_t * aBuffer, - uint16_t aLength, - const otIp6Address *aDestAddress) -{ - otError error = OT_ERROR_NONE; - Packet *packet; - - OT_UNUSED_VARIABLE(aInstance); - - otEXPECT(sEnabled); - otEXPECT_ACTION(sNumPendingTx < TREL_MAX_PENDING_TX, error = OT_ERROR_ABORT); - otEXPECT_ACTION(aLength <= TREL_MAX_PACKET_SIZE, error = OT_ERROR_ABORT); - - packet = &sPendingTx[sNumPendingTx++]; - packet->mHeader.mSrcIp6Address = sUnicastAddress; - packet->mHeader.mDestIp6Address = *aDestAddress; - packet->mHeader.mPort = sUdpPort; - packet->mLength = aLength + sizeof(PacketHeader); - memcpy(packet->mPayload, aBuffer, aLength); - -exit: - return error; -} - -otError otPlatTrelUdp6SetTestMode(otInstance *aInstance, bool aEnable) -{ - OT_UNUSED_VARIABLE(aInstance); - - sEnabled = aEnable; if (!sEnabled) { - sNumPendingTx = 0; + sEnabled = true; + sendBrowseMessage(); + } +} + +void otPlatTrelDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + +#if DEBUG_LOG + fprintf(stderr, "\r\n[trel-sim] otPlatTrelDisable()\r\n"); +#endif + + if (sEnabled) + { + sEnabled = false; + + if (sServiceRegistered) + { + sendServiceMessage(TREL_DNSSD_REMOVE_SERVICE_MESSAGE); + sServiceRegistered = false; + } + } +} + +void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength) +{ + OT_UNUSED_VARIABLE(aInstance); + + assert(aTxtLength <= TREL_MAX_SERVICE_TXT_DATA_LEN); + + if (sServiceRegistered) + { + sendServiceMessage(TREL_DNSSD_REMOVE_SERVICE_MESSAGE); } - return OT_ERROR_NONE; + sServiceRegistered = true; + sServicePort = aPort; + sServiceTxtLength = aTxtLength; + memcpy(sServiceTxtData, aTxtData, aTxtLength); + + sendServiceMessage(TREL_DNSSD_ADD_SERVICE_MESSAGE); + +#if DEBUG_LOG + fprintf(stderr, "\r\n[trel-sim] otPlatTrelRegisterService(aPort:%d, aTxtData:", aPort); + dumpBuffer(aTxtData, aTxtLength); + fprintf(stderr, ")\r\n"); +#endif +} + +void otPlatTrelSend(otInstance * aInstance, + const uint8_t * aUdpPayload, + uint16_t aUdpPayloadLen, + const otSockAddr *aDestSockAddr) +{ + OT_UNUSED_VARIABLE(aInstance); + + Message *message; + + assert(sNumPendingTx < TREL_MAX_PENDING_TX); + assert(aUdpPayloadLen <= TREL_MAX_PACKET_SIZE); + + message = &sPendingTx[sNumPendingTx++]; + + message->mType = TREL_DATA_MESSAGE; + message->mSockAddr = *aDestSockAddr; + message->mDataLength = aUdpPayloadLen; + memcpy(message->mData, aUdpPayload, aUdpPayloadLen); + +#if DEBUG_LOG + fprintf(stderr, "\r\n[trel-sim] otPlatTrelSend(len:%u, port:%u)\r\n", aUdpPayloadLen, aDestSockAddr->mPort); +#endif } //--------------------------------------------------------------------------------------------------------------------- @@ -304,7 +407,7 @@ void platformTrelInit(uint32_t aSpeedUpFactor) if (*endptr != '\0') { - fprintf(stderr, "\nInvalid PORT_OFFSET: %s\n", str); + fprintf(stderr, "\r\nInvalid PORT_OFFSET: %s\r\n", str); exit(EXIT_FAILURE); } @@ -351,15 +454,17 @@ void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const { if (FD_ISSET(sTxFd, aWriteFdSet) && (sNumPendingTx > 0)) { - sendPendingTxPackets(); + sendPendingTxMessages(); } if (FD_ISSET(sRxFd, aReadFdSet)) { - Packet rxPacket; + Message message; ssize_t rval; - rval = recvfrom(sRxFd, (char *)&rxPacket, sizeof(rxPacket) - sizeof(rxPacket.mLength), 0, NULL, NULL); + message.mDataLength = 0; + + rval = recvfrom(sRxFd, (char *)&message, sizeof(message), 0, NULL, NULL); if (rval < 0) { @@ -367,27 +472,14 @@ void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const exit(EXIT_FAILURE); } - rxPacket.mLength = (uint16_t)(rval); - -#if DEBUG_LOG - fprintf(stderr, "\n[trel-udp6] recvdPacket()"); - fprintf(stderr, " src:%s", ip6AddrToString(&rxPacket.mHeader.mSrcIp6Address)); - fprintf(stderr, " dst:%s", ip6AddrToString(&rxPacket.mHeader.mDestIp6Address)); - fprintf(stderr, " port:%u ", rxPacket.mHeader.mPort); - dumpBuffer(&rxPacket, rxPacket.mLength); - fprintf(stderr, "\n"); -#endif - if (sEnabled && (rxPacket.mHeader.mPort == sUdpPort) && - (ip6AddrsAreEqual(&rxPacket.mHeader.mDestIp6Address, &sUnicastAddress) || - ip6AddrsAreEqual(&rxPacket.mHeader.mDestIp6Address, &sMulticastAddress))) - { - otPlatTrelUdp6HandleReceived(aInstance, rxPacket.mPayload, rxPacket.mLength - sizeof(PacketHeader)); - } + processMessage(aInstance, &message, (uint16_t)(rval)); } } +//--------------------------------------------------------------------------------------------------------------------- + // This is added for RCP build to be built ok -OT_TOOL_WEAK void otPlatTrelUdp6HandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength) +OT_TOOL_WEAK void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength) { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aBuffer); @@ -396,4 +488,12 @@ OT_TOOL_WEAK void otPlatTrelUdp6HandleReceived(otInstance *aInstance, uint8_t *a assert(false); } +OT_TOOL_WEAK void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aInfo); + + assert(false); +} + #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/include/Makefile.am b/include/Makefile.am index 426e17c2a..6729d6817 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -86,6 +86,7 @@ openthread_headers = \ openthread/tcp.h \ openthread/thread.h \ openthread/thread_ftd.h \ + openthread/trel.h \ openthread/udp.h \ $(NULL) @@ -112,7 +113,7 @@ ot_platform_headers = \ openthread/platform/spi-slave.h \ openthread/platform/time.h \ openthread/platform/toolchain.h \ - openthread/platform/trel-udp6.h \ + openthread/platform/trel.h \ openthread/platform/udp.h \ $(NULL) diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index 7a16e83e4..3d50969d9 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -115,7 +115,7 @@ source_set("openthread") { "platform/spi-slave.h", "platform/time.h", "platform/toolchain.h", - "platform/trel-udp6.h", + "platform/trel.h", "platform/udp.h", "random_crypto.h", "random_noncrypto.h", @@ -128,6 +128,7 @@ source_set("openthread") { "tcp.h", "thread.h", "thread_ftd.h", + "trel.h", "udp.h", ] diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 4aec89d3b..71f5d39e0 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (188) +#define OPENTHREAD_API_VERSION (189) /** * @addtogroup api-instance diff --git a/include/openthread/platform/trel-udp6.h b/include/openthread/platform/trel-udp6.h deleted file mode 100644 index 4c0f9e21d..000000000 --- a/include/openthread/platform/trel-udp6.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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 - * @brief - * This file includes the platform abstraction for Thread Radio Encapsulation Link (TREL) using an IPv6/UDP interface. - * - */ - -#ifndef OPENTHREAD_PLATFORM_TREL_UDP6_H_ -#define OPENTHREAD_PLATFORM_TREL_UDP6_H_ - -#include - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @addtogroup plat-trel - * - * @brief - * This module includes the platform abstraction for Thread Radio Encapsulation Link (TREL) using an IPv6/UDP - * interface. - * - * @{ - * - */ - -/** - * This function initializes the TREL IPv6/UDP interface. - * - * This function is called before any other TREL platform functions. - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aUnicastAddress The unicast address to add to interface and use as tx source and rx destination. - * @param[in] aUdpPort A UDP port number to use. - * - */ -void otPlatTrelUdp6Init(otInstance *aInstance, const otIp6Address *aUnicastAddress, uint16_t aUdpPort); - -/** - * This function updates the unicast IPv6 address for TREL IPv6/UDP interface. - * - * The interface should only have one unicast IPv6 address. Calling this function replaces any previously set unicast - * IPv6 address (during initialization from `otPlatTrelUdp6Init` or earlier calls to `otPlatTrelUdp6UpdateAddress()`). - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aUnicastAddress The unicast address to add to interface and use for as tx source and rx destination. - * - */ -void otPlatTrelUdp6UpdateAddress(otInstance *aInstance, const otIp6Address *aUnicastAddress); - -/** - * This function subscribes the TREL IPv6/UDP interface to a new multicast address. - * - * This function may be called multiple times to subscribe to different addresses. The interface should accept/receive - * packets destined to any previously subscribed multicast address in addition to the unicast address added from the - * `otPlatTrelUdp6Init()` function when interface was initialized. - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aMulticastAddress A multicast IPv6 address. - * - */ -void otPlatTrelUdp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aMulticastAddress); - -/** - * This function requests a packet to be sent to a given destination. - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aBuffer A pointer to buffer containing the packet to send. - * @param[in] aLength Packet length (number of bytes). - * @param[in] aDestAddress The destination IPv6 address (can be a unicast or a multicast IPv6 address). - * - * @retval OT_ERROR_NONE The tx request was handled successfully. - * @retval OT_ERROR_ABORT The interface is not ready and tx was aborted - * - */ -otError otPlatTrelUdp6SendTo(otInstance * aInstance, - const uint8_t * aBuffer, - uint16_t aLength, - const otIp6Address *aDestAddress); - -/** - * This function is a callback from platform to notify of a received packet. - * - * @note The buffer content (up to its specified length) may get changed during processing by OpenThread core (e.g., - * decrypted in place), so the platform implementation should expect that after returning from this function the - * packet @p aBuffer content may have been altered. - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aBuffer A buffer containing the received packet. - * @param[in] aLength Packet length (number of bytes). - * - */ -extern void otPlatTrelUdp6HandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength); - -/** - * This optional function is intended for testing only. It changes the test mode status for TREL interface. - * - * This function requests TREL interface to be temporarily disabled or enabled. When disabled all traffic flow through - * the TREL interface should be silently dropped. - * - * A default weak implementation of this method is provided by OpenThread (returning NOT_IMPLEMENTED). - * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aEnable Indicates whether to enable/disable the TREL interface. - * - * @retval OT_ERROR_NONE Successfully changed the TREL interface test status (enabled/disabled). - * @retval OT_ERROR_FAILED Failed to enable the TREL interface. - * @retval OT_ERROR_NOT_IMPLEMENTED This function is not provided by the platform. - * - */ -otError otPlatTrelUdp6SetTestMode(otInstance *aInstance, bool aEnable); - -/** - * @} - * - */ - -#ifdef __cplusplus -} // end of extern "C" -#endif - -#endif // OPENTHREAD_PLATFORM_TREL_UDP6_H_ diff --git a/include/openthread/platform/trel.h b/include/openthread/platform/trel.h new file mode 100644 index 000000000..d4e0475a6 --- /dev/null +++ b/include/openthread/platform/trel.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021, 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 + * @brief + * This file includes the platform abstraction for Thread Radio Encapsulation Link (TREL) using DNS-SD and UDP/IPv6. + * + */ + +#ifndef OPENTHREAD_PLATFORM_TREL_H_ +#define OPENTHREAD_PLATFORM_TREL_H_ + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup plat-trel + * + * @brief + * This module includes the platform abstraction for Thread Radio Encapsulation Link (TREL) using DNS-SD and + * UDP/IPv6. + * + * @{ + * + */ + +/** + * This function initializes and enables TREL platform layer. + * + * Upon this call, the platform layer MUST perform the following: + * + * 1) TREL platform layer MUST open a UDP socket to listen for and receive TREL messages from peers. The socket is + * bound to an ephemeral port number chosen by the platform layer. The port number MUST be returned in @p aUdpPort. + * The socket is also bound to network interface(s) on which TREL is to be supported. The socket and the chosen port + * should stay valid while TREL is enabled. + * + * 2) Platform layer MUST initiate an ongoing DNS-SD browse on the service name "_trel._udp" within the local browsing + * domain to discover other devices supporting TREL. The ongoing browse will produce two different types of events: + * "add" events and "remove" events. When the browse is started, it should produce an "add" event for every TREL peer + * currently present on the network. Whenever a TREL peer goes offline, a "remove" event should be produced. "remove" + * events are not guaranteed, however. When a TREL service instance is discovered, a new ongoing DNS-SD query for an + * AAAA record should be started on the hostname indicated in the SRV record of the discovered instance. If multiple + * host IPv6 addressees are discovered for a peer, one with highest scope among all addresses MUST be reported (if + * there are multiple address at same scope, one must be selected randomly). + * + * TREL platform MUST signal back the discovered peer info using `otPlatTrelHandleDiscoveredPeerInfo()` callback. This + * callback MUST be invoked when a new peer is discovered, when there is a change in an existing entry (e.g., new + * TXT record or new port number or new IPv6 address), or when the peer is removed. + * + * @param[in] aInstance The OpenThread instance. + * @param[out] aUdpPort A pointer to return the selected port number by platform layer. + * + */ +void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort); + +/** + * This function disables TREL platform layer. + * + * After this call, the platform layer MUST stop DNS-SD browse on the service name "_trel._udp", stop advertising the + * TREL DNS-SD service (from `otPlatTrelRegisterService()`) and MUST close the UDP socket used to receive TREL messages. + * + * @pram[in] aInstance The OpenThread instance. + * + */ +void otPlatTrelDisable(otInstance *aInstance); + +/** + * This structure represents a TREL peer info discovered using DNS-SD browse on the service name "_trel._udp". + * + */ +typedef struct otPlatTrelPeerInfo +{ + /** + * This boolean flag indicates whether the entry is being removed or added. + * + * - TRUE indicates that peer is removed. + * - FALSE indicates that it is a new entry or an update to an existing entry. + * + */ + bool mRemoved; + + /** + * The TXT record data (encoded as specified by DNS-SD) from the SRV record of the discovered TREL peer service + * instance. + * + */ + const uint8_t *mTxtData; + + uint16_t mTxtLength; ///< Number of bytes in @p mTxtData buffer. + + /** + * The TREL peer socket address (IPv6 address and port number). + * + * The port number is determined from the SRV record of the discovered TREL peer service instance. The IPv6 address + * is determined from the DNS-SD query for AAAA records on the hostname indicated in the SRV record of the + * discovered service instance. If multiple host IPv6 addressees are discovered, one with highest scope is used. + * + */ + otSockAddr mSockAddr; +} otPlatTrelPeerInfo; + +/** + * This is a callback function from platform layer to report a discovered TREL peer info. + * + * @note The @p aInfo structure and its content (e.g., the `mTxtData` buffer) does not need to persist after returning + * from this call. OpenThread code will make a copy of all the info it needs. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aInfo A pointer to the TREL peer info. + * + */ +extern void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo); + +/** + * This function registers a new service to be advertised using DNS-SD [RFC6763]. + * + * The service name is "_trel._udp". The platform should use its own hostname, which when combined with the service + * name and the local DNS-SD domain name will produce the full service instance name, for example + * "example-host._trel._udp.local.". + * + * The domain under which the service instance name appears will be 'local' for mDNS, and will be whatever domain is + * used for service registration in the case of a non-mDNS local DNS-SD service. + * + * A subsequent call to this function updates the previous service. It is used to update the TXT record data and/or the + * port number. + * + * The @p aTxtData buffer is not persisted after the return from this function. The platform layer MUST NOT keep the + * pointer and instead copy the content if needed. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aPort The port number to include in the SRV record of the advertised service. + * @param[in] aTxtData A pointer to the TXT record data (encoded) to be include in the advertised service. + * @param[in] aTxtLength The length of @p aTxtData (number of bytes). + * + * + */ +void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength); + +/** + * This function requests a TREL UDP packet to be sent to a given destination. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aUdpPayload A pointer to UDP payload. + * @param[in] aUdpPayloadLen The payload length (number of bytes). + * @param[in] aDestSockAddr The destination socket address. + * + */ +void otPlatTrelSend(otInstance * aInstance, + const uint8_t * aUdpPayload, + uint16_t aUdpPayloadLen, + const otSockAddr *aDestSockAddr); + +/** + * This function is a callback from platform to notify of a received TREL UDP packet. + * + * @note The buffer content (up to its specified length) may get changed during processing by OpenThread core (e.g., + * decrypted in place), so the platform implementation should expect that after returning from this function the + * @p aBuffer content may have been altered. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aBuffer A buffer containing the received UDP payload. + * @param[in] aLength UDP payload length (number of bytes). + * + */ +extern void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // OPENTHREAD_PLATFORM_TREL_H_ diff --git a/include/openthread/trel.h b/include/openthread/trel.h new file mode 100644 index 000000000..d8ee67f7f --- /dev/null +++ b/include/openthread/trel.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2021, 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 + * @brief + * This file defines the OpenThread TREL (Thread Radio Encapsulation Link) APIs for Thread Over Infrastructure. + * + */ + +#ifndef OPENTHREAD_TREL_H_ +#define OPENTHREAD_TREL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-trel + * + * @brief + * This file defines Thread Radio Encapsulation Link (TREL) APIs for Thread Over Infrastructure. + * + * The functions in this file require `OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE` to be enabled. + * + * @{ + * + */ + +/** + * This struct represents a TREL peer. + * + */ +typedef struct otTrelPeer +{ + otExtAddress mExtAddress; ///< The Extended MAC Address of TREL peer. + otExtendedPanId mExtPanId; ///< The Extended PAN Identifier of TREL peer. + otSockAddr mSockAddr; ///< The IPv6 socket address of TREL peer. +} otTrelPeer; + +/** + * This type represents an iterator for iterating over TREL peer table entries. + * + */ +typedef uint16_t otTrelPeerIterator; + +/** + * This function enables TREL operation. + * + * This function initiates an ongoing DNS-SD browse on the service name "_trel._udp" within the local browsing domain + * to discover other devices supporting TREL. Device also registers a new service to be advertised using DNS-SD, + * with the service name is "_trel._udp" indicating its support for TREL. Device is then ready to receive TREL messages + * from peers. + * + * @note By default the OpenThread stack enables the TREL operation on start. + * + * @param[in] aInstance The OpenThread instance. + * + */ +void otTrelEnable(otInstance *aInstance); + +/** + * This function disables TREL operation. + * + * This function stops the DNS-SD browse on the service name "_trel._udp", stops advertising TREL DNS-SD service, and + * clears the TREL peer table. + * + * @param[in] aInstance The OpenThread instance. + * + */ +void otTrelDisable(otInstance *aInstance); + +/** + * This function indicates whether the TREL operation is enabled. + * + * @param[in] aInstance The OpenThread instance. + * + * @retval TRUE if the TREL operation is enabled. + * @retval FALSE if the TREL operation is disabled. + * + */ +bool otTrelIsEnabled(otInstance *aInstance); + +/** + * This function initializes a peer table iterator. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aIterator The iterator to initialize. + * + */ +void otTrelInitPeerIterator(otInstance *aInstance, otTrelPeerIterator *aIterator); + +/** + * This function iterates over the peer table entries and get the next entry from the table + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aIterator The iterator. MUST be initialized. + * + * @returns A pointer to the next `otTrelPeer` entry or `NULL` if no more entries in the table. + * + */ +const otTrelPeer *otTrelGetNextPeer(otInstance *aInstance, otTrelPeerIterator *aIterator); + +/** + * This function sets the filter mode (enables/disables filtering). + * + * When filter mode is enabled, any rx and tx traffic through TREL interface is silently dropped. This is mainly + * intended for use during testing. + * + * Unlike `otTrel{Enable/Disable}()` which fully starts/stops the TREL operation, when filter mode is enabled the + * TREL interface continues to be enabled. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aFiltered TRUE to enable filter mode, FALSE to disable filter mode. + * + */ +void otTrelSetFilterEnabled(otInstance *aInstance, bool aEnable); + +/** + * This function indicates whether or not the filter mode is enabled. + * + * @param[in] aInstance The OpenThread instance. + * + * @retval TRUE if the TREL filter mode is enabled. + * @retval FALSE if the TREL filter mode is disabled. + * + */ +bool otTrelIsFilterEnabled(otInstance *aInstance); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_TREL_H_ diff --git a/src/cli/README.md b/src/cli/README.md index e7b3e7566..1085b2b17 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -108,7 +108,7 @@ Done - [state](#state) - [srp](README_SRP.md) - [thread](#thread-start) -- [trel](#trel-enable) +- [trel](#trel) - [tvcheck](#tvcheck-enable) - [txpower](#txpower) - [udp](README_UDP.md) @@ -2643,13 +2643,21 @@ Get the Thread Version number. Done ``` +### trel + +Indicate whether TREL radio operation is enabled or not. + +`OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE` is required for all `trel` sub-commands. + +```bash +> trel +Enabled +Done +``` + ### trel enable -Enable TREL radio link. - -`OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE` is required. - -Note: TREL radio link can be enabled only when a valid TREL URL was specified. +Enable TREL operation. ```bash > trel enable @@ -2658,15 +2666,62 @@ Done ### trel disable -Disable TREL radio link. - -`OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE` is required. +Disable TREL operation. ```bash > trel disable Done ``` +### trel filter + +Indicate whether TREL filter mode is enabled or not + +When filter mode is enabled, any rx and tx traffic through TREL interface is silently dropped. This is mainly intended for use during testing. + +```bash +> trel filter +Disabled +Done +``` + +### trel filter enable + +Enable TREL filter mode. + +```bash +> trel filter enable +Done +``` + +### trel filter disable + +Disable TREL filter mode. + +```bash +> trel filter disable +Done +``` + +### trel peers [list] + +Get the TREL peer table in table format or as a list. + +```bash +> trel peers +| No | Ext MAC Address | Ext PAN Id | IPv6 Socket Address | ++-----+------------------+------------------+--------------------------------------------------+ +| 1 | 5e5785ba3a63adb9 | f0d9c001f00d2e43 | [fe80:0:0:0:cc79:2a29:d311:1aea]:9202 | +| 2 | ce792a29d3111aea | dead00beef00cafe | [fe80:0:0:0:5c57:85ba:3a63:adb9]:9203 | +Done + +> trel peers list +001 ExtAddr:5e5785ba3a63adb9 ExtPanId:f0d9c001f00d2e43 SockAddr:[fe80:0:0:0:cc79:2a29:d311:1aea]:9202 +002 ExtAddr:ce792a29d3111aea ExtPanId:dead00beef00cafe SockAddr:[fe80:0:0:0:5c57:85ba:3a63:adb9]:9203 +>>>>>>> [trel] implement new TREL model using DNS-SD +Done +``` + ### tvcheck enable Enable thread version check when upgrading to router or leader. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index f0fb03472..74713cdfe 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -82,7 +82,7 @@ #include #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE -#include +#include #endif #include "common/logging.hpp" @@ -4622,12 +4622,92 @@ exit: #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE otError Interpreter::ProcessTrel(Arg aArgs[]) { - otError error; + otError error = OT_ERROR_NONE; bool enable; - SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable)); + if (aArgs[0].IsEmpty()) + { + OutputEnabledDisabledStatus(otTrelIsEnabled(GetInstancePtr())); + } + else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE) + { + if (enable) + { + otTrelEnable(GetInstancePtr()); + } + else + { + otTrelDisable(GetInstancePtr()); + } + } + else if (aArgs[0] == "filter") + { + if (aArgs[1].IsEmpty()) + { + OutputEnabledDisabledStatus(otTrelIsFilterEnabled(GetInstancePtr())); + } + else + { + SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable)); + otTrelSetFilterEnabled(GetInstancePtr(), enable); + } + } + else if (aArgs[0] == "peers") + { + uint16_t index = 0; + otTrelPeerIterator iterator; + const otTrelPeer * peer; + bool isTable = true; - error = otPlatTrelUdp6SetTestMode(GetInstancePtr(), enable); + if (aArgs[1] == "list") + { + isTable = false; + } + else + { + VerifyOrExit(aArgs[1].IsEmpty(), error = kErrorInvalidArgs); + } + + if (isTable) + { + static const char *const kTrelPeerTableTitles[] = {"No", "Ext MAC Address", "Ext PAN Id", + "IPv6 Socket Address"}; + + static const uint8_t kTrelPeerTableColumnWidths[] = {5, 18, 18, 50}; + + OutputTableHeader(kTrelPeerTableTitles, kTrelPeerTableColumnWidths); + } + + otTrelInitPeerIterator(GetInstancePtr(), &iterator); + + while ((peer = otTrelGetNextPeer(GetInstancePtr(), &iterator)) != nullptr) + { + if (!isTable) + { + OutputFormat("%03u ExtAddr:", ++index); + OutputExtAddress(peer->mExtAddress); + OutputFormat(" ExtPanId:"); + OutputBytes(peer->mExtPanId.m8); + OutputFormat(" SockAddr:"); + OutputSockAddrLine(peer->mSockAddr); + } + else + { + char string[OT_IP6_SOCK_ADDR_STRING_SIZE]; + + OutputFormat("| %3u | ", ++index); + OutputExtAddress(peer->mExtAddress); + OutputFormat(" | "); + OutputBytes(peer->mExtPanId.m8); + otIp6SockAddrToString(&peer->mSockAddr, string, sizeof(string)); + OutputLine(" | %-48s |", string); + } + } + } + else + { + error = OT_ERROR_INVALID_ARGS; + } exit: return error; diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index f2a3894b0..c7bb9dcf5 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -346,6 +346,7 @@ openthread_core_files = [ "api/tcp_api.cpp", "api/thread_api.cpp", "api/thread_ftd_api.cpp", + "api/trel_api.cpp", "api/udp_api.cpp", "backbone_router/backbone_tmf.cpp", "backbone_router/backbone_tmf.hpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 013f47225..95ac99841 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -78,6 +78,7 @@ set(COMMON_SOURCES api/tcp_api.cpp api/thread_api.cpp api/thread_ftd_api.cpp + api/trel_api.cpp api/udp_api.cpp backbone_router/backbone_tmf.cpp backbone_router/bbr_leader.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index f40f8510a..c965f9e00 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -168,6 +168,7 @@ SOURCES_COMMON = \ api/tcp_api.cpp \ api/thread_api.cpp \ api/thread_ftd_api.cpp \ + api/trel_api.cpp \ api/udp_api.cpp \ backbone_router/backbone_tmf.cpp \ backbone_router/bbr_leader.cpp \ diff --git a/src/core/api/trel_api.cpp b/src/core/api/trel_api.cpp new file mode 100644 index 000000000..01feb25a4 --- /dev/null +++ b/src/core/api/trel_api.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021, 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 the OpenThread TREL (Thread Radio Encapsulation Link) APIs for Thread Over Infrastructure. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +#include + +#include "common/as_core_type.hpp" +#include "common/code_utils.hpp" +#include "common/instance.hpp" + +using namespace ot; + +void otTrelEnable(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().Enable(); +} + +void otTrelDisable(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().Disable(); +} + +bool otTrelIsEnabled(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsEnabled(); +} + +void otTrelInitPeerIterator(otInstance *aInstance, otTrelPeerIterator *aIterator) +{ + AsCoreType(aInstance).Get().InitIterator(*aIterator); +} + +const otTrelPeer *otTrelGetNextPeer(otInstance *aInstance, otTrelPeerIterator *aIterator) +{ + return AsCoreType(aInstance).Get().GetNextPeer(*aIterator); +} + +void otTrelSetFilterEnabled(otInstance *aInstance, bool aEnable) +{ + AsCoreType(aInstance).Get().SetFilterEnabled(aEnable); +} + +bool otTrelIsFilterEnabled(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsFilterEnabled(); +} + +#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/common/notifier.cpp b/src/core/common/notifier.cpp index a77660ac2..4b764ec1e 100644 --- a/src/core/common/notifier.cpp +++ b/src/core/common/notifier.cpp @@ -165,6 +165,9 @@ void Notifier::EmitEvents(void) #if OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE) Get().HandleNotifierEvents(events); #endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + Get().HandleNotifierEvents(events); +#endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE Get().HandleNotifierEvents(events); #endif diff --git a/src/core/radio/trel_interface.cpp b/src/core/radio/trel_interface.cpp index 66698eebe..a2cf1a56e 100644 --- a/src/core/radio/trel_interface.cpp +++ b/src/core/radio/trel_interface.cpp @@ -34,102 +34,419 @@ #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE -#include +#include #include "common/as_core_type.hpp" #include "common/code_utils.hpp" +#include "common/debug.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/logging.hpp" +#include "common/string.hpp" +#include "net/dns_types.hpp" +#include "utils/parse_cmdline.hpp" namespace ot { namespace Trel { +const char Interface::kTxtRecordExtAddressKey[] = "xa"; +const char Interface::kTxtRecordExtPanIdKey[] = "xp"; + Interface::Interface(Instance &aInstance) : InstanceLocator(aInstance) , mInitialized(false) + , mEnabled(false) + , mFiltered(false) + , mRegisterServiceTask(aInstance, HandleRegisterServiceTask) { } void Interface::Init(void) { - Ip6::Address ip6Address; - OT_ASSERT(!mInitialized); - ip6Address.SetToLinkLocalAddress(Get().GetExtAddress()); - otPlatTrelUdp6Init(&GetInstance(), &ip6Address, kUdpPort); - - CreateMulticastIp6Address(ip6Address); - otPlatTrelUdp6SubscribeMulticastAddress(&GetInstance(), &ip6Address); - mInitialized = true; + + if (mEnabled) + { + mEnabled = false; + Enable(); + } } -void Interface::HandleExtAddressChange(void) +void Interface::Enable(void) { - Ip6::Address ip6Address; + VerifyOrExit(!mEnabled); + mEnabled = true; VerifyOrExit(mInitialized); - ip6Address.SetToLinkLocalAddress(Get().GetExtAddress()); - otPlatTrelUdp6UpdateAddress(&GetInstance(), &ip6Address); + otPlatTrelEnable(&GetInstance(), &mUdpPort); + + otLogInfoMac("Trel: Enabled interface, local port:%u", mUdpPort); + mRegisterServiceTask.Post(); exit: return; } -Error Interface::Send(const Packet &aPacket) +void Interface::Disable(void) { - Ip6::Address destIp6Address; + VerifyOrExit(mEnabled); + + mEnabled = false; + VerifyOrExit(mInitialized); + + otPlatTrelDisable(&GetInstance()); + mPeerTable.Clear(); + otLogDebgMac("Trel: Disabled interface"); + +exit: + return; +} + +void Interface::HandleExtAddressChange(void) +{ + VerifyOrExit(mInitialized && mEnabled); + otLogDebgMac("Trel: Extended Address changed, re-registering DNS-SD service"); + mRegisterServiceTask.Post(); + +exit: + return; +} + +void Interface::HandleExtPanIdChange(void) +{ + VerifyOrExit(mInitialized && mEnabled); + otLogDebgMac("Trel: Extended PAN ID changed, re-registering DNS-SD service"); + mRegisterServiceTask.Post(); + +exit: + return; +} + +void Interface::HandleRegisterServiceTask(Tasklet &aTasklet) +{ + aTasklet.Get().RegisterService(); +} + +void Interface::RegisterService(void) +{ + // TXT data consists of two entries: the length fields , the + // "key" string, "=" char, and hex representation of the MAC or + // Extended PAN ID values. + static constexpr uint8_t kTxtDataSize = + sizeof(uint8_t) + sizeof(kTxtRecordExtAddressKey) - 1 + sizeof(char) + sizeof(Mac::ExtAddress) * 2 + + sizeof(uint8_t) + sizeof(kTxtRecordExtPanIdKey) - 1 + sizeof(char) + sizeof(Mac::ExtendedPanId) * 2; + + uint8_t txtData[kTxtDataSize]; + uint8_t * txtPtr = &txtData[0]; + String string; + + VerifyOrExit(mInitialized && mEnabled); + + string.Append("%s=", kTxtRecordExtAddressKey); + string.AppendHexBytes(Get().GetExtAddress().m8, sizeof(Mac::ExtAddress)); + + *txtPtr++ = static_cast(string.GetLength()); + memcpy(txtPtr, string.AsCString(), string.GetLength()); + txtPtr += string.GetLength(); + + string.Clear(); + string.Append("%s=", kTxtRecordExtPanIdKey); + string.AppendHexBytes(Get().GetExtendedPanId().m8, sizeof(Mac::ExtendedPanId)); + + *txtPtr++ = static_cast(string.GetLength()); + memcpy(txtPtr, string.AsCString(), string.GetLength()); + txtPtr += string.GetLength(); + + OT_ASSERT(txtPtr == OT_ARRAY_END(txtData)); + + otLogInfoMac("Trel: Registering DNS-SD service: port:%u, txt:\"%s=%s, %s=%s\"", mUdpPort, kTxtRecordExtAddressKey, + Get().GetExtAddress().ToString().AsCString(), kTxtRecordExtPanIdKey, + Get().GetExtendedPanId().ToString().AsCString()); + + otPlatTrelRegisterService(&GetInstance(), mUdpPort, txtData, sizeof(txtData)); + +exit: + return; +} + +extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo) +{ + Instance &instance = AsCoreType(aInstance); + + VerifyOrExit(instance.IsInitialized()); + instance.Get().HandleDiscoveredPeerInfo(*static_cast(aInfo)); + +exit: + return; +} + +void Interface::HandleDiscoveredPeerInfo(const Peer::Info &aInfo) +{ + Peer * entry; + Mac::ExtAddress extAddress; + Mac::ExtendedPanId extPanId; + bool isNew = false; + + VerifyOrExit(mInitialized && mEnabled); + + SuccessOrExit(ParsePeerInfoTxtData(aInfo, extAddress, extPanId)); + + VerifyOrExit(extAddress != Get().GetExtAddress()); + + if (aInfo.IsRemoved()) + { + entry = mPeerTable.FindMatching(extAddress); + VerifyOrExit(entry != nullptr); + RemovePeerEntry(*entry); + ExitNow(); + } + + // It is a new entry or an update to an existing entry. First + // check whether we have an existing entry that matches the same + // socket address, and remove it if it is associated with a + // different Extended MAC address. This ensures that we do not + // keep stale entries in the peer table. + + entry = mPeerTable.FindMatching(aInfo.GetSockAddr()); + + if ((entry != nullptr) && !entry->Matches(extAddress)) + { + RemovePeerEntry(*entry); + entry = nullptr; + } + + if (entry == nullptr) + { + entry = mPeerTable.FindMatching(extAddress); + } + + if (entry == nullptr) + { + entry = GetNewPeerEntry(); + VerifyOrExit(entry != nullptr); + + entry->SetExtAddress(extAddress); + isNew = true; + } + + if (!isNew) + { + VerifyOrExit((entry->GetExtPanId() != extPanId) || (entry->GetSockAddr() != aInfo.GetSockAddr())); + } + + entry->SetExtPanId(extPanId); + entry->SetSockAddr(aInfo.GetSockAddr()); + + entry->Log(isNew ? "Added" : "Updated"); + +exit: + return; +} + +template +static Error ParseValueAsHexString(const Dns::TxtEntry &aTxtEntry, uint8_t (&aBuffer)[kBufferSize]) +{ + // Parse the value from `Dns::TxtEntry` as a hex string and + // populates the parsed bytes into `aBuffer`. It requires parsing of + // the hex string to result in exactly `kBufferSize` decoded bytes. + + Error error = kErrorParse; + char hexString[kBufferSize * 2 + 1]; + + VerifyOrExit(aTxtEntry.mValueLength < sizeof(hexString)); + + memcpy(hexString, aTxtEntry.mValue, aTxtEntry.mValueLength); + hexString[aTxtEntry.mValueLength] = '\0'; + + error = Utils::CmdLineParser::ParseAsHexString(hexString, aBuffer); + +exit: + return error; +} + +Error Interface::ParsePeerInfoTxtData(const Peer::Info & aInfo, + Mac::ExtAddress & aExtAddress, + Mac::ExtendedPanId &aExtPanId) const +{ + Error error; + Dns::TxtEntry entry; + Dns::TxtEntry::Iterator iterator; + bool parsedExtAddress = false; + bool parsedExtPanId = false; + + aExtPanId.Clear(); + + iterator.Init(aInfo.GetTxtData(), aInfo.GetTxtLength()); + + while ((error = iterator.GetNextEntry(entry)) == kErrorNone) + { + if (strcmp(entry.mKey, kTxtRecordExtAddressKey) == 0) + { + VerifyOrExit(!parsedExtAddress, error = kErrorParse); + SuccessOrExit(error = ParseValueAsHexString(entry, aExtAddress.m8)); + parsedExtAddress = true; + } + else if (strcmp(entry.mKey, kTxtRecordExtPanIdKey) == 0) + { + VerifyOrExit(!parsedExtPanId, error = kErrorParse); + SuccessOrExit(error = ParseValueAsHexString(entry, aExtPanId.m8)); + parsedExtPanId = true; + } + + // Skip over and ignore any unknown keys. + } + + VerifyOrExit(error == kErrorNotFound); + error = kErrorNone; + + VerifyOrExit(parsedExtAddress && parsedExtPanId, error = kErrorParse); + +exit: + return error; +} + +Interface::Peer *Interface::GetNewPeerEntry(void) +{ + Peer *peerEntry; + + peerEntry = mPeerTable.PushBack(); + VerifyOrExit(peerEntry == nullptr); + + for (Peer &entry : mPeerTable) + { + if (entry.GetExtPanId() != Get().GetExtendedPanId()) + { + ExitNow(peerEntry = &entry); + } + } + + for (Peer &entry : mPeerTable) + { + // We skip over any existing entry in neighbor table (even if the + // entry is in invalid state). + + if (Get().FindNeighbor(entry.GetExtAddress(), Neighbor::kInStateAny) != nullptr) + { + continue; + } + +#if OPENTHREAD_FTD + { + Mac::Address macAddress; + + macAddress.SetExtended(entry.GetExtAddress()); + + if (Get().FindRxOnlyNeighborRouter(macAddress) != nullptr) + { + continue; + } + } +#endif + + ExitNow(peerEntry = &entry); + } + +exit: + return peerEntry; +} + +void Interface::RemovePeerEntry(Peer &aEntry) +{ + aEntry.Log("Removing"); + + // Replace the entry being removed with the last entry (if not the + // last one already) and then pop the last entry from array. + + if (&aEntry != mPeerTable.Back()) + { + aEntry = *mPeerTable.Back(); + } + + mPeerTable.PopBack(); +} + +Error Interface::Send(const Packet &aPacket, bool aIsDiscovery) +{ + Error error = kErrorNone; + Peer *peerEntry; + + VerifyOrExit(mInitialized && mEnabled, error = kErrorAbort); + VerifyOrExit(!mFiltered); switch (aPacket.GetHeader().GetType()) { case Header::kTypeBroadcast: - CreateMulticastIp6Address(destIp6Address); + for (Peer &entry : mPeerTable) + { + if (!aIsDiscovery && (entry.GetExtPanId() != Get().GetExtendedPanId())) + { + continue; + } + + otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &entry.mSockAddr); + } break; case Header::kTypeUnicast: case Header::kTypeAck: - destIp6Address.SetToLinkLocalAddress(aPacket.GetHeader().GetDestination()); + peerEntry = mPeerTable.FindMatching(aPacket.GetHeader().GetDestination()); + VerifyOrExit(peerEntry != nullptr, error = kErrorAbort); + otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &peerEntry->mSockAddr); break; } - return otPlatTrelUdp6SendTo(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &destIp6Address); +exit: + return error; +} + +extern "C" void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength) +{ + Instance &instance = AsCoreType(aInstance); + + VerifyOrExit(instance.IsInitialized()); + instance.Get().HandleReceived(aBuffer, aLength); + +exit: + return; } void Interface::HandleReceived(uint8_t *aBuffer, uint16_t aLength) { + otLogDebgMac("Trel: HandleReceived(aLength:%u)", aLength); + + VerifyOrExit(mInitialized && mEnabled && !mFiltered); + mRxPacket.Init(aBuffer, aLength); Get().ProcessReceivedPacket(mRxPacket); + +exit: + return; } -void Interface::CreateMulticastIp6Address(Ip6::Address &aIp6Address) +const Interface::Peer *Interface::GetNextPeer(PeerIterator &aIterator) const { - // Use ff02::1 (Link-local All Nodes multicast address). - aIp6Address.SetToLinkLocalAllNodesMulticast(); + const Peer *entry = mPeerTable.At(aIterator); + + if (entry != nullptr) + { + aIterator++; + } + + return entry; +} + +void Interface::Peer::Log(const char *aAction) const +{ + OT_UNUSED_VARIABLE(aAction); + + otLogInfoMac("Trel: %s peer mac:%s, xpan:%s, %s", aAction, GetExtAddress().ToString().AsCString(), + GetExtPanId().ToString().AsCString(), GetSockAddr().ToString().AsCString()); } } // namespace Trel } // namespace ot -extern "C" void otPlatTrelUdp6HandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength) -{ - ot::Instance &instance = ot::AsCoreType(aInstance); - - VerifyOrExit(instance.IsInitialized()); - instance.Get().HandleReceived(aBuffer, aLength); - -exit: - return; -} - -extern "C" OT_TOOL_WEAK otError otPlatTrelUdp6SetTestMode(otInstance *aInstance, bool aEnable) -{ - OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aEnable); - - return ot::kErrorNotImplemented; -} - #endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/radio/trel_interface.hpp b/src/core/radio/trel_interface.hpp index 1d84f7f10..93a1eab40 100644 --- a/src/core/radio/trel_interface.hpp +++ b/src/core/radio/trel_interface.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, The OpenThread Authors. + * Copyright (c) 2019-2021, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,82 +38,222 @@ #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +#include +#include + +#include "common/array.hpp" #include "common/locator.hpp" +#include "common/tasklet.hpp" +#include "common/time.hpp" #include "mac/mac_types.hpp" #include "net/ip6_address.hpp" +#include "net/socket.hpp" #include "radio/trel_packet.hpp" +#include "thread/mle_types.hpp" namespace ot { namespace Trel { +class Link; + +extern "C" void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength); +extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo); + /** * This class represents a TREL link interface. * */ class Interface : public InstanceLocator { + friend class Link; + friend void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength); + friend void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo); + public: /** - * This method initializes an `Interface` object - * - * @param[in] aInstance A reference to the OpenThread instance. + * This class represents information about a discovered TREL peer. * */ - explicit Interface(Instance &aInstance); + class Peer : public otTrelPeer + { + friend class Interface; + friend void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo); + + public: + /** + * This method returns the Extended MAC Address of the discovered TREL peer. + * + * @returns The Extended MAC Address of the TREL peer. + * + */ + const Mac::ExtAddress &GetExtAddress(void) const { return static_cast(mExtAddress); } + + /** + * This method returns the Extended PAN Identifier of the discovered TREL peer. + * + * @returns The Extended PAN Identifier of the TREL peer. + * + */ + const Mac::ExtendedPanId &GetExtPanId(void) const { return static_cast(mExtPanId); } + + /** + * This method returns the IPv6 socket address of the discovered TREL peer. + * + * @returns The IPv6 socket address of the TREP peer. + * + */ + const Ip6::SockAddr &GetSockAddr(void) const { return static_cast(mSockAddr); } + + /** + * This method indicates whether the peer matches a given Extended Address. + * + * @param[in] aExtAddress A Extended Address to match with. + * + * @retval TRUE if the peer matches @p aExtAddress. + * @retval FALSE if the peer does not match @p aExtAddress. + * + */ + bool Matches(const Mac::ExtAddress &aExtAddress) const { return GetExtAddress() == aExtAddress; } + + /** + * This method indicates whether the peer matches a given Socket Address. + * + * @param[in] aSockAddr A Socket Address to match with. + * + * @retval TRUE if the peer matches @p aSockAddr. + * @retval FALSE if the peer does not match @p aSockAddr. + * + */ + bool Matches(const Ip6::SockAddr &aSockAddr) const { return GetSockAddr() == aSockAddr; } + + private: + class Info : public otPlatTrelPeerInfo + { + public: + bool IsRemoved(void) const { return mRemoved; } + const uint8_t * GetTxtData(void) const { return mTxtData; } + uint16_t GetTxtLength(void) const { return mTxtLength; } + const Ip6::SockAddr &GetSockAddr(void) const { return static_cast(mSockAddr); } + }; + + void SetExtAddress(const Mac::ExtAddress &aExtAddress) { mExtAddress = aExtAddress; } + void SetExtPanId(const Mac::ExtendedPanId &aExtPanId) { mExtPanId = aExtPanId; } + void SetSockAddr(const Ip6::SockAddr &aSockAddr) { mSockAddr = aSockAddr; } + void Log(const char *aAction) const; + }; /** - * This method initializes the interface. - * - * This method should be called after OpenThread instance itself is fully initialized, allowing the `Init()` method - * to use method from `Instance` and any of its containing objects. + * This type represents an iterator for iterating over TREL peer table entries. * */ - void Init(void); + typedef otTrelPeerIterator PeerIterator; /** - * This method indicates whether the interface is initialized or not. + * This method enables the TREL interface. * - * @returns TRUE if the interface is initialized, FALSE otherwise. + * This call initiates an ongoing DNS-SD browse on the service name "_trel._udp" within the local browsing domain + * to discover other devices supporting TREL. Device also registers a new service to be advertised using DNS-SD, + * with the service name is "_trel._udp" indicating its support for TREL. Device is ready to receive TREL messages + * from peers. * */ - bool IsInitialized(void) const { return mInitialized; } + void Enable(void); /** - * This method notifies the interface that device's extended MAC address has changed for it to update any - * internal address/state. + * This method disables the TREL interface. + * + * This call stops the DNS-SD browse on the service name "_trel._udp", stops advertising TREL DNS-SD service, and + * clears the TREL peer table. * */ - void HandleExtAddressChange(void); + void Disable(void); /** - * This method sends a packet over the interface. + * This method indicates whether the TREL interface is enabled. * - * @note There is no expected callback from the interface to notify completion of send. - * - * @param[in] aPacket A packet to send. - * - * @retval kErrorNone The frame was sent successfully. - * @retval kErrorAbort The interface is not ready and send was aborted. + * @retval TRUE if the TREL interface is enabled. + * @retval FALSE if the TREL interface is disabled. * */ - Error Send(const Packet &aPacket); + bool IsEnabled(void) const { return mEnabled; } /** - * This method is a callback from platform layer to handle a received packet over the interface. + * This method initializes a peer table iterator. * - * @param[in] aBuffer A pointer to buffer containing the received packet. - * @param[in] aLength The length (number of bytes) in the received packet. + * @param[in] aIterator The iterator to initialize. * */ - void HandleReceived(uint8_t *aBuffer, uint16_t aLength); + void InitIterator(PeerIterator &aIterator) const { aIterator = 0; } + + /** + * This method iterates over the peer table entries. + * + * @param[in] aIterator The iterator. MUST be initialized. + * + * @returns A pointer to the next `Peer` entry or `nullptr` if no more entries in the table. + * + */ + const Peer *GetNextPeer(PeerIterator &aIterator) const; + + /** + * This method sets the filter mode (enables/disables filtering). + * + * When filtering is enabled, any rx and tx traffic through TREL interface is silently dropped. This is mainly + * intended for use during testing. + * + * Unlike `Enable()/Disable()` which fully start/stop the TREL interface operation, when filter mode is enabled the + * TREL interface continues to be enabled. + * + * @param[in] aFiltered TRUE to enable filter mode, FALSE to disable filter mode. + * + */ + void SetFilterEnabled(bool aEnable) { mFiltered = aEnable; } + + /** + * This method indicates whether or not the filter mode is enabled. + * + * @retval TRUE if the TREL filter mode is enabled. + * @retval FALSE if the TREL filter mode is disabled. + * + */ + bool IsFilterEnabled(void) const { return mFiltered; } private: - static constexpr uint16_t kUdpPort = 19788; // UDP port (same as MLE port). + static constexpr uint16_t kPeerTableExtraEntries = 32; + static constexpr uint16_t kPeerTableSize = Mle::kMaxRouters + Mle::kMaxChildren + kPeerTableExtraEntries; - void CreateMulticastIp6Address(Ip6::Address &aIp6Address); + static const char kTxtRecordExtAddressKey[]; + static const char kTxtRecordExtPanIdKey[]; - bool mInitialized; - Packet mRxPacket; + typedef Array PeerTable; + + explicit Interface(Instance &aInstance); + + // Methods used by `Trel::Link`. + void Init(void); + void HandleExtAddressChange(void); + void HandleExtPanIdChange(void); + Error Send(const Packet &aPacket, bool aIsDiscovery = false); + + // Callbacks from `otPlatTrel`. + void HandleReceived(uint8_t *aBuffer, uint16_t aLength); + void HandleDiscoveredPeerInfo(const Peer::Info &aInfo); + + static void HandleRegisterServiceTask(Tasklet &aTasklet); + void RegisterService(void); + Error ParsePeerInfoTxtData(const Peer::Info & aInfo, + Mac::ExtAddress & aExtAddress, + Mac::ExtendedPanId &aExtPanId) const; + Peer * GetNewPeerEntry(void); + void RemovePeerEntry(Peer &aEntry); + + bool mInitialized : 1; + bool mEnabled : 1; + bool mFiltered : 1; + Tasklet mRegisterServiceTask; + uint16_t mUdpPort; + Packet mRxPacket; + PeerTable mPeerTable; }; } // namespace Trel diff --git a/src/core/radio/trel_link.cpp b/src/core/radio/trel_link.cpp index 0cf6acb87..263c98f5d 100644 --- a/src/core/radio/trel_link.cpp +++ b/src/core/radio/trel_link.cpp @@ -75,6 +75,8 @@ void Link::AfterInit(void) void Link::Enable(void) { + mInterface.Enable(); + if (mState == kStateDisabled) { SetState(kStateSleep); @@ -83,6 +85,8 @@ void Link::Enable(void) void Link::Disable(void) { + mInterface.Disable(); + if (mState != kStateDisabled) { SetState(kStateDisabled); @@ -126,8 +130,9 @@ void Link::BeginTransmit(void) Mac::PanId destPanId; Header::Type type; Packet txPacket; - Neighbor * neighbor = nullptr; - Mac::RxFrame *ackFrame = nullptr; + Neighbor * neighbor = nullptr; + Mac::RxFrame *ackFrame = nullptr; + bool isDisovery = false; VerifyOrExit(mState == kStateTransmit); @@ -163,6 +168,28 @@ void Link::BeginTransmit(void) } } + if (type == Header::kTypeBroadcast) + { + // Thread utilizes broadcast transmissions to discover + // neighboring devices. We determine whether this broadcast + // frame tx is a discovery or normal data. All messages + // used for discovery either disable MAC security or utilize + // MAC Key ID mode 2. All data communication uses MAC Key ID + // Mode 1. + + if (!mTxFrame.GetSecurityEnabled()) + { + isDisovery = true; + } + else + { + uint8_t keyIdMode; + + IgnoreError(mTxFrame.GetKeyIdMode(keyIdMode)); + isDisovery = (keyIdMode == Mac::Frame::kKeyIdMode2); + } + } + if (mTxFrame.GetDstPanId(destPanId) != kErrorNone) { destPanId = Mac::kPanIdBroadcast; @@ -195,7 +222,7 @@ void Link::BeginTransmit(void) otLogDebgMac("Trel: BeginTransmit() [%s] plen:%d", txPacket.GetHeader().ToString().AsCString(), txPacket.GetPayloadLength()); - VerifyOrExit(mInterface.Send(txPacket) == kErrorNone, InvokeSendDone(kErrorAbort)); + VerifyOrExit(mInterface.Send(txPacket, isDisovery) == kErrorNone, InvokeSendDone(kErrorAbort)); if (mTxFrame.GetAckRequest()) { @@ -445,6 +472,14 @@ void Link::SetState(State aState) } } +void Link::HandleNotifierEvents(Events aEvents) +{ + if (aEvents.Contains(kEventThreadExtPanIdChanged)) + { + mInterface.HandleExtPanIdChange(); + } +} + // LCOV_EXCL_START const char *Link::StateToString(State aState) diff --git a/src/core/radio/trel_link.hpp b/src/core/radio/trel_link.hpp index 617ece7d9..fd00ac501 100644 --- a/src/core/radio/trel_link.hpp +++ b/src/core/radio/trel_link.hpp @@ -40,6 +40,7 @@ #include "common/encoding.hpp" #include "common/locator.hpp" +#include "common/notifier.hpp" #include "common/tasklet.hpp" #include "common/timer.hpp" #include "mac/mac_frame.hpp" @@ -70,6 +71,7 @@ namespace Trel { class Link : public InstanceLocator { friend class ot::Instance; + friend class ot::Notifier; friend class Interface; public: @@ -170,6 +172,7 @@ private: void SendAck(Packet &aRxPacket); void ReportDeferredAckStatus(Neighbor &aNeighbor, Error aError); void HandleTimer(Neighbor &aNeighbor); + void HandleNotifierEvents(Events aEvents); static void HandleTxTasklet(Tasklet &aTasklet); void HandleTxTasklet(void); diff --git a/src/core/radio/trel_packet.cpp b/src/core/radio/trel_packet.cpp index ad2ae9848..aaad695f9 100644 --- a/src/core/radio/trel_packet.cpp +++ b/src/core/radio/trel_packet.cpp @@ -110,12 +110,6 @@ Header::InfoString Header::ToString(void) const return string; } -void Packet::Init(uint8_t *aBuffer, uint16_t aLength) -{ - mBuffer = aBuffer; - mLength = aLength; -} - void Packet::Init(Header::Type aType, uint8_t *aPayload, uint16_t aPayloadLength) { uint16_t headerSize = Header::GetSize(aType); @@ -129,8 +123,8 @@ void Packet::Init(Header::Type aType, uint8_t *aPayload, uint16_t aPayloadLength bool Packet::IsHeaderValid(void) const { - return ((mBuffer != nullptr) && (mLength > 0) && GetHeader().IsVersionValid() && - (mLength >= GetHeader().GetLength())); + return ((GetBytes() != nullptr) && (GetLength() > 0) && GetHeader().IsVersionValid() && + (GetLength() >= GetHeader().GetLength())); } } // namespace Trel diff --git a/src/core/radio/trel_packet.hpp b/src/core/radio/trel_packet.hpp index 31c7ab927..5b28cb784 100644 --- a/src/core/radio/trel_packet.hpp +++ b/src/core/radio/trel_packet.hpp @@ -38,6 +38,7 @@ #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +#include "common/data.hpp" #include "common/encoding.hpp" #include "common/locator.hpp" #include "common/string.hpp" @@ -250,11 +251,13 @@ private: } OT_TOOL_PACKED_END; /** - * This class represent a TREL radio link packet. + * This class represents a TREL radio link packet. * */ -class Packet +class Packet : private MutableData { + using Base = MutableData; + public: /** * This method initializes the `Packet` with a given buffer and length. @@ -263,7 +266,7 @@ public: * @param[in] aLength Length (number of bytes) of the packet (including header and payload). * */ - void Init(uint8_t *aBuffer, uint16_t aLength); + void Init(uint8_t *aBuffer, uint16_t aLength) { Base::Init(aBuffer, aLength); } /** * This method initializes the `Packet` with a specified header type and given a payload. @@ -286,7 +289,7 @@ public: * @returns A pointer to buffer containing the packet. * */ - uint8_t *GetBuffer(void) { return mBuffer; } + uint8_t *GetBuffer(void) { return Base::GetBytes(); } /** * This method gets a pointer to buffer containing the packet. @@ -294,7 +297,7 @@ public: * @returns A pointer to buffer containing the packet. * */ - const uint8_t *GetBuffer(void) const { return mBuffer; } + const uint8_t *GetBuffer(void) const { return Base::GetBytes(); } /** * This method gets the length of packet. @@ -302,7 +305,7 @@ public: * @returns The length (number of bytes) of packet (header and payload). * */ - uint16_t GetLength(void) const { return mLength; } + uint16_t GetLength(void) const { return Base::GetLength(); } /** * This method checks whether or not the packet header is valid. @@ -319,7 +322,7 @@ public: * @returns A reference to the packet header as `Header`. * */ - Header &GetHeader(void) { return *reinterpret_cast
(mBuffer); } + Header &GetHeader(void) { return *reinterpret_cast
(Base::GetBytes()); } /** * This method gets the packet header. @@ -327,7 +330,7 @@ public: * @returns A reference to the packet header as `Header`. * */ - const Header &GetHeader(void) const { return *reinterpret_cast(mBuffer); } + const Header &GetHeader(void) const { return *reinterpret_cast(Base::GetBytes()); } /** * This method gets a pointer to start of packet payload. @@ -335,7 +338,7 @@ public: * @returns A pointer to start of packet payload (after header). * */ - uint8_t *GetPayload(void) { return mBuffer + GetHeader().GetLength(); } + uint8_t *GetPayload(void) { return Base::GetBytes() + GetHeader().GetLength(); } /** * This method gets a pointer to start of packet payload. @@ -343,7 +346,7 @@ public: * @returns A pointer to start of packet payload (after header). * */ - const uint8_t *GetPayload(void) const { return mBuffer + GetHeader().GetLength(); } + const uint8_t *GetPayload(void) const { return Base::GetBytes() + GetHeader().GetLength(); } /** * This method gets the payload length. @@ -351,11 +354,7 @@ public: * @returns The packet payload length (number of bytes). * */ - uint16_t GetPayloadLength(void) const { return mLength - GetHeader().GetLength(); } - -private: - uint8_t *mBuffer; - uint16_t mLength; + uint16_t GetPayloadLength(void) const { return GetLength() - GetHeader().GetLength(); } }; } // namespace Trel diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index d2abea7bd..1b710c658 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -261,9 +261,6 @@ NcpBase::NcpBase(Instance *aInstance) , mRxSpinelOutOfOrderTidCounter(0) , mTxSpinelFrameCounter(0) , mDidInitialUpdates(false) -#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE - , mTrelTestModeEnable(true) -#endif , mLogTimestampBase(0) { OT_ASSERT(mInstance != nullptr); diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index 28b020a02..f4e9f644a 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -677,10 +677,6 @@ protected: bool mDidInitialUpdates; -#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE - bool mTrelTestModeEnable; -#endif - uint64_t mLogTimestampBase; // Timestamp base used for logging }; diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 44f4475ce..2810a86c8 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -67,7 +67,7 @@ #include #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE -#include +#include #endif #include "common/code_utils.hpp" @@ -4046,15 +4046,21 @@ exit: #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { - return mEncoder.WriteBool(mTrelTestModeEnable); + return mEncoder.WriteBool(!otTrelIsFilterEnabled(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; + bool testMode; - SuccessOrExit(error = mDecoder.ReadBool(mTrelTestModeEnable)); - error = otPlatTrelUdp6SetTestMode(mInstance, mTrelTestModeEnable); + SuccessOrExit(error = mDecoder.ReadBool(testMode)); + + // Note that `TEST_MODE` being `true` indicates that the TREL + // interface should be enabled and functional, so filtering + // should be disabled. + + otTrelSetFilterEnabled(mInstance, !testMode); exit: return error; diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index a3b05d556..6ba5e3245 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -92,7 +92,7 @@ add_library(openthread-posix settings.cpp spi_interface.cpp system.cpp - trel_udp6.cpp + trel.cpp udp.cpp utils.cpp virtual_time.cpp diff --git a/src/posix/platform/Makefile.am b/src/posix/platform/Makefile.am index 90bb59d32..ab7ae85f4 100644 --- a/src/posix/platform/Makefile.am +++ b/src/posix/platform/Makefile.am @@ -62,7 +62,7 @@ libopenthread_posix_a_SOURCES = \ settings.cpp \ spi_interface.cpp \ system.cpp \ - trel_udp6.cpp \ + trel.cpp \ udp.cpp \ utils.cpp \ virtual_time.cpp \ diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h index 49de56500..9abf0ad0b 100644 --- a/src/posix/platform/openthread-posix-config.h +++ b/src/posix/platform/openthread-posix-config.h @@ -47,35 +47,6 @@ #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. Empty string disables TREL platform. - * - */ -#ifndef OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME -#define OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME "" -#endif - -/** - * @def OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - * - * Defines whether the TREL UDP6 platform uses netlink socket to add/remove addresses on the TREL netif or `ioctl()` - * command. - * - * When netlink is used Duplicate Address Detection (DAD) is disabled when a new address is added on the netif. - * - * Use of netlink is enabled by default on linux-based platforms. - * - */ -#ifndef OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET -#ifdef __linux__ -#define OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET 1 -#else -#define OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET 0 -#endif -#endif - /** * @def OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME * @@ -236,4 +207,15 @@ #endif // __APPLE__ +//--------------------------------------------------------------------------------------------------------------------- +// Removed or renamed POSIX specific configs. + +#ifdef OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME +#error "OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME was removed (no longer applicable with TREL over DNS-SD)." +#endif + +#ifdef OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET +#error "OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET was removed (no longer applicable with TREL over DNS-SD)." +#endif + #endif // OPENTHREAD_PLATFORM_CONFIG_H_ diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index 9eadc6871..6ecbc503d 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -389,10 +389,10 @@ enum SocketBlockOption /** * This function initializes platform TREL UDP6 driver. * - * @param[in] aInterfaceName The name of network interface. + * @param[in] aTrelUrl The TREL URL (configuration for TREL platform). * */ -void platformTrelInit(const char *aInterfaceName); +void platformTrelInit(const char *aTrelUrl); /** * This function shuts down the platform TREL UDP6 platform driver. diff --git a/src/posix/platform/trel.cpp b/src/posix/platform/trel.cpp new file mode 100644 index 000000000..03225ca8c --- /dev/null +++ b/src/posix/platform/trel.cpp @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2019-2021, 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 +#include +#include +#include +#include +#include + +#include + +#include "radio_url.hpp" +#include "system.hpp" +#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 + +typedef struct TxPacket +{ + struct TxPacket *mNext; + uint8_t mBuffer[TREL_MAX_PACKET_SIZE]; + uint16_t mLength; + otSockAddr mDestSockAddr; +} 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 bool sInitialized = false; +static bool sEnabled = false; +static int sSocket = -1; + +#if OPENTHREAD_CONFIG_LOG_PLATFORM && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG) + +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, (uint16_t)(end - cur), "[(len:%d) ", aLength); + VerifyOrExit(cur < end); + + while (aLength-- && (num < kMaxWrite)) + { + cur += snprintf(cur, (uint16_t)(end - cur), "%02x ", *aBuffer++); + VerifyOrExit(cur < end); + + num++; + } + + if (aLength != 0) + { + cur += snprintf(cur, (uint16_t)(end - cur), "... "); + VerifyOrExit(cur < end); + } + + *cur++ = ']'; + VerifyOrExit(cur < end); + + *cur = '\0'; + +exit: + *end = '\0'; + return string; +} + +#endif // #if OPENTHREAD_CONFIG_LOG_PLATFORM && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG) + +static void PrepareSocket(uint16_t &aUdpPort) +{ + int val; + struct sockaddr_in6 sockAddr; + socklen_t sockLen; + + otLogDebgPlat("[trel] PrepareSocket()"); + + sSocket = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, 0, kSocketNonBlock); + VerifyOrDie(sSocket >= 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. + + memset(&sockAddr, 0, sizeof(sockAddr)); + sockAddr.sin6_family = AF_INET6; + sockAddr.sin6_addr = in6addr_any; + sockAddr.sin6_port = 0; + + if (bind(sSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) == -1) + { + otLogCritPlat("[trel] Failed to bind socket"); + DieNow(OT_EXIT_ERROR_ERRNO); + } + + sockLen = sizeof(sockAddr); + + if (getsockname(sSocket, (struct sockaddr *)&sockAddr, &sockLen) == -1) + { + otLogCritPlat("[trel] Failed to get the socket name"); + DieNow(OT_EXIT_ERROR_ERRNO); + } + + aUdpPort = ntohs(sockAddr.sin6_port); +} + +static otError SendPacket(const uint8_t *aBuffer, uint16_t aLength, const otSockAddr *aDestSockAddr) +{ + 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(aDestSockAddr->mPort); + memcpy(&sockAddr.sin6_addr, &aDestSockAddr->mAddress, sizeof(otIp6Address)); + + ret = sendto(sSocket, aBuffer, aLength, 0, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); + + if (ret != aLength) + { + otLogDebgPlat("[trel] 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] SendPacket([%s]:%u) err:%s pkt:%s", Ip6AddrToString(&aDestSockAddr->mAddress), + aDestSockAddr->mPort, 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] ReceivePacket() - received from [%s]:%d, id:%d, pkt:%s", Ip6AddrToString(&sockAddr.sin6_addr), + ntohs(sockAddr.sin6_port), sockAddr.sin6_scope_id, BufferToString(sRxPacketBuffer, sRxPacketLength)); + + if (sEnabled) + { + otPlatTrelHandleReceived(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->mDestSockAddr) == OT_ERROR_INVALID_STATE) + { + otLogDebgPlat("[trel] 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 void EnqueuePacket(const uint8_t *aBuffer, uint16_t aLength, const otSockAddr *aDestSockAddr) +{ + TxPacket *packet; + + // Allocate an available packet entry (from the free packet list) + // and copy the packet content into it. + + VerifyOrExit(sFreeTxPacketHead != NULL, otLogWarnPlat("[trel] EnqueuePacket failed, queue is full")); + packet = sFreeTxPacketHead; + sFreeTxPacketHead = sFreeTxPacketHead->mNext; + + memcpy(packet->mBuffer, aBuffer, aLength); + packet->mLength = aLength; + packet->mDestSockAddr = *aDestSockAddr; + + // 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] EnqueuePacket([%s]:%u) - %s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort, + BufferToString(aBuffer, aLength)); + +exit: + return; +} + +//--------------------------------------------------------------------------------------------------------------------- +// trelDnssd +// +// The functions below are tied to mDNS or DNS-SD library being used on +// a device and need to be implemented per project/platform. A weak empty +// implementation is provided here which describes the expected +// behavior. They need to be overridden during project/platform +// integration. + +OT_TOOL_WEAK void trelDnssdStartBrowse(void) +{ + // This function initiates an ongoing DNS-SD browse on the service + // name "_trel._udp" within the local browsing domain to discover + // other devices supporting TREL. The ongoing browse will produce + // two different types of events: `add` events and `remove` events. + // When the browse is started, it should produce an `add` event for + // every TREL peer currently present on the network. Whenever a + // TREL peer goes offline, a "remove" event should be produced. + // `Remove` events are not guaranteed, however. When a TREL service + // instance is discovered, a new ongoing DNS-SD query for an AAAA + // record MUST be started on the hostname indicated in the SRV + // record of the discovered instance. If multiple host IPv6 + // addressees are discovered for a peer, one with highest scope + // among all addresses MUST be reported (if there are multiple + // address at same scope, one must be selected randomly). + // + // The platform MUST signal back the discovered peer info using + // `otPlatTrelHandleDiscoveredPeerInfo()` callback. This callback + // MUST be invoked when a new peer is discovered, or when there is + // a change in an existing entry (e.g., new TXT record or new port + // number or new IPv6 address), or when the peer is removed. +} + +OT_TOOL_WEAK void trelDnssdStopBrowse(void) +{ + // This function stops the ongoing DNS-SD browse started from an + // earlier call to `trelDnssdStartBrowse()`. +} + +OT_TOOL_WEAK void trelDnssdRegisterService(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength) +{ + // This function registers a new service to be advertised using + // DNS-SD. + // + // The service name is "_trel._udp". The platform should use its own + // hostname, which when combined with the service name and the + // local DNS-SD domain name will produce the full service instance + // name, for example "example-host._trel._udp.local.". + // + // The domain under which the service instance name appears will + // be 'local' for mDNS, and will be whatever domain is used for + // service registration in the case of a non-mDNS local DNS-SD + // service. + // + // A subsequent call to this function updates the previous service. + // It is used to update the TXT record data and/or the port + // number. + // + // The `aTxtData` buffer is not persisted after the return from this + // function. The platform layer MUST not keep the pointer and + // instead copy the content if needed. + + OT_UNUSED_VARIABLE(aPort); + OT_UNUSED_VARIABLE(aTxtData); + OT_UNUSED_VARIABLE(aTxtLength); +} + +OT_TOOL_WEAK void trelDnssdRemoveService(void) +{ + // This function removes any previously registered "_trel._udp" + // service using `platTrelRegisterService()`. Device must stop + // advertising TREL service after this call. +} + +OT_TOOL_WEAK void trelDnssdUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout) +{ + // This function can be used to update the file descriptor sets + // by DNS-SD layer (if needed). + + OT_UNUSED_VARIABLE(aReadFdSet); + OT_UNUSED_VARIABLE(aWriteFdSet); + OT_UNUSED_VARIABLE(aMaxFd); + OT_UNUSED_VARIABLE(aTimeout); +} + +OT_TOOL_WEAK void trelDnssdProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet) +{ + // This function performs processing by DNS-SD (if needed). + + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aReadFdSet); + OT_UNUSED_VARIABLE(aWriteFdSet); +} + +//--------------------------------------------------------------------------------------------------------------------- +// otPlatTrel + +void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort) +{ + OT_UNUSED_VARIABLE(aInstance); + + VerifyOrExit(!IsSystemDryRun()); + + assert(sInitialized); + + VerifyOrExit(!sEnabled); + + PrepareSocket(*aUdpPort); + trelDnssdStartBrowse(); + + sEnabled = true; + +exit: + return; +} + +void otPlatTrelDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + VerifyOrExit(!IsSystemDryRun()); + + assert(sInitialized); + VerifyOrExit(sEnabled); + + close(sSocket); + sSocket = -1; + trelDnssdStopBrowse(); + trelDnssdRemoveService(); + sEnabled = false; + +exit: + return; +} + +void otPlatTrelSend(otInstance * aInstance, + const uint8_t * aUdpPayload, + uint16_t aUdpPayloadLen, + const otSockAddr *aDestSockAddr) +{ + OT_UNUSED_VARIABLE(aInstance); + + VerifyOrExit(!IsSystemDryRun()); + + VerifyOrExit(sEnabled); + + assert(aUdpPayloadLen <= TREL_MAX_PACKET_SIZE); + + // 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. + + if ((sTxPacketQueueTail != NULL) || + (SendPacket(aUdpPayload, aUdpPayloadLen, aDestSockAddr) == OT_ERROR_INVALID_STATE)) + { + EnqueuePacket(aUdpPayload, aUdpPayloadLen, aDestSockAddr); + } + +exit: + return; +} + +void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength) +{ + OT_UNUSED_VARIABLE(aInstance); + VerifyOrExit(!IsSystemDryRun()); + + trelDnssdRegisterService(aPort, aTxtData, aTxtLength); + +exit: + return; +} + +//--------------------------------------------------------------------------------------------------------------------- +// platformTrel system + +void platformTrelInit(const char *aTrelUrl) +{ + OT_UNUSED_VARIABLE(aTrelUrl); + + assert(!sInitialized); + + otLogDebgPlat("[trel] platformTrelInit(aTrelUrl:\"%s\")", aTrelUrl != nullptr ? aTrelUrl : ""); + + InitPacketQueue(); + sInitialized = true; +} + +void platformTrelDeinit(void) +{ + VerifyOrExit(sInitialized); + + otPlatTrelDisable(nullptr); + sInitialized = false; + otLogDebgPlat("[trel] platformTrelDeinit()"); + +exit: + return; +} + +void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout) +{ + assert((aReadFdSet != NULL) && (aWriteFdSet != NULL) && (aMaxFd != NULL) && (aTimeout != NULL)); + + VerifyOrExit(sEnabled); + + FD_SET(sSocket, aReadFdSet); + + if (sTxPacketQueueTail != NULL) + { + FD_SET(sSocket, aWriteFdSet); + } + + if (*aMaxFd < sSocket) + { + *aMaxFd = sSocket; + } + + trelDnssdUpdateFdSet(aReadFdSet, aWriteFdSet, aMaxFd, aTimeout); + +exit: + return; +} + +void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet) +{ + VerifyOrExit(sEnabled); + + if (FD_ISSET(sSocket, aWriteFdSet)) + { + SendQueuedPackets(); + } + + if (FD_ISSET(sSocket, aReadFdSet)) + { + ReceivePacket(sSocket, aInstance); + } + + trelDnssdProcess(aInstance, aReadFdSet, aWriteFdSet); + +exit: + return; +} + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/posix/platform/trel_udp6.cpp b/src/posix/platform/trel_udp6.cpp deleted file mode 100644 index 894f2c420..000000000 --- a/src/posix/platform/trel_udp6.cpp +++ /dev/null @@ -1,935 +0,0 @@ -/* - * 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" - -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET && !defined(__linux__) -#error "netlink socket use is only supported on linux platform" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET -#include -#include -#include -#endif - -#include - -#include "common/code_utils.hpp" -#include "common/logging.hpp" -#include "posix/platform/radio_url.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 - -#define TREL_UNICAST_ADDRESS_PREFIX_LEN 64 -#define TREL_UNICAST_ADDRESS_SCOPE 2 // The unicast address is link-local - -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 bool sInitialized = false; -static bool sEnabled = false; -static int sInterfaceIndex = -1; -static int sMulticastSocket = -1; -static int sSocket = -1; -static uint16_t sUdpPort = 0; -static otIp6Address sInterfaceAddress; - -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET -constexpr static uint64_t kAddInterfaceAddressTimeoutSecs = 10; - -static int sNetlinkSocket = -1; -uint64_t sAddInterfaceAddressRetryTime = 0; -#endif - -#if OPENTHREAD_CONFIG_LOG_PLATFORM -#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_CRIT) -static const char *Ip6AddrToString(const void *aAddress) -{ - static char string[INET6_ADDRSTRLEN]; - return inet_ntop(AF_INET6, aAddress, string, sizeof(string)); -} -#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_CRIT) - -#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG) -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, (uint16_t)(end - cur), "[(len:%d) ", aLength); - VerifyOrExit(cur < end); - - while (aLength-- && (num < kMaxWrite)) - { - cur += snprintf(cur, (uint16_t)(end - cur), "%02x ", *aBuffer++); - VerifyOrExit(cur < end); - - num++; - } - - if (aLength != 0) - { - cur += snprintf(cur, (uint16_t)(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) -#endif // OPENTHREAD_CONFIG_LOG_PLATFORM - -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - -static void UpdateUnicastAddress(const otIp6Address *aUnicastAddress, bool aToAdd) -{ - int ret; - struct rtattr *rta; - - struct - { - struct nlmsghdr nh; - struct ifaddrmsg ifa; - char buf[64]; - } request; - - memset(&request, 0, sizeof(request)); - - request.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - request.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; - request.nh.nlmsg_type = aToAdd ? RTM_NEWADDR : RTM_DELADDR; - request.nh.nlmsg_pid = 0; - request.nh.nlmsg_seq = 0; - - request.ifa.ifa_family = AF_INET6; - request.ifa.ifa_prefixlen = TREL_UNICAST_ADDRESS_PREFIX_LEN; - request.ifa.ifa_flags = IFA_F_NODAD; - request.ifa.ifa_scope = TREL_UNICAST_ADDRESS_SCOPE; - request.ifa.ifa_index = (unsigned int)(sInterfaceIndex); - - rta = reinterpret_cast((reinterpret_cast(&request)) + NLMSG_ALIGN(request.nh.nlmsg_len)); - rta->rta_type = IFA_LOCAL; - rta->rta_len = RTA_LENGTH(sizeof(otIp6Address)); - - memcpy(RTA_DATA(rta), aUnicastAddress, sizeof(otIp6Address)); - - request.nh.nlmsg_len = NLMSG_ALIGN(request.nh.nlmsg_len) + rta->rta_len; - - ret = send(sNetlinkSocket, &request, request.nh.nlmsg_len, 0); - VerifyOrDie(ret != -1, OT_EXIT_ERROR_ERRNO); -} - -static void AddUnicastAddress(const otIp6Address *aUnicastAddress) -{ - otLogDebgPlat("[trel] AddUnicastAddress(%s)", Ip6AddrToString(aUnicastAddress)); - UpdateUnicastAddress(aUnicastAddress, /* aToAdd */ true); - sAddInterfaceAddressRetryTime = otPlatTimeGet() + kAddInterfaceAddressTimeoutSecs * US_PER_S; -} - -static void RemoveUnicastAddress(const otIp6Address *aUnicastAddress) -{ - otLogDebgPlat("[trel] RemoveUnicastAddress(%s)", Ip6AddrToString(aUnicastAddress)); - UpdateUnicastAddress(aUnicastAddress, /* aToAdd */ false); -} - -static void HandleAddInterfaceAddressTimeout(void) -{ - otLogWarnPlat("[trel] Failed to add TREL interface address after %d seconds. Trying to add again.", - kAddInterfaceAddressTimeoutSecs); - RemoveUnicastAddress(&sInterfaceAddress); - AddUnicastAddress(&sInterfaceAddress); - sAddInterfaceAddressRetryTime = otPlatTimeGet() + kAddInterfaceAddressTimeoutSecs * US_PER_S; -} - -static void ProcessNetifAddrEvent(struct nlmsghdr *aNetlinkMessage) -{ - struct ifaddrmsg *ifaddr = reinterpret_cast(NLMSG_DATA(aNetlinkMessage)); - size_t rtaLength; - - VerifyOrExit(ifaddr->ifa_index == static_cast(sInterfaceIndex) && ifaddr->ifa_family == AF_INET6); - - rtaLength = IFA_PAYLOAD(aNetlinkMessage); - - for (struct rtattr *rta = reinterpret_cast(IFA_RTA(ifaddr)); RTA_OK(rta, rtaLength); - rta = RTA_NEXT(rta, rtaLength)) - { - if (rta->rta_type != IFA_ADDRESS) - { - continue; - } - - if (memcmp(&sInterfaceAddress, RTA_DATA(rta), sizeof(sInterfaceAddress)) != 0) - { - continue; - } - - if (aNetlinkMessage->nlmsg_type == RTM_NEWADDR) - { - otLogInfoPlat("[trel] Interface address added successfully."); - sAddInterfaceAddressRetryTime = 0; - } - else if (aNetlinkMessage->nlmsg_type == RTM_DELADDR) - { - if (sAddInterfaceAddressRetryTime == 0) - { - otLogWarnPlat("[trel] Interface address removed unexpectedly."); - sAddInterfaceAddressRetryTime = otPlatTimeGet() + kAddInterfaceAddressTimeoutSecs * US_PER_S; - } - } - } - -exit: - return; -} - -static void ReceiveNetlinkMessage(void) -{ - const size_t kMaxNetLinkBufSize = 8192; - ssize_t len; - union - { - nlmsghdr mHeader; - uint8_t mBuffer[kMaxNetLinkBufSize]; - } msgBuffer; - - len = recv(sNetlinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0); - if (len < 0) - { - otLogCritPlat("failed to receive netlink message: %s", strerror(errno)); - ExitNow(); - } - - for (struct nlmsghdr *header = &msgBuffer.mHeader; NLMSG_OK(header, static_cast(len)); - header = NLMSG_NEXT(header, len)) - { - switch (header->nlmsg_type) - { - case RTM_NEWADDR: - case RTM_DELADDR: - ProcessNetifAddrEvent(header); - break; - case NLMSG_ERROR: - { - struct nlmsgerr *errMsg = reinterpret_cast(NLMSG_DATA(header)); - - if (errMsg->error != 0) - { - otLogWarnPlat("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error); - } - break; - } - default: - break; - } - } - -exit: - return; -} - -#else // OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - -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] AddUnicastAddress(%s)", Ip6AddrToString(aUnicastAddress)); - - mgmtFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketNonBlock); - 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); - - if (!(ret == 0 || errno == EALREADY || errno == EEXIST)) - { - otLogCritPlat("[trel] Failed to add unicast address %s on TREL netif \"%s\"", Ip6AddrToString(aUnicastAddress), - sInterfaceName); - DieNow(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] RemoveUnicastAddress(%s)", Ip6AddrToString(aUnicastAddress)); - - mgmtFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketNonBlock); - 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); -} - -#endif // OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - -static void PrepareSocket(void) -{ - int val; - struct sockaddr_in6 sockAddr; - uint64_t startTime; - bool isSocketBound = false; - - otLogDebgPlat("[trel] PrepareSocket()"); - - sSocket = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, 0, kSocketNonBlock); - 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) - { - isSocketBound = true; - break; - } - - if (errno == EADDRNOTAVAIL) - { - continue; - } - - otLogCritPlat("[trel] Failed to bind socket to %s (port %d) on TREL netif \"%s\"", - Ip6AddrToString(&sInterfaceAddress), sUdpPort, sInterfaceName); - DieNow(OT_EXIT_ERROR_ERRNO); - } - - if (!isSocketBound) - { - otLogCritPlat("[trel] Timed out waiting for address %s to become available for binding on TREL " - "netif \"%s\" - timeout %lu (ms)", - Ip6AddrToString(&sInterfaceAddress), sInterfaceName, TREL_SOCKET_BIND_MAX_WAIT_TIME_MSEC); - DieNow(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] 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] 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] 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)); - - if (sEnabled) - { - 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] 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] EnqueuePacket(%s) - %s", Ip6AddrToString(aDestAddress), BufferToString(aBuffer, aLength)); - -exit: - return error; -} - -//--------------------------------------------------------------------------------------------------------------------- -// otPlatTrelUdp6 - -void otPlatTrelUdp6Init(otInstance *aInstance, const otIp6Address *aUnicastAddress, uint16_t aUdpPort) -{ - OT_UNUSED_VARIABLE(aInstance); - - int val; - struct sockaddr_in6 sockAddr; - - VerifyOrExit(sEnabled); - - otLogDebgPlat("[trel] otPlatTrelUdp6Init(%s, port:%d)", Ip6AddrToString(aUnicastAddress), aUdpPort); - - sUdpPort = aUdpPort; - sInterfaceAddress = *aUnicastAddress; - sInterfaceIndex = (int)if_nametoindex(sInterfaceName); - - if (sInterfaceIndex <= 0) - { - otLogCritPlat("[trel] Failed to find index of TREL netif \"%s\"", sInterfaceName); - DieNow(OT_EXIT_ERROR_ERRNO); - } - -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - { - struct sockaddr_nl addr; - - sNetlinkSocket = SocketWithCloseExec(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, kSocketNonBlock); - VerifyOrDie(sNetlinkSocket >= 0, OT_EXIT_ERROR_ERRNO); - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_groups = RTMGRP_IPV6_IFADDR; - - VerifyOrDie(bind(sNetlinkSocket, reinterpret_cast(&addr), sizeof(addr)) == 0, - OT_EXIT_ERROR_ERRNO); - } -#endif - - RemoveUnicastAddress(aUnicastAddress); - 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; - - if (bind(sMulticastSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) == -1) - { - otLogCritPlat("[trel] Failed to bind multicast socket to any address on TREL netif \"%s\"", sInterfaceName); - DieNow(OT_EXIT_ERROR_ERRNO); - } - - PrepareSocket(); - -exit: - return; -} - -void otPlatTrelUdp6UpdateAddress(otInstance *aInstance, const otIp6Address *aUnicastAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - VerifyOrExit(sEnabled); - - assert(sSocket >= 0); - - otLogDebgPlat("[trel] otPlatTrelUdp6UpdateAddress(%s)", Ip6AddrToString(aUnicastAddress)); - - VerifyOrExit(memcmp(aUnicastAddress, &sInterfaceAddress, sizeof(otIp6Address)) != 0); - - close(sSocket); - RemoveUnicastAddress(&sInterfaceAddress); - - sInterfaceAddress = *aUnicastAddress; - AddUnicastAddress(aUnicastAddress); - - PrepareSocket(); - -exit: - return; -} - -void otPlatTrelUdp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aMulticastAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - struct ipv6_mreq mr; - - VerifyOrExit(sEnabled); - - 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] otPlatTrelUdp6SubscribeMulticastAddress(%s)", Ip6AddrToString(aMulticastAddress)); - -exit: - return; -} - -otError otPlatTrelUdp6SendTo(otInstance * aInstance, - const uint8_t * aBuffer, - uint16_t aLength, - const otIp6Address *aDestAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - - VerifyOrExit(sEnabled); - - assert(aLength <= TREL_MAX_PACKET_SIZE); - - otLogDebgPlat("[trel] 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; - } - } - -exit: - return error; -} - -otError otPlatTrelUdp6SetTestMode(otInstance *aInstance, bool aEnable) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - - VerifyOrExit(aEnable != sEnabled); - - if (aEnable) - { - VerifyOrExit(sInitialized, error = OT_ERROR_FAILED); - } - - sEnabled = aEnable; - - if (!sEnabled) - { - InitPacketQueue(); - } - -exit: - return error; -} - -//--------------------------------------------------------------------------------------------------------------------- -// platformTrel system - -void platformTrelInit(const char *aTrelUrl) -{ - assert(!sInitialized); - - if (aTrelUrl != NULL) - { - ot::Posix::RadioUrl url(aTrelUrl); - - strncpy(sInterfaceName, url.GetPath(), sizeof(sInterfaceName) - 1); - } - else - { - strncpy(sInterfaceName, OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME, sizeof(sInterfaceName) - 1); - } - - sInterfaceName[sizeof(sInterfaceName) - 1] = 0; - otLogDebgPlat("[trel] platformTrelInit(InterfaceName:\"%s\")", sInterfaceName); - - InitPacketQueue(); - - // Disable trel platform when interface name is empty. - sInitialized = (sInterfaceName[0] != '\0'); - sEnabled = sInitialized; -} - -void platformTrelDeinit(void) -{ - VerifyOrExit(sInitialized); - - if (sSocket != -1) - { - close(sSocket); - } - - if (sMulticastSocket != -1) - { - close(sMulticastSocket); - } - - if (!otIp6IsAddressUnspecified(&sInterfaceAddress)) - { - RemoveUnicastAddress(&sInterfaceAddress); - } - -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - if (sNetlinkSocket != -1) - { - close(sNetlinkSocket); - sNetlinkSocket = -1; - } -#endif - - sInitialized = false; - sEnabled = false; - - otLogDebgPlat("[trel] platformTrelDeinit()"); -exit: - return; -} - -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)); -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - VerifyOrExit(sNetlinkSocket >= 0); -#endif - - FD_SET(sMulticastSocket, aReadFdSet); - FD_SET(sSocket, aReadFdSet); -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - FD_SET(sNetlinkSocket, aReadFdSet); -#endif - - if (sTxPacketQueueTail != NULL) - { - FD_SET(sSocket, aWriteFdSet); - } - - if (*aMaxFd < sMulticastSocket) - { - *aMaxFd = sMulticastSocket; - } - - if (*aMaxFd < sSocket) - { - *aMaxFd = sSocket; - } - -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - if (*aMaxFd < sNetlinkSocket) - { - *aMaxFd = sNetlinkSocket; - } - - if (sAddInterfaceAddressRetryTime > 0) - { - uint64_t now = otPlatTimeGet(); - - if (sAddInterfaceAddressRetryTime > now) - { - uint64_t remain = sAddInterfaceAddressRetryTime - now; - - if (remain < - (static_cast(aTimeout->tv_sec) * US_PER_S + static_cast(aTimeout->tv_usec))) - { - aTimeout->tv_sec = static_cast(remain / US_PER_S); - aTimeout->tv_usec = static_cast(remain % US_PER_S); - } - } - else - { - aTimeout->tv_sec = 0; - aTimeout->tv_usec = 0; - } - } -#endif - -exit: - return; -} - -void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet) -{ - VerifyOrExit((sSocket >= 0) && (sMulticastSocket >= 0)); -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - VerifyOrExit(sNetlinkSocket >= 0); -#endif - - if (FD_ISSET(sSocket, aWriteFdSet)) - { - SendQueuedPackets(); - } - - if (FD_ISSET(sSocket, aReadFdSet)) - { - ReceivePacket(sSocket, aInstance); - } - - if (FD_ISSET(sMulticastSocket, aReadFdSet)) - { - ReceivePacket(sMulticastSocket, aInstance); - } - -#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - if (FD_ISSET(sNetlinkSocket, aReadFdSet)) - { - ReceiveNetlinkMessage(); - } - - if (sAddInterfaceAddressRetryTime > 0) - { - uint64_t now = otPlatTimeGet(); - - if (sAddInterfaceAddressRetryTime <= now) - { - sAddInterfaceAddressRetryTime = 0; - - HandleAddInterfaceAddressTimeout(); - } - } -#endif - -exit: - return; -} - -#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/tests/toranj/openthread-core-toranj-config-posix.h b/tests/toranj/openthread-core-toranj-config-posix.h index 8a2c58fbb..ecf3ba853 100644 --- a/tests/toranj/openthread-core-toranj-config-posix.h +++ b/tests/toranj/openthread-core-toranj-config-posix.h @@ -61,29 +61,6 @@ */ #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1 -/** - * @def OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME - * - * Defines the default interface name used for TREL UDP6 platform. Empty string disables TREL platform. - * - */ -#define OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME "trel" - -/** - * @def OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET - * - * Defines whether the TREL UDP6 platform uses netlink socket to add/remove addresses on the TREL netif or `ioctl()` - * command. - * - * When netlink is used Duplicate Address Detection (DAD) is disabled when a new address is added on the netif. - * - */ -#ifdef __linux__ -#define OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET 1 -#else -#define OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET 0 -#endif - /** * @def OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE * diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 466ebb905..ce6ab2a44 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -446,29 +446,21 @@ OT_TOOL_WEAK void otPlatOtnsStatus(const char *) #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE - -OT_TOOL_WEAK void otPlatTrelUdp6Init(otInstance *, const otIp6Address *, uint16_t) +OT_TOOL_WEAK void otPlatTrelEnable(otInstance *, uint16_t *) { } -OT_TOOL_WEAK void otPlatTrelUdp6UpdateAddress(otInstance *, const otIp6Address *) +OT_TOOL_WEAK void otPlatTrelDisable(otInstance *) { } -OT_TOOL_WEAK void otPlatTrelUdp6SubscribeMulticastAddress(otInstance *, const otIp6Address *) +OT_TOOL_WEAK void otPlatTrelSend(otInstance *, const uint8_t *, uint16_t, const otSockAddr *) { } -OT_TOOL_WEAK otError otPlatTrelUdp6SendTo(otInstance *, const uint8_t *, uint16_t, const otIp6Address *) +OT_TOOL_WEAK void otPlatTrelRegisterService(otInstance *, uint16_t, const uint8_t *, uint8_t) { - return OT_ERROR_ABORT; } - -OT_TOOL_WEAK otError otPlatTrelUdp6SetTestMode(otInstance *, bool) -{ - return OT_ERROR_NOT_IMPLEMENTED; -} - #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE