• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2019-2021, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements platform for TREL using IPv6/UDP socket under POSIX.
32  */
33 
34 #include "openthread-posix-config.h"
35 
36 #include "platform-posix.h"
37 
38 #include <arpa/inet.h>
39 #include <assert.h>
40 #include <fcntl.h>
41 #include <netinet/in.h>
42 #include <sys/socket.h>
43 #include <unistd.h>
44 
45 #include <openthread/logging.h>
46 #include <openthread/platform/trel.h>
47 
48 #include "logger.hpp"
49 #include "radio_url.hpp"
50 #include "system.hpp"
51 #include "common/code_utils.hpp"
52 
53 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
54 
55 static constexpr uint16_t kMaxPacketSize = 1400; // The max size of a TREL packet.
56 
57 typedef struct TxPacket
58 {
59     struct TxPacket *mNext;
60     uint8_t          mBuffer[kMaxPacketSize];
61     uint16_t         mLength;
62     otSockAddr       mDestSockAddr;
63 } TxPacket;
64 
65 static uint8_t            sRxPacketBuffer[kMaxPacketSize];
66 static uint16_t           sRxPacketLength;
67 static TxPacket           sTxPacketPool[OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE];
68 static TxPacket          *sFreeTxPacketHead;  // A singly linked list of free/available `TxPacket` from pool.
69 static TxPacket          *sTxPacketQueueTail; // A circular linked list for queued tx packets.
70 static otPlatTrelCounters sCounters;
71 
72 static char sInterfaceName[IFNAMSIZ + 1];
73 static bool sInitialized = false;
74 static bool sEnabled     = false;
75 static int  sSocket      = -1;
76 
77 static const char kLogModuleName[] = "Trel";
78 
LogCrit(const char * aFormat,...)79 static void LogCrit(const char *aFormat, ...)
80 {
81     va_list args;
82 
83     va_start(args, aFormat);
84     otLogPlatArgs(OT_LOG_LEVEL_CRIT, kLogModuleName, aFormat, args);
85     va_end(args);
86 }
87 
LogWarn(const char * aFormat,...)88 static void LogWarn(const char *aFormat, ...)
89 {
90     va_list args;
91 
92     va_start(args, aFormat);
93     otLogPlatArgs(OT_LOG_LEVEL_WARN, kLogModuleName, aFormat, args);
94     va_end(args);
95 }
96 
LogNote(const char * aFormat,...)97 static void LogNote(const char *aFormat, ...)
98 {
99     va_list args;
100 
101     va_start(args, aFormat);
102     otLogPlatArgs(OT_LOG_LEVEL_NOTE, kLogModuleName, aFormat, args);
103     va_end(args);
104 }
105 
LogInfo(const char * aFormat,...)106 static void LogInfo(const char *aFormat, ...)
107 {
108     va_list args;
109 
110     va_start(args, aFormat);
111     otLogPlatArgs(OT_LOG_LEVEL_INFO, kLogModuleName, aFormat, args);
112     va_end(args);
113 }
114 
LogDebg(const char * aFormat,...)115 static void LogDebg(const char *aFormat, ...)
116 {
117     va_list args;
118 
119     va_start(args, aFormat);
120     otLogPlatArgs(OT_LOG_LEVEL_DEBG, kLogModuleName, aFormat, args);
121     va_end(args);
122 }
123 
Ip6AddrToString(const void * aAddress)124 static const char *Ip6AddrToString(const void *aAddress)
125 {
126     static char string[INET6_ADDRSTRLEN];
127     return inet_ntop(AF_INET6, aAddress, string, sizeof(string));
128 }
129 
BufferToString(const uint8_t * aBuffer,uint16_t aLength)130 static const char *BufferToString(const uint8_t *aBuffer, uint16_t aLength)
131 {
132     const uint16_t kMaxWrite = 16;
133     static char    string[1600];
134 
135     uint16_t num = 0;
136     char    *cur = &string[0];
137     char    *end = &string[sizeof(string) - 1];
138 
139     cur += snprintf(cur, (uint16_t)(end - cur), "[(len:%d) ", aLength);
140     VerifyOrExit(cur < end);
141 
142     while (aLength-- && (num < kMaxWrite))
143     {
144         cur += snprintf(cur, (uint16_t)(end - cur), "%02x ", *aBuffer++);
145         VerifyOrExit(cur < end);
146 
147         num++;
148     }
149 
150     if (aLength != 0)
151     {
152         cur += snprintf(cur, (uint16_t)(end - cur), "... ");
153         VerifyOrExit(cur < end);
154     }
155 
156     *cur++ = ']';
157     VerifyOrExit(cur < end);
158 
159     *cur = '\0';
160 
161 exit:
162     *end = '\0';
163     return string;
164 }
165 
PrepareSocket(uint16_t & aUdpPort)166 static void PrepareSocket(uint16_t &aUdpPort)
167 {
168     int                 val;
169     struct sockaddr_in6 sockAddr;
170     socklen_t           sockLen;
171 
172     LogDebg("PrepareSocket()");
173 
174     sSocket = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, 0, kSocketNonBlock);
175     VerifyOrDie(sSocket >= 0, OT_EXIT_ERROR_ERRNO);
176 
177     // Make the socket non-blocking to allow immediate tx attempt.
178     val = fcntl(sSocket, F_GETFL, 0);
179     VerifyOrDie(val != -1, OT_EXIT_ERROR_ERRNO);
180     val = val | O_NONBLOCK;
181     VerifyOrDie(fcntl(sSocket, F_SETFL, val) == 0, OT_EXIT_ERROR_ERRNO);
182 
183     // Bind the socket.
184 
185     memset(&sockAddr, 0, sizeof(sockAddr));
186     sockAddr.sin6_family = AF_INET6;
187     sockAddr.sin6_addr   = in6addr_any;
188     sockAddr.sin6_port   = OPENTHREAD_POSIX_CONFIG_TREL_UDP_PORT;
189 
190     if (bind(sSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) == -1)
191     {
192         LogCrit("Failed to bind socket");
193         DieNow(OT_EXIT_ERROR_ERRNO);
194     }
195 
196     sockLen = sizeof(sockAddr);
197 
198     if (getsockname(sSocket, (struct sockaddr *)&sockAddr, &sockLen) == -1)
199     {
200         LogCrit("Failed to get the socket name");
201         DieNow(OT_EXIT_ERROR_ERRNO);
202     }
203 
204     aUdpPort = ntohs(sockAddr.sin6_port);
205 }
206 
SendPacket(const uint8_t * aBuffer,uint16_t aLength,const otSockAddr * aDestSockAddr)207 static otError SendPacket(const uint8_t *aBuffer, uint16_t aLength, const otSockAddr *aDestSockAddr)
208 {
209     otError             error = OT_ERROR_NONE;
210     struct sockaddr_in6 sockAddr;
211     ssize_t             ret;
212 
213     VerifyOrExit(sSocket >= 0, error = OT_ERROR_INVALID_STATE);
214 
215     memset(&sockAddr, 0, sizeof(sockAddr));
216     sockAddr.sin6_family = AF_INET6;
217     sockAddr.sin6_port   = htons(aDestSockAddr->mPort);
218     memcpy(&sockAddr.sin6_addr, &aDestSockAddr->mAddress, sizeof(otIp6Address));
219 
220     ret = sendto(sSocket, aBuffer, aLength, 0, (struct sockaddr *)&sockAddr, sizeof(sockAddr));
221 
222     if (ret != aLength)
223     {
224         LogDebg("SendPacket() -- sendto() failed errno %d", errno);
225 
226         switch (errno)
227         {
228         case ENETUNREACH:
229         case ENETDOWN:
230         case EHOSTUNREACH:
231             error = OT_ERROR_ABORT;
232             break;
233 
234         default:
235             error = OT_ERROR_INVALID_STATE;
236         }
237     }
238     else
239     {
240         ++sCounters.mTxPackets;
241         sCounters.mTxBytes += aLength;
242     }
243 
244 exit:
245     LogDebg("SendPacket([%s]:%u) err:%s pkt:%s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
246             otThreadErrorToString(error), BufferToString(aBuffer, aLength));
247     if (error != OT_ERROR_NONE)
248     {
249         ++sCounters.mTxFailure;
250     }
251     return error;
252 }
253 
ReceivePacket(int aSocket,otInstance * aInstance)254 static void ReceivePacket(int aSocket, otInstance *aInstance)
255 {
256     struct sockaddr_in6 sockAddr;
257     socklen_t           sockAddrLen = sizeof(sockAddr);
258     ssize_t             ret;
259 
260     memset(&sockAddr, 0, sizeof(sockAddr));
261 
262     ret = recvfrom(aSocket, (char *)sRxPacketBuffer, sizeof(sRxPacketBuffer), 0, (struct sockaddr *)&sockAddr,
263                    &sockAddrLen);
264     VerifyOrDie(ret >= 0, OT_EXIT_ERROR_ERRNO);
265 
266     sRxPacketLength = (uint16_t)(ret);
267 
268     if (sRxPacketLength > sizeof(sRxPacketBuffer))
269     {
270         sRxPacketLength = sizeof(sRxPacketLength);
271     }
272 
273     LogDebg("ReceivePacket() - received from [%s]:%d, id:%d, pkt:%s", Ip6AddrToString(&sockAddr.sin6_addr),
274             ntohs(sockAddr.sin6_port), sockAddr.sin6_scope_id, BufferToString(sRxPacketBuffer, sRxPacketLength));
275 
276     if (sEnabled)
277     {
278         ++sCounters.mRxPackets;
279         sCounters.mRxBytes += sRxPacketLength;
280         otPlatTrelHandleReceived(aInstance, sRxPacketBuffer, sRxPacketLength);
281     }
282 }
283 
InitPacketQueue(void)284 static void InitPacketQueue(void)
285 {
286     sTxPacketQueueTail = NULL;
287 
288     // Chain all the packets in pool in the free linked list.
289     sFreeTxPacketHead = NULL;
290 
291     for (uint16_t index = 0; index < OT_ARRAY_LENGTH(sTxPacketPool); index++)
292     {
293         TxPacket *packet = &sTxPacketPool[index];
294 
295         packet->mNext     = sFreeTxPacketHead;
296         sFreeTxPacketHead = packet;
297     }
298 }
299 
SendQueuedPackets(void)300 static void SendQueuedPackets(void)
301 {
302     while (sTxPacketQueueTail != NULL)
303     {
304         TxPacket *packet = sTxPacketQueueTail->mNext; // tail->mNext is the head of the list.
305 
306         if (SendPacket(packet->mBuffer, packet->mLength, &packet->mDestSockAddr) == OT_ERROR_INVALID_STATE)
307         {
308             LogDebg("SendQueuedPackets() - SendPacket() would block");
309             break;
310         }
311 
312         // Remove the `packet` from the packet queue (circular
313         // linked list).
314 
315         if (packet == sTxPacketQueueTail)
316         {
317             sTxPacketQueueTail = NULL;
318         }
319         else
320         {
321             sTxPacketQueueTail->mNext = packet->mNext;
322         }
323 
324         // Add the `packet` to the free packet singly linked list.
325 
326         packet->mNext     = sFreeTxPacketHead;
327         sFreeTxPacketHead = packet;
328     }
329 }
330 
EnqueuePacket(const uint8_t * aBuffer,uint16_t aLength,const otSockAddr * aDestSockAddr)331 static void EnqueuePacket(const uint8_t *aBuffer, uint16_t aLength, const otSockAddr *aDestSockAddr)
332 {
333     TxPacket *packet;
334 
335     // Allocate an available packet entry (from the free packet list)
336     // and copy the packet content into it.
337 
338     VerifyOrExit(sFreeTxPacketHead != NULL, LogWarn("EnqueuePacket failed, queue is full"));
339     packet            = sFreeTxPacketHead;
340     sFreeTxPacketHead = sFreeTxPacketHead->mNext;
341 
342     memcpy(packet->mBuffer, aBuffer, aLength);
343     packet->mLength       = aLength;
344     packet->mDestSockAddr = *aDestSockAddr;
345 
346     // Add packet to the tail of TxPacketQueue circular linked-list.
347 
348     if (sTxPacketQueueTail == NULL)
349     {
350         packet->mNext      = packet;
351         sTxPacketQueueTail = packet;
352     }
353     else
354     {
355         packet->mNext             = sTxPacketQueueTail->mNext;
356         sTxPacketQueueTail->mNext = packet;
357         sTxPacketQueueTail        = packet;
358     }
359 
360     LogDebg("EnqueuePacket([%s]:%u) - %s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
361             BufferToString(aBuffer, aLength));
362 
363 exit:
364     return;
365 }
366 
ResetCounters()367 static void ResetCounters() { memset(&sCounters, 0, sizeof(sCounters)); }
368 
369 //---------------------------------------------------------------------------------------------------------------------
370 // trelDnssd
371 //
372 // The functions below are tied to mDNS or DNS-SD library being used on
373 // a device and need to be implemented per project/platform. A weak empty
374 // implementation is provided here which describes the expected
375 // behavior. They need to be overridden during project/platform
376 // integration.
377 
trelDnssdInitialize(const char * aTrelNetif)378 OT_TOOL_WEAK void trelDnssdInitialize(const char *aTrelNetif)
379 {
380     // This function initialize the TREL DNS-SD module on the given
381     // TREL Network Interface.
382 
383     OT_UNUSED_VARIABLE(aTrelNetif);
384 }
385 
trelDnssdStartBrowse(void)386 OT_TOOL_WEAK void trelDnssdStartBrowse(void)
387 {
388     // This function initiates an ongoing DNS-SD browse on the service
389     // name "_trel._udp" within the local browsing domain to discover
390     // other devices supporting TREL. The ongoing browse will produce
391     // two different types of events: `add` events and `remove` events.
392     // When the browse is started, it should produce an `add` event for
393     // every TREL peer currently present on the network. Whenever a
394     // TREL peer goes offline, a "remove" event should be produced.
395     // `Remove` events are not guaranteed, however. When a TREL service
396     // instance is discovered, a new ongoing DNS-SD query for an AAAA
397     // record MUST be started on the hostname indicated in the SRV
398     // record of the discovered instance. If multiple host IPv6
399     // addressees are discovered for a peer, one with highest scope
400     // among all addresses MUST be reported (if there are multiple
401     // address at same scope, one must be selected randomly).
402     //
403     // The platform MUST signal back the discovered peer info using
404     // `otPlatTrelHandleDiscoveredPeerInfo()` callback. This callback
405     // MUST be invoked when a new peer is discovered, or when there is
406     // a change in an existing entry (e.g., new TXT record or new port
407     // number or new IPv6 address), or when the peer is removed.
408 }
409 
trelDnssdStopBrowse(void)410 OT_TOOL_WEAK void trelDnssdStopBrowse(void)
411 {
412     // This function stops the ongoing DNS-SD browse started from an
413     // earlier call to `trelDnssdStartBrowse()`.
414 }
415 
trelDnssdRegisterService(uint16_t aPort,const uint8_t * aTxtData,uint8_t aTxtLength)416 OT_TOOL_WEAK void trelDnssdRegisterService(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
417 {
418     // This function registers a new service to be advertised using
419     // DNS-SD.
420     //
421     // The service name is "_trel._udp". The platform should use its own
422     // hostname, which when combined with the service name and the
423     // local DNS-SD domain name will produce the full service instance
424     // name, for example "example-host._trel._udp.local.".
425     //
426     // The domain under which the service instance name appears will
427     // be 'local' for mDNS, and will be whatever domain is used for
428     // service registration in the case of a non-mDNS local DNS-SD
429     // service.
430     //
431     // A subsequent call to this function updates the previous service.
432     // It is used to update the TXT record data and/or the port
433     // number.
434     //
435     // The `aTxtData` buffer is not persisted after the return from this
436     // function. The platform layer MUST not keep the pointer and
437     // instead copy the content if needed.
438 
439     OT_UNUSED_VARIABLE(aPort);
440     OT_UNUSED_VARIABLE(aTxtData);
441     OT_UNUSED_VARIABLE(aTxtLength);
442 }
443 
trelDnssdRemoveService(void)444 OT_TOOL_WEAK void trelDnssdRemoveService(void)
445 {
446     // This function removes any previously registered "_trel._udp"
447     // service using `platTrelRegisterService()`. Device must stop
448     // advertising TREL service after this call.
449 }
450 
trelDnssdUpdateFdSet(otSysMainloopContext * aContext)451 OT_TOOL_WEAK void trelDnssdUpdateFdSet(otSysMainloopContext *aContext)
452 {
453     // This function can be used to update the file descriptor sets
454     // by DNS-SD layer (if needed).
455 
456     OT_UNUSED_VARIABLE(aContext);
457 }
458 
trelDnssdProcess(otInstance * aInstance,const otSysMainloopContext * aContext)459 OT_TOOL_WEAK void trelDnssdProcess(otInstance *aInstance, const otSysMainloopContext *aContext)
460 {
461     // This function performs processing by DNS-SD (if needed).
462 
463     OT_UNUSED_VARIABLE(aInstance);
464     OT_UNUSED_VARIABLE(aContext);
465 }
466 
467 //---------------------------------------------------------------------------------------------------------------------
468 // otPlatTrel
469 
otPlatTrelEnable(otInstance * aInstance,uint16_t * aUdpPort)470 void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort)
471 {
472     OT_UNUSED_VARIABLE(aInstance);
473 
474     VerifyOrExit(!IsSystemDryRun());
475 
476     assert(sInitialized);
477 
478     VerifyOrExit(!sEnabled);
479 
480     PrepareSocket(*aUdpPort);
481     trelDnssdStartBrowse();
482 
483     sEnabled = true;
484 
485 exit:
486     return;
487 }
488 
otPlatTrelDisable(otInstance * aInstance)489 void otPlatTrelDisable(otInstance *aInstance)
490 {
491     OT_UNUSED_VARIABLE(aInstance);
492 
493     VerifyOrExit(!IsSystemDryRun());
494 
495     assert(sInitialized);
496     VerifyOrExit(sEnabled);
497 
498     close(sSocket);
499     sSocket = -1;
500     trelDnssdStopBrowse();
501     trelDnssdRemoveService();
502     sEnabled = false;
503 
504 exit:
505     return;
506 }
507 
otPlatTrelSend(otInstance * aInstance,const uint8_t * aUdpPayload,uint16_t aUdpPayloadLen,const otSockAddr * aDestSockAddr)508 void otPlatTrelSend(otInstance       *aInstance,
509                     const uint8_t    *aUdpPayload,
510                     uint16_t          aUdpPayloadLen,
511                     const otSockAddr *aDestSockAddr)
512 {
513     OT_UNUSED_VARIABLE(aInstance);
514 
515     VerifyOrExit(!IsSystemDryRun());
516 
517     VerifyOrExit(sEnabled);
518 
519     assert(aUdpPayloadLen <= kMaxPacketSize);
520 
521     // We try to send the packet immediately. If it fails (e.g.,
522     // network is down) `SendPacket()` returns `OT_ERROR_ABORT`. If
523     // the send operation would block (e.g., socket is not yet ready
524     // or is out of buffer) we get `OT_ERROR_INVALID_STATE`. In that
525     // case we enqueue the packet to send it later when socket becomes
526     // ready.
527 
528     if ((sTxPacketQueueTail != NULL) ||
529         (SendPacket(aUdpPayload, aUdpPayloadLen, aDestSockAddr) == OT_ERROR_INVALID_STATE))
530     {
531         EnqueuePacket(aUdpPayload, aUdpPayloadLen, aDestSockAddr);
532     }
533 
534 exit:
535     return;
536 }
537 
otPlatTrelRegisterService(otInstance * aInstance,uint16_t aPort,const uint8_t * aTxtData,uint8_t aTxtLength)538 void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
539 {
540     OT_UNUSED_VARIABLE(aInstance);
541     VerifyOrExit(!IsSystemDryRun());
542 
543     trelDnssdRegisterService(aPort, aTxtData, aTxtLength);
544 
545 exit:
546     return;
547 }
548 
549 // We keep counters at the platform layer because TREL failures can only be captured accurately within
550 // the platform layer as the platform sometimes only queues the packet and the packet will be sent later
551 // and the error is only known after sent.
otPlatTrelGetCounters(otInstance * aInstance)552 const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance)
553 {
554     OT_UNUSED_VARIABLE(aInstance);
555     return &sCounters;
556 }
557 
otPlatTrelResetCounters(otInstance * aInstance)558 void otPlatTrelResetCounters(otInstance *aInstance)
559 {
560     OT_UNUSED_VARIABLE(aInstance);
561     ResetCounters();
562 }
563 
564 //---------------------------------------------------------------------------------------------------------------------
565 // platformTrel system
566 
platformTrelInit(const char * aTrelUrl)567 void platformTrelInit(const char *aTrelUrl)
568 {
569     // To silence "unused function" warning.
570     (void)LogCrit;
571     (void)LogWarn;
572     (void)LogInfo;
573     (void)LogNote;
574     (void)LogDebg;
575 
576     LogDebg("platformTrelInit(aTrelUrl:\"%s\")", aTrelUrl != nullptr ? aTrelUrl : "");
577 
578     assert(!sInitialized);
579 
580     if (aTrelUrl != nullptr)
581     {
582         ot::Posix::RadioUrl url(aTrelUrl);
583 
584         strncpy(sInterfaceName, url.GetPath(), sizeof(sInterfaceName) - 1);
585         sInterfaceName[sizeof(sInterfaceName) - 1] = '\0';
586     }
587 
588     trelDnssdInitialize(sInterfaceName);
589 
590     InitPacketQueue();
591     sInitialized = true;
592 
593     ResetCounters();
594 }
595 
platformTrelDeinit(void)596 void platformTrelDeinit(void)
597 {
598     VerifyOrExit(sInitialized);
599 
600     otPlatTrelDisable(nullptr);
601     sInterfaceName[0] = '\0';
602     sInitialized      = false;
603     LogDebg("platformTrelDeinit()");
604 
605 exit:
606     return;
607 }
608 
platformTrelUpdateFdSet(otSysMainloopContext * aContext)609 void platformTrelUpdateFdSet(otSysMainloopContext *aContext)
610 {
611     assert(aContext != nullptr);
612 
613     VerifyOrExit(sEnabled);
614 
615     FD_SET(sSocket, &aContext->mReadFdSet);
616 
617     if (sTxPacketQueueTail != nullptr)
618     {
619         FD_SET(sSocket, &aContext->mWriteFdSet);
620     }
621 
622     if (aContext->mMaxFd < sSocket)
623     {
624         aContext->mMaxFd = sSocket;
625     }
626 
627     trelDnssdUpdateFdSet(aContext);
628 
629 exit:
630     return;
631 }
632 
platformTrelProcess(otInstance * aInstance,const otSysMainloopContext * aContext)633 void platformTrelProcess(otInstance *aInstance, const otSysMainloopContext *aContext)
634 {
635     VerifyOrExit(sEnabled);
636 
637     if (FD_ISSET(sSocket, &aContext->mWriteFdSet))
638     {
639         SendQueuedPackets();
640     }
641 
642     if (FD_ISSET(sSocket, &aContext->mReadFdSet))
643     {
644         ReceivePacket(sSocket, aInstance);
645     }
646 
647     trelDnssdProcess(aInstance, aContext);
648 
649 exit:
650     return;
651 }
652 
653 #endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
654