[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.
This commit is contained in:
Abtin Keshavarzian
2022-01-28 12:07:42 -08:00
committed by Jonathan Hui
parent 1c6062c610
commit 69ad96675f
33 changed files with 2019 additions and 1409 deletions
+2 -1
View File
@@ -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 \
+1
View File
@@ -84,6 +84,7 @@
* @defgroup api-message Message
*
* @defgroup api-multi-radio Multi Radio Link
* @defgroup api-trel TREL
*
* @defgroup api-thread Thread
*
+225 -125
View File
@@ -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 <openthread/random_noncrypto.h>
#include <openthread/platform/trel-udp6.h>
#include <openthread/platform/trel.h>
#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
+2 -1
View File
@@ -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)
+2 -1
View File
@@ -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",
]
+1 -1
View File
@@ -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
-155
View File
@@ -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 <stdint.h>
#include <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#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_
+209
View File
@@ -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 <stdint.h>
#include <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#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_
+168
View File
@@ -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 <openthread/dataset.h>
#include <openthread/ip6.h>
#include <openthread/platform/radio.h>
#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_
+64 -9
View File
@@ -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.
+84 -4
View File
@@ -82,7 +82,7 @@
#include <openthread/platform/debug_uart.h>
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
#include <openthread/platform/trel-udp6.h>
#include <openthread/trel.h>
#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;
+1
View File
@@ -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",
+1
View File
@@ -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
+1
View File
@@ -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 \
+81
View File
@@ -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 <openthread/trel.h>
#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<Trel::Interface>().Enable();
}
void otTrelDisable(otInstance *aInstance)
{
AsCoreType(aInstance).Get<Trel::Interface>().Disable();
}
bool otTrelIsEnabled(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Trel::Interface>().IsEnabled();
}
void otTrelInitPeerIterator(otInstance *aInstance, otTrelPeerIterator *aIterator)
{
AsCoreType(aInstance).Get<Trel::Interface>().InitIterator(*aIterator);
}
const otTrelPeer *otTrelGetNextPeer(otInstance *aInstance, otTrelPeerIterator *aIterator)
{
return AsCoreType(aInstance).Get<Trel::Interface>().GetNextPeer(*aIterator);
}
void otTrelSetFilterEnabled(otInstance *aInstance, bool aEnable)
{
AsCoreType(aInstance).Get<Trel::Interface>().SetFilterEnabled(aEnable);
}
bool otTrelIsFilterEnabled(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Trel::Interface>().IsFilterEnabled();
}
#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+3
View File
@@ -165,6 +165,9 @@ void Notifier::EmitEvents(void)
#if OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE)
Get<DuaManager>().HandleNotifierEvents(events);
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
Get<Trel::Link>().HandleNotifierEvents(events);
#endif
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
Get<TimeSync>().HandleNotifierEvents(events);
#endif
+357 -40
View File
@@ -34,102 +34,419 @@
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
#include <openthread/platform/trel-udp6.h>
#include <string.h>
#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<Mac::Mac>().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<Mac::Mac>().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<Interface>().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<kTxtDataSize> string;
VerifyOrExit(mInitialized && mEnabled);
string.Append("%s=", kTxtRecordExtAddressKey);
string.AppendHexBytes(Get<Mac::Mac>().GetExtAddress().m8, sizeof(Mac::ExtAddress));
*txtPtr++ = static_cast<uint8_t>(string.GetLength());
memcpy(txtPtr, string.AsCString(), string.GetLength());
txtPtr += string.GetLength();
string.Clear();
string.Append("%s=", kTxtRecordExtPanIdKey);
string.AppendHexBytes(Get<Mac::Mac>().GetExtendedPanId().m8, sizeof(Mac::ExtendedPanId));
*txtPtr++ = static_cast<uint8_t>(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<Mac::Mac>().GetExtAddress().ToString().AsCString(), kTxtRecordExtPanIdKey,
Get<Mac::Mac>().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<Interface>().HandleDiscoveredPeerInfo(*static_cast<const Interface::Peer::Info *>(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<Mac::Mac>().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 <uint16_t kBufferSize>
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<Mac::Mac>().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<NeighborTable>().FindNeighbor(entry.GetExtAddress(), Neighbor::kInStateAny) != nullptr)
{
continue;
}
#if OPENTHREAD_FTD
{
Mac::Address macAddress;
macAddress.SetExtended(entry.GetExtAddress());
if (Get<NeighborTable>().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<Mac::Mac>().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<Interface>().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<Link>().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<ot::Trel::Interface>().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
+172 -32
View File
@@ -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 <openthread/trel.h>
#include <openthread/platform/trel.h>
#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<const Mac::ExtAddress &>(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<const Mac::ExtendedPanId &>(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<const Ip6::SockAddr &>(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<const Ip6::SockAddr &>(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<Peer, kPeerTableSize, uint16_t> 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
+38 -3
View File
@@ -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)
+3
View File
@@ -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);
+2 -8
View File
@@ -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
+14 -15
View File
@@ -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<kWithUint16Length>
{
using Base = MutableData<kWithUint16Length>;
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<Header *>(mBuffer); }
Header &GetHeader(void) { return *reinterpret_cast<Header *>(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<const Header *>(mBuffer); }
const Header &GetHeader(void) const { return *reinterpret_cast<const Header *>(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
-3
View File
@@ -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);
-4
View File
@@ -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
};
+10 -4
View File
@@ -67,7 +67,7 @@
#include <openthread/srp_client_buffers.h>
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
#include <openthread/platform/trel-udp6.h>
#include <openthread/trel.h>
#endif
#include "common/code_utils.hpp"
@@ -4046,15 +4046,21 @@ exit:
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_DEBUG_TREL_TEST_MODE_ENABLE>(void)
{
return mEncoder.WriteBool(mTrelTestModeEnable);
return mEncoder.WriteBool(!otTrelIsFilterEnabled(mInstance));
}
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_DEBUG_TREL_TEST_MODE_ENABLE>(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;
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 \
+11 -29
View File
@@ -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_
+2 -2
View File
@@ -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.
+559
View File
@@ -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 <arpa/inet.h>
#include <assert.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <openthread/platform/trel.h>
#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
-935
View File
@@ -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 <arpa/inet.h>
#include <assert.h>
#include <fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#if OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET
#include <linux/if_tun.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#endif
#include <openthread/platform/trel-udp6.h>
#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<struct rtattr *>((reinterpret_cast<char *>(&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<struct ifaddrmsg *>(NLMSG_DATA(aNetlinkMessage));
size_t rtaLength;
VerifyOrExit(ifaddr->ifa_index == static_cast<unsigned int>(sInterfaceIndex) && ifaddr->ifa_family == AF_INET6);
rtaLength = IFA_PAYLOAD(aNetlinkMessage);
for (struct rtattr *rta = reinterpret_cast<struct rtattr *>(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<size_t>(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<struct nlmsgerr *>(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<struct sockaddr *>(&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<uint64_t>(aTimeout->tv_sec) * US_PER_S + static_cast<uint64_t>(aTimeout->tv_usec)))
{
aTimeout->tv_sec = static_cast<time_t>(remain / US_PER_S);
aTimeout->tv_usec = static_cast<suseconds_t>(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
@@ -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
*
+4 -12
View File
@@ -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