mirror of
https://github.com/espressif/openthread.git
synced 2026-08-31 06:19:54 +00:00
[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:
committed by
Jonathan Hui
parent
1c6062c610
commit
69ad96675f
+2
-1
@@ -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 \
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
* @defgroup api-message Message
|
||||
*
|
||||
* @defgroup api-multi-radio Multi Radio Link
|
||||
* @defgroup api-trel TREL
|
||||
*
|
||||
* @defgroup api-thread Thread
|
||||
*
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user