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