From 2d36853fe6943d1ce7d67d5c3d0d7235e2d0d66a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 18 Dec 2019 15:50:50 -0800 Subject: [PATCH] [trel] implementing Thread Radio Encapsulation Link (TREL) (#4440) This commit implements TREL protocol to encapsulate and exchange IEEE 802.15.4 frames over a packet switched network (e.g., a wireless LAN). This commit also provides a TREL IPv6/UDP interface implementation (using link-local IPv6 unicast/multicast addresses to communicate over a subnet). It defines a platform abstraction (`otPlatTrelUdp6*`) for TREL over IPv6/UDP. This commit also provides an implementation of the TREL platform under `examples/posix` focusing on simulating the behavior of the link. --- Android.mk | 4 +- examples/platforms/simulation/CMakeLists.txt | 1 + examples/platforms/simulation/Makefile.am | 1 + .../simulation/platform-simulation.h | 39 ++ examples/platforms/simulation/system.c | 12 + examples/platforms/simulation/trel.c | 345 +++++++++++++ include/Makefile.am | 1 + include/openthread/BUILD.gn | 1 + include/openthread/platform/trel-udp6.h | 154 ++++++ src/core/BUILD.gn | 8 +- src/core/CMakeLists.txt | 4 +- src/core/Makefile.am | 8 +- src/core/common/instance.hpp | 5 + src/core/mac/mac.hpp | 2 +- src/core/mac/mac_frame.cpp | 2 +- src/core/mac/mac_links.hpp | 5 +- src/core/radio/trel.cpp | 64 --- src/core/radio/trel.hpp | 106 ---- src/core/radio/trel_interface.cpp | 137 +++++ src/core/radio/trel_interface.hpp | 127 +++++ src/core/radio/trel_link.cpp | 475 ++++++++++++++++++ src/core/radio/trel_link.hpp | 252 ++++++++++ src/core/radio/trel_packet.cpp | 140 ++++++ src/core/radio/trel_packet.hpp | 373 ++++++++++++++ src/core/thread/topology.hpp | 5 + tests/unit/test_platform.cpp | 43 ++ 26 files changed, 2135 insertions(+), 179 deletions(-) create mode 100644 examples/platforms/simulation/trel.c create mode 100644 include/openthread/platform/trel-udp6.h delete mode 100644 src/core/radio/trel.cpp delete mode 100644 src/core/radio/trel.hpp create mode 100644 src/core/radio/trel_interface.cpp create mode 100644 src/core/radio/trel_interface.hpp create mode 100644 src/core/radio/trel_link.cpp create mode 100644 src/core/radio/trel_link.hpp create mode 100644 src/core/radio/trel_packet.cpp create mode 100644 src/core/radio/trel_packet.hpp diff --git a/Android.mk b/Android.mk index 9ef3f6ee5..60348b113 100644 --- a/Android.mk +++ b/Android.mk @@ -251,7 +251,9 @@ LOCAL_SRC_FILES := \ src/core/radio/radio.cpp \ src/core/radio/radio_callbacks.cpp \ src/core/radio/radio_platform.cpp \ - src/core/radio/trel.cpp \ + src/core/radio/trel_interface.cpp \ + src/core/radio/trel_link.cpp \ + src/core/radio/trel_packet.cpp \ src/core/thread/address_resolver.cpp \ src/core/thread/announce_begin_server.cpp \ src/core/thread/announce_sender.cpp \ diff --git a/examples/platforms/simulation/CMakeLists.txt b/examples/platforms/simulation/CMakeLists.txt index c67ae8a68..9352fba1e 100644 --- a/examples/platforms/simulation/CMakeLists.txt +++ b/examples/platforms/simulation/CMakeLists.txt @@ -69,6 +69,7 @@ add_library(openthread-simulation radio.c spi-stubs.c system.c + trel.c uart.c virtual_time/alarm-sim.c virtual_time/platform-sim.c diff --git a/examples/platforms/simulation/Makefile.am b/examples/platforms/simulation/Makefile.am index 348c6b331..a36329b52 100644 --- a/examples/platforms/simulation/Makefile.am +++ b/examples/platforms/simulation/Makefile.am @@ -52,6 +52,7 @@ PLATFORM_SOURCES = \ radio.c \ spi-stubs.c \ system.c \ + trel.c \ uart.c \ virtual_time/alarm-sim.c \ virtual_time/platform-sim.c \ diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h index f9e7f9810..079305384 100644 --- a/examples/platforms/simulation/platform-simulation.h +++ b/examples/platforms/simulation/platform-simulation.h @@ -231,4 +231,43 @@ void otSimSendUartWriteEvent(const uint8_t *aData, uint16_t aLength); */ bool platformRadioIsTransmitPending(void); +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +/** + * This function initializes the TREL service. + * + * @param[in] aSpeedUpFactor The time speed-up factor. + * + */ +void platformTrelInit(uint32_t aSpeedUpFactor); + +/** + * This function shuts down the TREL service. + * + */ +void platformTrelDeinit(void); + +/** + * This function updates the file descriptor sets with file descriptors used by the TREL. + * + * @param[inout] aReadFdSet A pointer to the read file descriptors. + * @param[inout] aWriteFdSet A pointer to the write file descriptors. + * @param[inout] aTimeout A pointer to the timeout. + * @param[inout] aMaxFd A pointer to the max file descriptor. + * + */ +void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd); + +/** + * This function performs TREL processing. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aReadFdSet A pointer to the read file descriptors. + * @param[in] aWriteFdSet A pointer to the write file descriptors. + * + */ +void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet); + +#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + #endif // PLATFORM_SIMULATION_H_ diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index b86f712ca..82e4bc17b 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -116,6 +116,9 @@ void otSysInit(int aArgCount, char *aArgVector[]) platformAlarmInit(speedUpFactor); platformRadioInit(); +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + platformTrelInit(speedUpFactor); +#endif platformRandomInit(); } @@ -127,6 +130,9 @@ bool otSysPseudoResetWasRequested(void) void otSysDeinit(void) { platformRadioDeinit(); +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + platformTrelDeinit(); +#endif } void otSysProcessDrivers(otInstance *aInstance) @@ -145,6 +151,9 @@ void otSysProcessDrivers(otInstance *aInstance) platformUartUpdateFdSet(&read_fds, &write_fds, &error_fds, &max_fd); platformRadioUpdateFdSet(&read_fds, &write_fds, &max_fd); platformAlarmUpdateTimeout(&timeout); +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd); +#endif if (otTaskletsArePending(aInstance)) { @@ -166,6 +175,9 @@ void otSysProcessDrivers(otInstance *aInstance) } platformAlarmProcess(aInstance); +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + platformTrelProcess(aInstance, &read_fds, &write_fds); +#endif if (gTerminate) { diff --git a/examples/platforms/simulation/trel.c b/examples/platforms/simulation/trel.c new file mode 100644 index 000000000..a77a1c315 --- /dev/null +++ b/examples/platforms/simulation/trel.c @@ -0,0 +1,345 @@ +/* + * 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. + */ + +#include "platform-simulation.h" + +#include +#include + +#include "utils/code_utils.h" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +// Change DEBUG_LOG to all extra logging +#define DEBUG_LOG 0 + +// The IPv4 group for receiving +#define TREL_SIM_GROUP "224.0.0.116" +#define TREL_SIM_PORT 9200 + +#define TREL_MAX_PACKET_SIZE 1800 + +#define TREL_MAX_PENDING_TX 5 + +typedef struct PendingTx +{ + uint8_t mPacketBuffer[TREL_MAX_PACKET_SIZE]; + uint16_t mPacketLength; + otIp6Address mDestIp6Address; +} PendingTx; + +static uint8_t sNumPendingTx = 0; +static PendingTx sPendingTx[TREL_MAX_PENDING_TX]; + +static int sTxFd = -1; +static int sRxFd = -1; +static uint16_t sPortOffset = 0; +static bool sEnabled = true; + +#if DEBUG_LOG +static void dumpBuffer(const uint8_t *aBuffer, uint16_t aLength) +{ + fprintf(stderr, "[ (len:%d) ", aLength); + + while (aLength--) + { + fprintf(stderr, "%02x ", *aBuffer++); + } + + fprintf(stderr, "]"); +} +#endif + +static void initFds(void) +{ + int fd; + int one = 1; + struct sockaddr_in sockaddr; + struct ip_mreqn mreq; + + memset(&sockaddr, 0, sizeof(sockaddr)); + + otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)")); + + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset + gNodeId)); + sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)) != -1, + perror("setsockopt(sTxFd, IP_MULTICAST_IF)")); + + otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)) != -1, + perror("setsockopt(sTxFd, IP_MULTICAST_LOOP)")); + + otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sTxFd)")); + + // Tx fd is successfully initialized. + sTxFd = fd; + + otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sRxFd)")); + + otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1, + perror("setsockopt(sRxFd, SO_REUSEADDR)")); + otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1, + perror("setsockopt(sRxFd, SO_REUSEPORT)")); + + memset(&mreq, 0, sizeof(mreq)); + inet_pton(AF_INET, TREL_SIM_GROUP, &mreq.imr_multiaddr); + + // Always use loopback device to send simulation packets. + mreq.imr_address.s_addr = inet_addr("127.0.0.1"); + + otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)) != -1, + perror("setsockopt(sRxFd, IP_MULTICAST_IF)")); + otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1, + perror("setsockopt(sRxFd, IP_ADD_MEMBERSHIP)")); + + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset)); + sockaddr.sin_addr.s_addr = inet_addr(TREL_SIM_GROUP); + + otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)")); + + // Rx fd is successfully initialized. + sRxFd = fd; + +exit: + if (sRxFd == -1 || sTxFd == -1) + { + exit(EXIT_FAILURE); + } +} + +static void deinitFds(void) +{ + if (sRxFd != -1) + { + close(sRxFd); + } + + if (sTxFd != -1) + { + close(sTxFd); + } +} + +void sendPendingTxPackets(void) +{ + ssize_t rval; + struct sockaddr_in sockaddr; + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + inet_pton(AF_INET, TREL_SIM_GROUP, &sockaddr.sin_addr); + + sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset)); + + for (uint8_t i = 0; i < sNumPendingTx; i++) + { +#if DEBUG_LOG + fprintf(stderr, "\n[trel-udp] Sending packet (num:%d)", i); + dumpBuffer(sPendingTx[i].mPacketBuffer, sPendingTx[i].mPacketLength); + fprintf(stderr, "\n"); +#endif + + rval = sendto(sTxFd, sPendingTx[i].mPacketBuffer, sPendingTx[i].mPacketLength, 0, (struct sockaddr *)&sockaddr, + sizeof(sockaddr)); + + if (rval < 0) + { + perror("sendto(sTxFd)"); + exit(EXIT_FAILURE); + } + } + + sNumPendingTx = 0; +} + +//--------------------------------------------------------------------------------------------------------------------- +// otPlatTrel + +void otPlatTrelUdp6Init(otInstance *aInstance, const otIp6Address *aUnicastAddress, uint16_t aPort) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aUnicastAddress); + OT_UNUSED_VARIABLE(aPort); +} + +void otPlatTrelUdp6UpdateAddress(otInstance *aInstance, const otIp6Address *aUnicastAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aUnicastAddress); +} + +void otPlatTrelUdp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aMulticastAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMulticastAddress); +} + +otError otPlatTrelUdp6SendTo(otInstance * aInstance, + const uint8_t * aBuffer, + uint16_t aLength, + const otIp6Address *aDestAddress) +{ + otError error = OT_ERROR_NONE; + + OT_UNUSED_VARIABLE(aInstance); + + otEXPECT(sEnabled); + otEXPECT_ACTION(sNumPendingTx < TREL_MAX_PENDING_TX, error = OT_ERROR_ABORT); + + memcpy(sPendingTx[sNumPendingTx].mPacketBuffer, aBuffer, aLength); + sPendingTx[sNumPendingTx].mPacketLength = aLength; + memcpy(&sPendingTx[sNumPendingTx].mDestIp6Address, aDestAddress, sizeof(otIp6Address)); + sNumPendingTx++; + +exit: + return error; +} + +otError otPlatTrelUdp6SetTestMode(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + + sEnabled = aEnable; + + if (!sEnabled) + { + sNumPendingTx = 0; + } + + return OT_ERROR_NONE; +} + +//--------------------------------------------------------------------------------------------------------------------- +// platformTrel system + +void platformTrelInit(uint32_t aSpeedUpFactor) +{ + char *str; + + str = getenv("PORT_OFFSET"); + + if (str != NULL) + { + char *endptr; + + sPortOffset = (uint16_t)strtol(str, &endptr, 0); + + if (*endptr != '\0') + { + fprintf(stderr, "\nInvalid PORT_OFFSET: %s\n", str); + exit(EXIT_FAILURE); + } + + sPortOffset *= (MAX_NETWORK_SIZE + 1); + } + + initFds(); + + OT_UNUSED_VARIABLE(aSpeedUpFactor); +} + +void platformTrelDeinit(void) +{ + deinitFds(); +} + +void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd) +{ + OT_UNUSED_VARIABLE(aTimeout); + + // Always ready to receive + if (aReadFdSet != NULL) + { + FD_SET(sRxFd, aReadFdSet); + + if (aMaxFd != NULL && *aMaxFd < sRxFd) + { + *aMaxFd = sRxFd; + } + } + + if ((aWriteFdSet != NULL) && (sNumPendingTx > 0)) + { + FD_SET(sTxFd, aWriteFdSet); + + if (aMaxFd != NULL && *aMaxFd < sTxFd) + { + *aMaxFd = sTxFd; + } + } +} + +void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet) +{ + if (FD_ISSET(sTxFd, aWriteFdSet) && (sNumPendingTx > 0)) + { + sendPendingTxPackets(); + } + + if (FD_ISSET(sRxFd, aReadFdSet)) + { + uint8_t rxPacketBuffer[TREL_MAX_PACKET_SIZE]; + uint16_t rxPacketLength; + ssize_t rval; + + rval = recvfrom(sRxFd, (char *)rxPacketBuffer, sizeof(rxPacketBuffer), 0, NULL, NULL); + + if (rval < 0) + { + perror("recvfrom(sRxFd)"); + exit(EXIT_FAILURE); + } + + rxPacketLength = (uint16_t)(rval); + +#if DEBUG_LOG + fprintf(stderr, "\n[trel-udp6] recvdPacket()"); + dumpBuffer(rxPacketBuffer, rxPacketLength); + fprintf(stderr, "\n"); +#endif + if (sEnabled) + { + otPlatTrelUdp6HandleReceived(aInstance, rxPacketBuffer, rxPacketLength); + } + } +} + +// This is added for RCP build to be built ok +OT_TOOL_WEAK void otPlatTrelUdp6HandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aBuffer); + OT_UNUSED_VARIABLE(aLength); + + assert(false); +} + +#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/include/Makefile.am b/include/Makefile.am index bd61c319e..460a1c6e5 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -100,6 +100,7 @@ ot_platform_headers = \ openthread/platform/settings.h \ openthread/platform/messagepool.h \ openthread/platform/toolchain.h \ + openthread/platform/trel-udp6.h \ $(NULL) ot_platformdir = $(includedir)/openthread/platform diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index 731efe7d9..ae91fa73f 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -105,6 +105,7 @@ source_set("openthread") { "platform/spi-slave.h", "platform/time.h", "platform/toolchain.h", + "platform/trel-udp6.h", "platform/uart.h", "platform/udp.h", "random_crypto.h", diff --git a/include/openthread/platform/trel-udp6.h b/include/openthread/platform/trel-udp6.h new file mode 100644 index 000000000..9bc834e82 --- /dev/null +++ b/include/openthread/platform/trel-udp6.h @@ -0,0 +1,154 @@ +/* + * 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_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/src/core/BUILD.gn b/src/core/BUILD.gn index eaaac1e80..14edd568b 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -496,8 +496,12 @@ openthread_core_files = [ "radio/radio.hpp", "radio/radio_callbacks.cpp", "radio/radio_platform.cpp", - "radio/trel.cpp", - "radio/trel.hpp", + "radio/trel_interface.cpp", + "radio/trel_interface.hpp", + "radio/trel_link.cpp", + "radio/trel_link.hpp", + "radio/trel_packet.cpp", + "radio/trel_packet.hpp", "thread/address_resolver.cpp", "thread/address_resolver.hpp", "thread/announce_begin_server.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9668b2395..b9ea7c4b6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -144,7 +144,9 @@ set(COMMON_SOURCES radio/radio.cpp radio/radio_callbacks.cpp radio/radio_platform.cpp - radio/trel.cpp + radio/trel_interface.cpp + radio/trel_link.cpp + radio/trel_packet.cpp thread/address_resolver.cpp thread/announce_begin_server.cpp thread/announce_sender.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 3403d7049..9670f0293 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -221,7 +221,9 @@ SOURCES_COMMON = \ radio/radio.cpp \ radio/radio_callbacks.cpp \ radio/radio_platform.cpp \ - radio/trel.cpp \ + radio/trel_interface.cpp \ + radio/trel_link.cpp \ + radio/trel_packet.cpp \ thread/address_resolver.cpp \ thread/announce_begin_server.cpp \ thread/announce_sender.cpp \ @@ -457,7 +459,9 @@ HEADERS_COMMON = \ net/tcp.hpp \ net/udp6.hpp \ radio/radio.hpp \ - radio/trel.hpp \ + radio/trel_interface.hpp \ + radio/trel_link.hpp \ + radio/trel_packet.hpp \ thread/address_resolver.hpp \ thread/announce_begin_server.hpp \ thread/announce_sender.hpp \ diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index b328cb829..2cb432475 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -515,6 +515,11 @@ template <> inline Trel::Link &Instance::Get(void) { return mThreadNetif.mMac.mLinks.mTrel; } + +template <> inline Trel::Interface &Instance::Get(void) +{ + return mThreadNetif.mMac.mLinks.mTrel.mInterface; +} #endif #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index 0e671b0e1..d0400cd4c 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -50,7 +50,7 @@ #include "mac/mac_links.hpp" #include "mac/mac_types.hpp" #include "mac/sub_mac.hpp" -#include "radio/trel.hpp" +#include "radio/trel_link.hpp" #include "thread/key_manager.hpp" #include "thread/link_quality.hpp" diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 2fe3ce57f..84686a01a 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -37,7 +37,7 @@ #include "common/code_utils.hpp" #include "common/debug.hpp" -#include "radio/trel.hpp" +#include "radio/trel_link.hpp" #if !OPENTHREAD_RADIO || OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE #include "crypto/aes_ccm.hpp" #endif diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp index f03374d59..b69827d42 100644 --- a/src/core/mac/mac_links.hpp +++ b/src/core/mac/mac_links.hpp @@ -41,7 +41,7 @@ #include "mac/mac_frame.hpp" #include "mac/mac_types.hpp" #include "mac/sub_mac.hpp" -#include "radio/trel.hpp" +#include "radio/trel_link.hpp" namespace ot { namespace Mac { @@ -377,6 +377,9 @@ public: mSubMac.SetExtAddress(aExtAddress); #else mExtAddress = aExtAddress; +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrel.HandleExtAddressChange(); +#endif #endif } diff --git a/src/core/radio/trel.cpp b/src/core/radio/trel.cpp deleted file mode 100644 index a51f933ec..000000000 --- a/src/core/radio/trel.cpp +++ /dev/null @@ -1,64 +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 Thread Radio Encapsulation Link (TREL). - */ - -#include "trel.hpp" - -#include "common/code_utils.hpp" -#include "common/instance.hpp" -#include "common/locator-getters.hpp" - -#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE - -namespace ot { -namespace Trel { - -Link::Link(Instance &aInstance) - : InstanceLocator(aInstance) -{ - memset(&mTxFrame, 0, sizeof(mTxFrame)); - mTxFrame.mPsdu = &mFrameBuffer[kHeaderSize]; - mTxFrame.SetLength(0); -#if OPENTHREAD_CONFIG_MULTI_RADIO - mTxFrame.SetRadioType(Mac::kRadioTypeTrel); -#endif -} - -void Link::Send(void) -{ - Get().RecordFrameTransmitStatus(mTxFrame, NULL, OT_ERROR_ABORT, 0, false); - Get().HandleTransmitDone(mTxFrame, NULL, OT_ERROR_ABORT); -} - -} // namespace Trel -} // namespace ot - -#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/radio/trel.hpp b/src/core/radio/trel.hpp deleted file mode 100644 index 025be8275..000000000 --- a/src/core/radio/trel.hpp +++ /dev/null @@ -1,106 +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 includes definitions for Thread Radio Encapsulation Link (TREL). - */ - -#ifndef TREL_HPP_ -#define TREL_HPP_ - -#include "openthread-core-config.h" - -#include "common/locator.hpp" -#include "mac/mac_frame.hpp" -#include "mac/mac_types.hpp" - -#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE - -namespace ot { -namespace Trel { - -/** - * @addtogroup core-trel - * - * @brief - * This module includes definitions for Thread Radio Encapsulation Link (TREL) - * - * @{ - * - */ - -/** - * This class represents a Thread Radio Encapsulation Link (TREL). - * - */ -class Link : public InstanceLocator -{ - friend class Instance; - -public: - enum - { - kMtuSize = 1600, - kFcsSize = 0, - }; - - explicit Link(Instance &aInstance); - - void SetPanId(Mac::PanId) {} - - void Enable(void) {} - void Disable(void) {} - - void Sleep(void) {} - void Receive(uint8_t) {} - void Send(void); - - Mac::TxFrame &GetTransmitFrame(void) { return mTxFrame; } - -private: - enum - { - kHeaderSize = 10, - }; - - Mac::TxFrame mTxFrame; - uint8_t mFrameBuffer[kHeaderSize + kMtuSize]; -}; - -/** - * @} - * - */ - -} // namespace Trel -} // namespace ot - -#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE - -#endif // TREL_HPP_ diff --git a/src/core/radio/trel_interface.cpp b/src/core/radio/trel_interface.cpp new file mode 100644 index 000000000..5de95be06 --- /dev/null +++ b/src/core/radio/trel_interface.cpp @@ -0,0 +1,137 @@ +/* + * 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 Thread Radio Encapsulation Link (TREL) interface. + */ + +#include "trel_interface.hpp" + +#include + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/locator-getters.hpp" +#include "common/logging.hpp" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +namespace ot { +namespace Trel { + +Interface::Interface(Instance &aInstance) + : InstanceLocator(aInstance) + , mInitialized(false) +{ +} + +void Interface::Init(void) +{ + Ip6::Address ip6Address; + + VerifyOrExit(!mInitialized); + + ip6Address.SetToLinkLocalAddress(Get().GetExtAddress()); + otPlatTrelUdp6Init(&GetInstance(), &ip6Address, kUdpPort); + + CreateMulticastIp6Address(ip6Address); + otPlatTrelUdp6SubscribeMulticastAddress(&GetInstance(), &ip6Address); + + mInitialized = true; + +exit: + return; +} + +void Interface::HandleExtAddressChange(void) +{ + Ip6::Address ip6Address; + + VerifyOrExit(mInitialized); + + ip6Address.SetToLinkLocalAddress(Get().GetExtAddress()); + otPlatTrelUdp6UpdateAddress(&GetInstance(), &ip6Address); + +exit: + return; +} + +otError Interface::Send(const Packet &aPacket) +{ + Ip6::Address destIp6Address; + + switch (aPacket.GetHeader().GetType()) + { + case Header::kTypeBroadcast: + CreateMulticastIp6Address(destIp6Address); + break; + + case Header::kTypeUnicast: + case Header::kTypeAck: + destIp6Address.SetToLinkLocalAddress(aPacket.GetHeader().GetDestination()); + break; + } + + return otPlatTrelUdp6SendTo(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &destIp6Address); +} + +void Interface::HandleReceived(uint8_t *aBuffer, uint16_t aLength) +{ + mRxPacket.Init(aBuffer, aLength); + Get().ProcessReceivedPacket(mRxPacket); +} + +void Interface::CreateMulticastIp6Address(Ip6::Address &aIp6Address) +{ + // Use ff02::1 (Link-local All Nodes multicast address). + aIp6Address.SetToLinkLocalAllNodesMulticast(); +} + +} // namespace Trel +} // namespace ot + +extern "C" void otPlatTrelUdp6HandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength) +{ + ot::Instance &instance = *static_cast(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_ERROR_NOT_IMPLEMENTED; +} + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/radio/trel_interface.hpp b/src/core/radio/trel_interface.hpp new file mode 100644 index 000000000..843b54612 --- /dev/null +++ b/src/core/radio/trel_interface.hpp @@ -0,0 +1,127 @@ +/* + * 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 includes definitions for Thread Radio Encapsulation Link (TREL) interface. + */ + +#ifndef TREL_INTERFACE_HPP_ +#define TREL_INTERFACE_HPP_ + +#include "openthread-core-config.h" + +#include "common/locator.hpp" +#include "mac/mac_types.hpp" +#include "net/ip6_address.hpp" +#include "radio/trel_packet.hpp" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +namespace ot { +namespace Trel { + +/** + * This class represents a TREL link interface. + * + */ +class Interface : public InstanceLocator +{ +public: + /** + * This method initializes an `Interface` object + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit Interface(Instance &aInstance); + + /** + * 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. + * + */ + void Init(void); + + /** + * This method indicates whether the interface is initialized or not. + * + * @returns TRUE if the interface is initialized, FALSE otherwise. + * + */ + bool IsInitialized(void) const { return mInitialized; } + + /** + * This method notifies the interface that device's extended MAC address has changed for it to update any + * internal address/state. + * + */ + void HandleExtAddressChange(void); + + /** + * This method sends a packet over the interface. + * + * @note There is no expected callback from the interface to notify completion of send. + * + * @param[in] aPacket A packet to send. + * + * @retval OT_ERROR_NONE The frame was sent successfully. + * @retval OT_ERROR_ABORT The interface is not ready and send was aborted. + * + */ + otError Send(const Packet &aPacket); + + /** + * This method is a callback from platform layer to handle a received packet over the interface. + * + * @param[in] aBuffer A pointer to buffer containing the received packet. + * @param[in] aLength The length (number of bytes) in the received packet. + * + */ + void HandleReceived(uint8_t *aBuffer, uint16_t aLength); + +private: + enum + { + kUdpPort = 19788, // UDP port (same as MLE port). + }; + + void CreateMulticastIp6Address(Ip6::Address &aIp6Address); + + bool mInitialized; + Packet mRxPacket; +}; + +} // namespace Trel +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +#endif // TREL_INTERFACE_HPP_ diff --git a/src/core/radio/trel_link.cpp b/src/core/radio/trel_link.cpp new file mode 100644 index 000000000..2d935f479 --- /dev/null +++ b/src/core/radio/trel_link.cpp @@ -0,0 +1,475 @@ +/* + * 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 Thread Radio Encapsulation Link (TREL). + */ + +#include "trel_link.hpp" + +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "common/locator-getters.hpp" +#include "common/logging.hpp" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +namespace ot { +namespace Trel { + +Link::Link(Instance &aInstance) + : InstanceLocator(aInstance) + , mState(kStateDisabled) + , mRxChannel(0) + , mPanId(Mac::kPanIdBroadcast) + , mTxPacketNumber(0) + , mTxTasklet(aInstance, HandleTxTasklet, this) + , mTimer(aInstance, HandleTimer, this) + , mInterface(aInstance) +{ + memset(&mTxFrame, 0, sizeof(mTxFrame)); + memset(&mRxFrame, 0, sizeof(mRxFrame)); + memset(mAckFrameBuffer, 0, sizeof(mAckFrameBuffer)); + + mTxFrame.mPsdu = &mTxPacketBuffer[kMaxHeaderSize]; + mTxFrame.SetLength(0); + +#if OPENTHREAD_CONFIG_MULTI_RADIO + mTxFrame.SetRadioType(Mac::kRadioTypeTrel); + mRxFrame.SetRadioType(Mac::kRadioTypeTrel); +#endif + + // `mTxTasklet` is used for initializing the interface (in addition + // to handling `Send()` requests). Invoking `Interface::Init()` from + // a tasklet ensures that it happens after the constructors for + // `Instance` and all its objects are done, allowing the interface + // to safely use any of the core object methods (e.g., get the + // randomly generated MAC address). + + mTxTasklet.Post(); + mTimer.Start(kAckWaitWindow); +} + +void Link::Enable(void) +{ + if (mState == kStateDisabled) + { + SetState(kStateSleep); + } +} + +void Link::Disable(void) +{ + if (mState != kStateDisabled) + { + SetState(kStateDisabled); + } +} + +void Link::Sleep(void) +{ + OT_ASSERT(mState != kStateDisabled); + SetState(kStateSleep); +} + +void Link::Receive(uint8_t aChannel) +{ + OT_ASSERT(mState != kStateDisabled); + mRxChannel = aChannel; + SetState(kStateReceive); +} + +void Link::Send(void) +{ + OT_ASSERT(mState != kStateDisabled); + + SetState(kStateTransmit); + mTxTasklet.Post(); +} + +void Link::HandleTxTasklet(Tasklet &aTasklet) +{ + aTasklet.GetOwner().HandleTxTasklet(); +} + +void Link::HandleTxTasklet(void) +{ + if (!mInterface.IsInitialized()) + { + mInterface.Init(); + } + + BeginTransmit(); +} + +void Link::BeginTransmit(void) +{ + Mac::Address destAddr; + Mac::PanId destPanId; + Header::Type type; + Packet txPacket; + Neighbor * neighbor = nullptr; + Mac::RxFrame *ackFrame = nullptr; + + VerifyOrExit(mState == kStateTransmit); + + // After sending a frame on a given channel we should + // continue to rx on same channel + mRxChannel = mTxFrame.GetChannel(); + + VerifyOrExit(!mTxFrame.IsEmpty(), InvokeSendDone(OT_ERROR_ABORT)); + + IgnoreError(mTxFrame.GetDstAddr(destAddr)); + + if (destAddr.IsNone() || destAddr.IsBroadcast()) + { + type = Header::kTypeBroadcast; + } + else + { + type = Header::kTypeUnicast; + neighbor = Get().FindNeighbor(destAddr, Neighbor::kInStateAnyExceptInvalid); + + if (destAddr.IsShort()) + { + if (neighbor != nullptr) + { + destAddr.SetExtended(neighbor->GetExtAddress()); + } + else + { + // Send as a broadcast since we don't know the dest + // ext address to include in the packet header. + type = Header::kTypeBroadcast; + } + } + } + + if (mTxFrame.GetDstPanId(destPanId) != OT_ERROR_NONE) + { + destPanId = Mac::kPanIdBroadcast; + } + + txPacket.Init(type, mTxFrame.GetPsdu(), mTxFrame.GetLength()); + + if (neighbor == nullptr) + { + txPacket.GetHeader().SetAckMode(Header::kNoAck); + txPacket.GetHeader().SetPacketNumber(mTxPacketNumber++); + } + else + { + txPacket.GetHeader().SetAckMode(Header::kAckRequested); + txPacket.GetHeader().SetPacketNumber(neighbor->mTrelTxPacketNumber++); + neighbor->mTrelCurrentPendingAcks++; + } + + txPacket.GetHeader().SetChannel(mTxFrame.GetChannel()); + txPacket.GetHeader().SetPanId(destPanId); + txPacket.GetHeader().SetSource(Get().GetExtAddress()); + + if (type == Header::kTypeUnicast) + { + OT_ASSERT(destAddr.IsExtended()); + txPacket.GetHeader().SetDestination(destAddr.GetExtended()); + } + + otLogDebgMac("Trel: BeginTransmit() [%s] plen:%d", txPacket.GetHeader().ToString().AsCString(), + txPacket.GetPayloadLength()); + + VerifyOrExit(mInterface.Send(txPacket) == OT_ERROR_NONE, InvokeSendDone(OT_ERROR_ABORT)); + + if (mTxFrame.GetAckRequest()) + { + uint16_t fcf = Mac::Frame::kFcfFrameAck; + + if (!Get().IsRxOnWhenIdle()) + { + fcf |= Mac::Frame::kFcfFramePending; + } + + // Prepare the ack frame (FCF followed by sequence number) + Encoding::LittleEndian::WriteUint16(fcf, mAckFrameBuffer); + mAckFrameBuffer[sizeof(fcf)] = mTxFrame.GetSequence(); + + mRxFrame.mPsdu = mAckFrameBuffer; + mRxFrame.mLength = k154AckFrameSize; + mRxFrame.mChannel = mTxFrame.GetChannel(); +#if OPENTHREAD_CONFIG_MULTI_RADIO + mRxFrame.mRadioType = Mac::kRadioTypeTrel; +#endif + mRxFrame.mInfo.mRxInfo.mTimestamp = 0; + mRxFrame.mInfo.mRxInfo.mRssi = OT_RADIO_RSSI_INVALID; + mRxFrame.mInfo.mRxInfo.mLqi = OT_RADIO_LQI_NONE; + mRxFrame.mInfo.mRxInfo.mAckedWithFramePending = false; + + ackFrame = &mRxFrame; + } + + InvokeSendDone(OT_ERROR_NONE, ackFrame); + +exit: + return; +} + +void Link::InvokeSendDone(otError aError, Mac::RxFrame *aAckFrame) +{ + SetState(kStateReceive); + + Get().RecordFrameTransmitStatus(mTxFrame, aAckFrame, aError, /* aRetryCount */ 0, /* aWillRetx */ false); + Get().HandleTransmitDone(mTxFrame, aAckFrame, aError); +} + +void Link::HandleTimer(Timer &aTimer) +{ + aTimer.GetOwner().HandleTimer(); +} + +void Link::HandleTimer(void) +{ + mTimer.Start(kAckWaitWindow); + +#if OPENTHREAD_FTD + for (Child &child : Get().Iterate(Child::kInStateAnyExceptInvalid)) + { + HandleTimer(child); + } + + for (Router &router : Get().Iterate()) + { + HandleTimer(router); + } +#endif + + // Parent and ParentCandidate should also be updated as neighbors. + // Parent is considered only when the device is detached or child. + // When device becomes a router/leader the parent entry is copied + // into the router table but the MLE parent may still stay in + // valid state. Note that "Parent Candidate" may be in use on a + // router/leader during a partition merge, so it is always treated + // as a neighbor. + + switch (Get().GetRole()) + { + case Mle::kRoleDisabled: + break; + + case Mle::kRoleDetached: + case Mle::kRoleChild: + HandleTimer(Get().GetParent()); + + // Fall through + + case Mle::kRoleRouter: + case Mle::kRoleLeader: + HandleTimer(Get().GetParentCandidate()); + break; + } +} + +void Link::HandleTimer(Neighbor &aNeighbor) +{ + VerifyOrExit(!aNeighbor.IsStateInvalid()); + + // Any remaining previous pending ack has timed out. + + while (aNeighbor.mTrelPreviousPendingAcks > 0) + { + aNeighbor.mTrelPreviousPendingAcks--; + + ReportDeferredAckStatus(aNeighbor, OT_ERROR_NO_ACK); + VerifyOrExit(!aNeighbor.IsStateInvalid()); + } + + aNeighbor.mTrelPreviousPendingAcks = aNeighbor.mTrelCurrentPendingAcks; + aNeighbor.mTrelCurrentPendingAcks = 0; + +exit: + return; +} + +void Link::ProcessReceivedPacket(Packet &aPacket) +{ + Header::Type type; + + VerifyOrExit(aPacket.IsHeaderValid()); + + type = aPacket.GetHeader().GetType(); + + if (type != Header::kTypeAck) + { + // No need to check state or channel for a TREL ack packet. + // Note that TREL ack may be received much later than the tx + // and device can be on a different rx channel. + + VerifyOrExit((mState == kStateReceive) || (mState == kStateTransmit)); + VerifyOrExit(aPacket.GetHeader().GetChannel() == mRxChannel); + } + + if (mPanId != Mac::kPanIdBroadcast) + { + Mac::PanId rxPanId = aPacket.GetHeader().GetPanId(); + + VerifyOrExit((rxPanId == mPanId) || (rxPanId == Mac::kPanIdBroadcast)); + } + + // Drop packets originating from same device. + VerifyOrExit(aPacket.GetHeader().GetSource() != Get().GetExtAddress()); + + if (type != Header::kTypeBroadcast) + { + VerifyOrExit(aPacket.GetHeader().GetDestination() == Get().GetExtAddress()); + + if (type == Header::kTypeAck) + { + HandleAck(aPacket); + ExitNow(); + } + } + + otLogDebgMac("Trel: ReceivedPacket() [%s] plen:%d", aPacket.GetHeader().ToString().AsCString(), + aPacket.GetPayloadLength()); + + if (aPacket.GetHeader().GetAckMode() == Header::kAckRequested) + { + SendAck(aPacket); + } + + mRxFrame.mPsdu = aPacket.GetPayload(); + mRxFrame.mLength = aPacket.GetPayloadLength(); + mRxFrame.mChannel = aPacket.GetHeader().GetChannel(); +#if OPENTHREAD_CONFIG_MULTI_RADIO + mRxFrame.mRadioType = Mac::kRadioTypeTrel; +#endif + mRxFrame.mInfo.mRxInfo.mTimestamp = 0; + mRxFrame.mInfo.mRxInfo.mRssi = kRxRssi; + mRxFrame.mInfo.mRxInfo.mLqi = OT_RADIO_LQI_NONE; + mRxFrame.mInfo.mRxInfo.mAckedWithFramePending = true; + + Get().HandleReceivedFrame(&mRxFrame, OT_ERROR_NONE); + +exit: + return; +} + +void Link::HandleAck(Packet &aAckPacket) +{ + otError ackError; + Mac::Address srcAddress; + Neighbor * neighbor; + uint32_t ackNumber; + + otLogDebgMac("Trel: HandleAck() [%s]", aAckPacket.GetHeader().ToString().AsCString()); + + srcAddress.SetExtended(aAckPacket.GetHeader().GetSource()); + neighbor = Get().FindNeighbor(srcAddress, Neighbor::kInStateAnyExceptInvalid); + VerifyOrExit(neighbor != nullptr); + + ackNumber = aAckPacket.GetHeader().GetPacketNumber(); + + // Verify that neighbor is waiting for acks and the received ack + // number is within the range of expected ack numbers. + + VerifyOrExit(neighbor->IsRxAckNumberValid(ackNumber)); + + do + { + // Check whether the received ack number matches the next + // expected one. If it does not, it indicates that some of + // packets missed their acks. + + ackError = (ackNumber == neighbor->GetExpectedTrelAckNumber()) ? OT_ERROR_NONE : OT_ERROR_NO_ACK; + + neighbor->DecrementPendingTrelAckCount(); + + ReportDeferredAckStatus(*neighbor, ackError); + VerifyOrExit(!neighbor->IsStateInvalid()); + + } while (ackError == OT_ERROR_NO_ACK); + +exit: + return; +} + +void Link::SendAck(Packet &aRxPacket) +{ + Packet ackPacket; + + ackPacket.Init(mAckPacketBuffer, sizeof(mAckPacketBuffer)); + + ackPacket.GetHeader().Init(Header::kTypeAck); + ackPacket.GetHeader().SetAckMode(Header::kNoAck); + ackPacket.GetHeader().SetChannel(aRxPacket.GetHeader().GetChannel()); + ackPacket.GetHeader().SetPanId(aRxPacket.GetHeader().GetPanId()); + ackPacket.GetHeader().SetPacketNumber(aRxPacket.GetHeader().GetPacketNumber()); + ackPacket.GetHeader().SetSource(Get().GetExtAddress()); + ackPacket.GetHeader().SetDestination(aRxPacket.GetHeader().GetSource()); + + otLogDebgMac("Trel: SendAck [%s]", ackPacket.GetHeader().ToString().AsCString()); + + IgnoreError(mInterface.Send(ackPacket)); +} + +void Link::ReportDeferredAckStatus(Neighbor &aNeighbor, otError aError) +{ + otLogDebgMac("Trel: ReportDeferredAckStatus(): %s for %s", aNeighbor.GetExtAddress().ToString().AsCString(), + otThreadErrorToString(aError)); + + Get().HandleDeferredAck(aNeighbor, aError); +} + +void Link::SetState(State aState) +{ + if (mState != aState) + { + otLogDebgMac("Trel: State: %s -> %s", StateToString(mState), StateToString(aState)); + mState = aState; + } +} + +// LCOV_EXCL_START + +const char *Link::StateToString(State aState) +{ + static const char *kStateStrings[] = { + "Disabled", // kStateDisabled + "Sleep", // kStateSleep + "Receive", // kStateReceive + "Transmit", // kStateTransmit + }; + + return (static_cast(aState) < OT_ARRAY_LENGTH(kStateStrings)) ? kStateStrings[aState] : "Unknown"; +} + +// LCOV_EXCL_STOP + +} // namespace Trel +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/radio/trel_link.hpp b/src/core/radio/trel_link.hpp new file mode 100644 index 000000000..01f819c18 --- /dev/null +++ b/src/core/radio/trel_link.hpp @@ -0,0 +1,252 @@ +/* + * 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 includes definitions for Thread Radio Encapsulation Link (TREL). + */ + +#ifndef TREL_LINK_HPP_ +#define TREL_LINK_HPP_ + +#include "openthread-core-config.h" + +#include "common/encoding.hpp" +#include "common/locator.hpp" +#include "common/tasklet.hpp" +#include "common/timer.hpp" +#include "mac/mac_frame.hpp" +#include "mac/mac_types.hpp" +#include "radio/trel_interface.hpp" +#include "radio/trel_packet.hpp" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +namespace ot { + +class Neighbor; + +namespace Trel { + +/** + * @addtogroup core-trel + * + * @brief + * This module includes definitions for Thread Radio Encapsulation Link (TREL) + * + * @{ + * + */ + +/** + * This class represents a Thread Radio Encapsulation Link (TREL). + * + */ +class Link : public InstanceLocator +{ + friend class ot::Instance; + friend class Interface; + +public: + enum + { + kMtuSize = OT_RADIO_FRAME_MAX_SIZE, ///< MTU size for TREL frame. + kFcsSize = 2, ///< FCS size for TREL frame. + }; + + /** + * This constructor initializes the `Link` object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit Link(Instance &aInstance); + + /** + * This method sets the PAN Identifier. + * + * @param[in] aPanId A PAN Identifier. + * + */ + void SetPanId(Mac::PanId aPanId) { mPanId = aPanId; } + + /** + * This method notifies TREL radio link that device's extended MAC address has changed for it to update any + * internal address/state. + * + */ + void HandleExtAddressChange(void) { mInterface.HandleExtAddressChange(); } + + /** + * This method enables the TREL radio link. + * + */ + void Enable(void); + + /** + * This method disables the TREL radio link. + * + */ + void Disable(void); + + /** + * This method requests TREL radio link to transition to Sleep mode + * + */ + void Sleep(void); + + /** + * This method requests TREL radio link to transition to Receive mode on a given channel. + * + * `Mac::HandleReceivedFrame()` is used to notify MAC layer upon receiving a frame. + * + * @param[in] aChannel The channel to receive on. + * + */ + void Receive(uint8_t aChannel); + + /** + * This method gets the radio transmit frame for TREL radio link. + * + * @returns The transmit frame. + * + */ + Mac::TxFrame &GetTransmitFrame(void) { return mTxFrame; } + + /** + * This method requests a frame to be sent over TREL radio link. + * + * The frame should be already placed in `GetTransmitFrame()` frame. + * + * `Mac::RecordFrameTransmitStatus()` and `Mac::HandleTransmitDone()` are used to notify the success or error status + * of frame transmission upon completion of send. + * + */ + void Send(void); + +private: + enum + { + kMaxHeaderSize = sizeof(Header), + k154AckFrameSize = 3 + kFcsSize, + kRxRssi = -20, // The RSSI value used for received frames on TREL radio link. + kAckWaitWindow = 750, // (in msec) + }; + + enum State : uint8_t + { + kStateDisabled, + kStateSleep, + kStateReceive, + kStateTransmit, + }; + + void SetState(State aState); + void BeginTransmit(void); + void InvokeSendDone(otError aError) { InvokeSendDone(aError, nullptr); } + void InvokeSendDone(otError aError, Mac::RxFrame *aAckFrame); + void ProcessReceivedPacket(Packet &aPacket); + void HandleAck(Packet &aAckPacket); + void SendAck(Packet &aRxPacket); + void ReportDeferredAckStatus(Neighbor &aNeighbor, otError aError); + void HandleTimer(Neighbor &aNeighbor); + + static void HandleTxTasklet(Tasklet &aTasklet); + void HandleTxTasklet(void); + + static void HandleTimer(Timer &aTimer); + void HandleTimer(void); + + static const char *StateToString(State aState); + + State mState; + uint8_t mRxChannel; + Mac::PanId mPanId; + uint32_t mTxPacketNumber; + Tasklet mTxTasklet; + TimerMilli mTimer; + Interface mInterface; + Mac::RxFrame mRxFrame; + Mac::TxFrame mTxFrame; + uint8_t mTxPacketBuffer[kMaxHeaderSize + kMtuSize]; + uint8_t mAckPacketBuffer[kMaxHeaderSize]; + uint8_t mAckFrameBuffer[k154AckFrameSize]; +}; + +/** + * This class defines all the neighbor info required for TREL link. + * + * `Neighbor` class publicly inherits from this class. + * + */ +class NeighborInfo +{ + friend class Link; + +private: + uint32_t GetPendingTrelAckCount(void) const { return (mTrelPreviousPendingAcks + mTrelCurrentPendingAcks); } + + void DecrementPendingTrelAckCount(void) + { + if (mTrelPreviousPendingAcks != 0) + { + mTrelPreviousPendingAcks--; + } + else if (mTrelCurrentPendingAcks != 0) + { + mTrelCurrentPendingAcks--; + } + } + + uint32_t GetExpectedTrelAckNumber(void) const { return mTrelTxPacketNumber - GetPendingTrelAckCount(); } + + bool IsRxAckNumberValid(uint32_t aAckNumber) const + { + // Note that calculating the difference between `aAckNumber` + // and `GetExpectedTrelAckNumber` will correctly handle the + // roll-over of packet number value. + + return (GetPendingTrelAckCount() != 0) && (aAckNumber - GetExpectedTrelAckNumber() < GetPendingTrelAckCount()); + } + + uint32_t mTrelTxPacketNumber; // Next packet number to use for tx + uint16_t mTrelCurrentPendingAcks; // Number of pending acks for current interval. + uint16_t mTrelPreviousPendingAcks; // Number of pending acks for previous interval. +}; + +/** + * @} + * + */ + +} // namespace Trel +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +#endif // TREL_LINK_HPP_ diff --git a/src/core/radio/trel_packet.cpp b/src/core/radio/trel_packet.cpp new file mode 100644 index 000000000..59f9d93a2 --- /dev/null +++ b/src/core/radio/trel_packet.cpp @@ -0,0 +1,140 @@ +/* + * 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 Thread Radio Encapsulation Link (TREL) packet. + */ + +#include "trel_packet.hpp" + +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "common/locator-getters.hpp" +#include "common/logging.hpp" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +namespace ot { +namespace Trel { + +void Header::SetAckMode(AckMode aAckMode) +{ + switch (aAckMode) + { + case kNoAck: + mControl &= ~kAckModeFlag; + break; + + case kAckRequested: + mControl |= kAckModeFlag; + break; + } +} + +uint16_t Header::GetSize(Type aType) +{ + uint16_t size = sizeof(Header); + + switch (aType) + { + case kTypeBroadcast: + size -= sizeof(Mac::ExtAddress); // Exclude mDestination field. + break; + + case kTypeUnicast: + case kTypeAck: + break; + } + + return size; +} + +Header::InfoString Header::ToString(void) const +{ + Type type = GetType(); + InfoString string; + + switch (type) + { + case kTypeBroadcast: + IgnoreError(string.Set("broadcast ch:%d", GetChannel())); + break; + + case kTypeUnicast: + IgnoreError(string.Set("unicast ch:%d", GetChannel())); + break; + + case kTypeAck: + IgnoreError(string.Set("ack")); + break; + } + + IgnoreError( + string.Append(" panid:%04x num:%lu src:%s", GetPanId(), GetPacketNumber(), GetSource().ToString().AsCString())); + + if ((type == kTypeUnicast) || (type == kTypeAck)) + { + IgnoreError(string.Append(" dst:%s", GetDestination().ToString().AsCString())); + } + + if ((type == kTypeUnicast) || (type == kTypeBroadcast)) + { + IgnoreError(string.Append(GetAckMode() == kNoAck ? " no-ack" : " ack-req")); + } + + return string; +} + +void Packet::Init(uint8_t *aBuffer, uint16_t aLength) +{ + mBuffer = aBuffer; + mLength = aLength; +} + +void Packet::Init(Header::Type aType, uint8_t *mPayload, uint16_t mPayloadLength) +{ + uint16_t headerSize = Header::GetSize(aType); + + // The payload buffer should reserve enough bytes for + // header (depending on type) before the payload. + + Init(mPayload - headerSize, mPayloadLength + headerSize); + GetHeader().Init(aType); +} + +bool Packet::IsHeaderValid(void) const +{ + return ((mBuffer != nullptr) && (mLength > 0) && GetHeader().IsVersionValid() && + (mLength >= GetHeader().GetLength())); +} + +} // namespace Trel +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/radio/trel_packet.hpp b/src/core/radio/trel_packet.hpp new file mode 100644 index 000000000..4f706e1d5 --- /dev/null +++ b/src/core/radio/trel_packet.hpp @@ -0,0 +1,373 @@ +/* + * 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 includes definitions for Thread Radio Encapsulation Link (TREL) Packet + */ + +#ifndef TREL_PACKET_HPP_ +#define TREL_PACKET_HPP_ + +#include "openthread-core-config.h" + +#include "common/encoding.hpp" +#include "common/locator.hpp" +#include "common/string.hpp" +#include "mac/mac_types.hpp" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +namespace ot { +namespace Trel { + +/** + * This class represents a TREL radio link packet encapsulation header. + * + */ +OT_TOOL_PACKED_BEGIN +class Header +{ +public: + /** + * This enumeration defines packet types. + * + */ + enum Type + { + kTypeBroadcast = 0, ///< A TREL broadcast packet. + kTypeUnicast = 1, ///< A TREL unicast packet. + kTypeAck = 2, ///< A TREL Ack packet. + }; + + /** + * This enumeration represents Ack Mode field in TREL header. + * + */ + enum AckMode + { + kNoAck, ///< No TREL Ack is requested. + kAckRequested, ///< A TREL Ack is requested. + }; + + enum + { + kInfoStringSize = 128, ///< Max chars needed for the info string representation (@sa ToInfoString()). + }; + + /** + * This type defines the fixed-length `String` object returned from `ToString()` method. + * + */ + typedef String InfoString; + + /** + * This method initializes the header. + * + * @param[in] aType The header type. + * + */ + void Init(Type aType) { mControl = aType + kVersion; } + + /** + * This method checks whether the version field in header is valid or not + * + * @returns TRUE if the version field is valid, FALSE otherwise. + * + */ + bool IsVersionValid(void) const { return (mControl & kVersionMask) == kVersion; } + + /** + * This method gets the packet type. + * + * @returns The packet type. + * + */ + Type GetType(void) const { return static_cast(mControl & kTypeMask); } + + /** + * This method gets the header length based on its type. + * + * @returns the header length (number of bytes). + * + */ + uint16_t GetLength(void) const { return GetSize(GetType()); } + + /** + * This method gets the Ack Mode field from the header. + * + * @returns The Ack Mode field. + * + */ + AckMode GetAckMode(void) const { return (mControl & kAckModeFlag) ? kAckRequested : kNoAck; } + + /** + * This method sets the Ack Mode field in the header. + * + * @param[in] aAckMode The Ack Mode field + * + */ + void SetAckMode(AckMode aAckMode); + + /** + * This method gets the channel field from the header. + * + * @returns The channel field. + * + */ + uint8_t GetChannel(void) const { return mChannel; } + + /** + * This method sets the channel field in the header. + * + * @param[in] aChannel A channel. + * + */ + void SetChannel(uint8_t aChannel) { mChannel = aChannel; } + + /** + * This method gets the PAN Identifier field from the header. + * + * @returns The PAN Identifier field. + * + */ + Mac::PanId GetPanId(void) const { return Encoding::BigEndian::HostSwap16(mPanId); } + + /** + * This method sets the PAN Identifier field in the header. + * + * @param[in] aPanId A PAN Identifier. + * + */ + void SetPanId(Mac::PanId aPanId) { mPanId = Encoding::BigEndian::HostSwap16(aPanId); } + + /** + * This method gets the packet number field from the header. + * + * @returns The packet number field. + * + */ + uint32_t GetPacketNumber(void) const { return Encoding::BigEndian::HostSwap32(mPacketNumber); } + + /** + * This method sets the packet number field in the header. + * + * @param[in] aPacketNumber The packet number. + * + */ + void SetPacketNumber(uint32_t aPacketNumber) { mPacketNumber = Encoding::BigEndian::HostSwap32(aPacketNumber); } + + /** + * This method gets the source MAC address field from the header. + * + * @returns The source MAC address field. + * + */ + const Mac::ExtAddress &GetSource(void) const { return mSource; } + + /** + * This method sets the source MAC address filed in the header. + * + * @param[in] aSource A MAC extended address to set as source. + * + */ + void SetSource(const Mac::ExtAddress &aSource) { mSource = aSource; } + + /** + * This method gets the destination MAC address field from the header. + * + * This method MUST be used with a unicast of ack type packet, otherwise its behavior is undefined. + * + * @returns The destination MAC address field. + * + */ + const Mac::ExtAddress &GetDestination(void) const { return mDestination; } + + /** + * This method sets the destination MAC address field in the header. + * + * This method MUST be used with a unicast of ack type packet, otherwise its behavior is undefined. + * + * @param[in] aDest A MAC extended address to set as destination. + * + */ + void SetDestination(const Mac::ExtAddress &aDest) { mDestination = aDest; } + + /** + * This static method gets the size (number of bytes) in header of given packet type. + * + * @param[in] aType The packet type. + * + * @returns The fixed header size (number of bytes) for @p aType packet. + * + */ + static uint16_t GetSize(Type aType); + + /** + * This method returns a string representation of header. + * + * @returns An `InfoString` representation of header. + * + */ + InfoString ToString(void) const; + +private: + enum + { + kTypeMask = (3 << 0), // Bits 0-1 specify packet `Type` + kAckModeFlag = (1 << 2), // Bit 2 indicate "ack mode" (TREL ack requested or not). + kVersionMask = (7 << 5), // Bits 5-7 specify the version. + kVersion = (0 << 5), // Current TREL header version. + }; + + // All header fields are big-endian. + + uint8_t mControl; + uint8_t mChannel; + uint16_t mPanId; + uint32_t mPacketNumber; + Mac::ExtAddress mSource; + Mac::ExtAddress mDestination; // Present in `kTypeAck` or `kTypeUnicast` packet types. +} OT_TOOL_PACKED_END; + +/** + * This class represent a TREL radio link packet. + * + */ +class Packet +{ +public: + /** + * This method initializes the `Packet` with a given buffer and length. + * + * @param[in] aBuffer A pointer to a buffer containing the entire packet (header and payload). + * @param[in] aLength Length (number of bytes) of the packet (including header and payload). + * + */ + void Init(uint8_t *aBuffer, uint16_t aLength); + + /** + * This method initializes the `Packet` with a specified header type and given a payload. + * + * The payload buffer @p aPayload should have space reserved before the start of payload for the packet header. + * This method will initialize the header with the given type @p aType. Rest of header fields can be updated after + * initializing the packet. + * + * @param[in] aType The packet type. + * @param[in] aPayload A pointer to a buffer containing the packet payload. Buffer should have space reserved + * for header before the payload. + * @param[in] aPayloadLength The length (number of bytes) in the payload only (not including the header). + * + */ + + void Init(Header::Type aType, uint8_t *aPayload, uint16_t aPayloadLength); + + /** + * This method gets a pointer to buffer containing the packet. + * + * @returns A pointer to buffer containing the packet. + * + */ + uint8_t *GetBuffer(void) { return mBuffer; } + + /** + * This method gets a pointer to buffer containing the packet. + * + * @returns A pointer to buffer containing the packet. + * + */ + const uint8_t *GetBuffer(void) const { return mBuffer; } + + /** + * This method gets the length of packet. + * + * @returns The length (number of bytes) of packet (header and payload). + * + */ + uint16_t GetLength(void) const { return mLength; } + + /** + * This method checks whether or not the packet header is valid. + * + * @retval TRUE The packet header is valid and well-formed. + * @retval FALSE The packet header is not valid. + * + */ + bool IsHeaderValid(void) const; + + /** + * This method gets the packet header. + * + * @returns A reference to the packet header as `Header`. + * + */ + Header &GetHeader(void) { return *reinterpret_cast
(mBuffer); } + + /** + * This method gets the packet header. + * + * @returns A reference to the packet header as `Header`. + * + */ + const Header &GetHeader(void) const { return *reinterpret_cast(mBuffer); } + + /** + * This method gets a pointer to start of packet payload. + * + * @returns A pointer to start of packet payload (after header). + * + */ + uint8_t *GetPayload(void) { return mBuffer + GetHeader().GetLength(); } + + /** + * This method gets a pointer to start of packet payload. + * + * @returns A pointer to start of packet payload (after header). + * + */ + const uint8_t *GetPayload(void) const { return mBuffer + GetHeader().GetLength(); } + + /** + * This method gets the payload length. + * + * @returns The packet payload length (number of bytes). + * + */ + uint16_t GetPayloadLength(void) const { return mLength - GetHeader().GetLength(); } + +private: + uint8_t *mBuffer; + uint16_t mLength; +}; + +} // namespace Trel +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +#endif // TREL_PACKET_HPP_ diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 356600ff7..6394e13e0 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -46,6 +46,7 @@ #include "common/timer.hpp" #include "mac/mac_types.hpp" #include "net/ip6.hpp" +#include "radio/trel_link.hpp" #include "thread/csl_tx_scheduler.hpp" #include "thread/indirect_sender.hpp" #include "thread/link_metrics.hpp" @@ -69,6 +70,10 @@ class Neighbor : public InstanceLocatorInit , public RadioSelector::NeighborInfo #endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + , + public Trel::NeighborInfo +#endif { public: /** diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 80aa3704f..8e8a1c48e 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -610,4 +610,47 @@ void otPlatOtnsStatus(const char *aStatus) } #endif // OPENTHREAD_CONFIG_OTNS_ENABLE +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +void otPlatTrelUdp6Init(otInstance *aInstance, const otIp6Address *aUnicastAddress, uint16_t aUdpPort) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aUnicastAddress); + OT_UNUSED_VARIABLE(aUdpPort); +} + +void otPlatTrelUdp6UpdateAddress(otInstance *aInstance, const otIp6Address *aUnicastAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aUnicastAddress); +} + +void otPlatTrelUdp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aMulticastAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMulticastAddress); +} + +otError otPlatTrelUdp6SendTo(otInstance * aInstance, + const uint8_t * aBuffer, + uint16_t aLength, + const otIp6Address *aDestAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aBuffer); + OT_UNUSED_VARIABLE(aLength); + OT_UNUSED_VARIABLE(aDestAddress); + + return OT_ERROR_ABORT; +} + +otError otPlatTrelUdp6SetTestMode(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + return OT_ERROR_NOT_IMPLEMENTED; +} + +#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + } // extern "C"