• 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 "radio_url.hpp"
49 #include "system.hpp"
50 #include "common/code_utils.hpp"
51 
52 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
53 
54 #define TREL_MAX_PACKET_SIZE 1400
55 #define TREL_PACKET_POOL_SIZE 5
56 
57 typedef struct TxPacket
58 {
59     struct TxPacket *mNext;
60     uint8_t          mBuffer[TREL_MAX_PACKET_SIZE];
61     uint16_t         mLength;
62     otSockAddr       mDestSockAddr;
63 } TxPacket;
64 
65 static uint8_t   sRxPacketBuffer[TREL_MAX_PACKET_SIZE];
66 static uint16_t  sRxPacketLength;
67 static TxPacket  sTxPacketPool[TREL_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 
71 static char sInterfaceName[IFNAMSIZ + 1];
72 static bool sInitialized = false;
73 static bool sEnabled     = false;
74 static int  sSocket      = -1;
75 
Ip6AddrToString(const void * aAddress)76 static const char *Ip6AddrToString(const void *aAddress)
77 {
78     static char string[INET6_ADDRSTRLEN];
79     return inet_ntop(AF_INET6, aAddress, string, sizeof(string));
80 }
81 
BufferToString(const uint8_t * aBuffer,uint16_t aLength)82 static const char *BufferToString(const uint8_t *aBuffer, uint16_t aLength)
83 {
84     const uint16_t kMaxWrite = 16;
85     static char    string[1600];
86 
87     uint16_t num = 0;
88     char *   cur = &string[0];
89     char *   end = &string[sizeof(string) - 1];
90 
91     cur += snprintf(cur, (uint16_t)(end - cur), "[(len:%d) ", aLength);
92     VerifyOrExit(cur < end);
93 
94     while (aLength-- && (num < kMaxWrite))
95     {
96         cur += snprintf(cur, (uint16_t)(end - cur), "%02x ", *aBuffer++);
97         VerifyOrExit(cur < end);
98 
99         num++;
100     }
101 
102     if (aLength != 0)
103     {
104         cur += snprintf(cur, (uint16_t)(end - cur), "... ");
105         VerifyOrExit(cur < end);
106     }
107 
108     *cur++ = ']';
109     VerifyOrExit(cur < end);
110 
111     *cur = '\0';
112 
113 exit:
114     *end = '\0';
115     return string;
116 }
117 
PrepareSocket(uint16_t & aUdpPort)118 static void PrepareSocket(uint16_t &aUdpPort)
119 {
120     int                 val;
121     struct sockaddr_in6 sockAddr;
122     socklen_t           sockLen;
123 
124     otLogDebgPlat("[trel] PrepareSocket()");
125 
126     sSocket = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, 0, kSocketNonBlock);
127     VerifyOrDie(sSocket >= 0, OT_EXIT_ERROR_ERRNO);
128 
129     // Make the socket non-blocking to allow immediate tx attempt.
130     val = fcntl(sSocket, F_GETFL, 0);
131     VerifyOrDie(val != -1, OT_EXIT_ERROR_ERRNO);
132     val = val | O_NONBLOCK;
133     VerifyOrDie(fcntl(sSocket, F_SETFL, val) == 0, OT_EXIT_ERROR_ERRNO);
134 
135     // Bind the socket.
136 
137     memset(&sockAddr, 0, sizeof(sockAddr));
138     sockAddr.sin6_family = AF_INET6;
139     sockAddr.sin6_addr   = in6addr_any;
140     sockAddr.sin6_port   = OPENTHREAD_POSIX_CONFIG_TREL_UDP_PORT;
141 
142     if (bind(sSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) == -1)
143     {
144         otLogCritPlat("[trel] Failed to bind socket");
145         DieNow(OT_EXIT_ERROR_ERRNO);
146     }
147 
148     sockLen = sizeof(sockAddr);
149 
150     if (getsockname(sSocket, (struct sockaddr *)&sockAddr, &sockLen) == -1)
151     {
152         otLogCritPlat("[trel] Failed to get the socket name");
153         DieNow(OT_EXIT_ERROR_ERRNO);
154     }
155 
156     aUdpPort = ntohs(sockAddr.sin6_port);
157 }
158 
SendPacket(const uint8_t * aBuffer,uint16_t aLength,const otSockAddr * aDestSockAddr)159 static otError SendPacket(const uint8_t *aBuffer, uint16_t aLength, const otSockAddr *aDestSockAddr)
160 {
161     otError             error = OT_ERROR_NONE;
162     struct sockaddr_in6 sockAddr;
163     ssize_t             ret;
164 
165     VerifyOrExit(sSocket >= 0, error = OT_ERROR_INVALID_STATE);
166 
167     memset(&sockAddr, 0, sizeof(sockAddr));
168     sockAddr.sin6_family = AF_INET6;
169     sockAddr.sin6_port   = htons(aDestSockAddr->mPort);
170     memcpy(&sockAddr.sin6_addr, &aDestSockAddr->mAddress, sizeof(otIp6Address));
171 
172     ret = sendto(sSocket, aBuffer, aLength, 0, (struct sockaddr *)&sockAddr, sizeof(sockAddr));
173 
174     if (ret != aLength)
175     {
176         otLogDebgPlat("[trel] SendPacket() -- sendto() failed errno %d", errno);
177 
178         switch (errno)
179         {
180         case ENETUNREACH:
181         case ENETDOWN:
182         case EHOSTUNREACH:
183             error = OT_ERROR_ABORT;
184             break;
185 
186         default:
187             error = OT_ERROR_INVALID_STATE;
188         }
189     }
190 
191 exit:
192     otLogDebgPlat("[trel] SendPacket([%s]:%u) err:%s pkt:%s", Ip6AddrToString(&aDestSockAddr->mAddress),
193                   aDestSockAddr->mPort, otThreadErrorToString(error), BufferToString(aBuffer, aLength));
194 
195     return error;
196 }
197 
ReceivePacket(int aSocket,otInstance * aInstance)198 static void ReceivePacket(int aSocket, otInstance *aInstance)
199 {
200     struct sockaddr_in6 sockAddr;
201     socklen_t           sockAddrLen = sizeof(sockAddr);
202     ssize_t             ret;
203 
204     memset(&sockAddr, 0, sizeof(sockAddr));
205 
206     ret = recvfrom(aSocket, (char *)sRxPacketBuffer, sizeof(sRxPacketBuffer), 0, (struct sockaddr *)&sockAddr,
207                    &sockAddrLen);
208     VerifyOrDie(ret >= 0, OT_EXIT_ERROR_ERRNO);
209 
210     sRxPacketLength = (uint16_t)(ret);
211 
212     if (sRxPacketLength > sizeof(sRxPacketBuffer))
213     {
214         sRxPacketLength = sizeof(sRxPacketLength);
215     }
216 
217     otLogDebgPlat("[trel] ReceivePacket() - received from [%s]:%d, id:%d, pkt:%s", Ip6AddrToString(&sockAddr.sin6_addr),
218                   ntohs(sockAddr.sin6_port), sockAddr.sin6_scope_id, BufferToString(sRxPacketBuffer, sRxPacketLength));
219 
220     if (sEnabled)
221     {
222         otPlatTrelHandleReceived(aInstance, sRxPacketBuffer, sRxPacketLength);
223     }
224 }
225 
InitPacketQueue(void)226 static void InitPacketQueue(void)
227 {
228     sTxPacketQueueTail = NULL;
229 
230     // Chain all the packets in pool in the free linked list.
231     sFreeTxPacketHead = NULL;
232 
233     for (uint16_t index = 0; index < OT_ARRAY_LENGTH(sTxPacketPool); index++)
234     {
235         TxPacket *packet = &sTxPacketPool[index];
236 
237         packet->mNext     = sFreeTxPacketHead;
238         sFreeTxPacketHead = packet;
239     }
240 }
241 
SendQueuedPackets(void)242 static void SendQueuedPackets(void)
243 {
244     while (sTxPacketQueueTail != NULL)
245     {
246         TxPacket *packet = sTxPacketQueueTail->mNext; // tail->mNext is the head of the list.
247 
248         if (SendPacket(packet->mBuffer, packet->mLength, &packet->mDestSockAddr) == OT_ERROR_INVALID_STATE)
249         {
250             otLogDebgPlat("[trel] SendQueuedPackets() - SendPacket() would block");
251             break;
252         }
253 
254         // Remove the `packet` from the packet queue (circular
255         // linked list).
256 
257         if (packet == sTxPacketQueueTail)
258         {
259             sTxPacketQueueTail = NULL;
260         }
261         else
262         {
263             sTxPacketQueueTail->mNext = packet->mNext;
264         }
265 
266         // Add the `packet` to the free packet singly linked list.
267 
268         packet->mNext     = sFreeTxPacketHead;
269         sFreeTxPacketHead = packet;
270     }
271 }
272 
EnqueuePacket(const uint8_t * aBuffer,uint16_t aLength,const otSockAddr * aDestSockAddr)273 static void EnqueuePacket(const uint8_t *aBuffer, uint16_t aLength, const otSockAddr *aDestSockAddr)
274 {
275     TxPacket *packet;
276 
277     // Allocate an available packet entry (from the free packet list)
278     // and copy the packet content into it.
279 
280     VerifyOrExit(sFreeTxPacketHead != NULL, otLogWarnPlat("[trel] EnqueuePacket failed, queue is full"));
281     packet            = sFreeTxPacketHead;
282     sFreeTxPacketHead = sFreeTxPacketHead->mNext;
283 
284     memcpy(packet->mBuffer, aBuffer, aLength);
285     packet->mLength       = aLength;
286     packet->mDestSockAddr = *aDestSockAddr;
287 
288     // Add packet to the tail of TxPacketQueue circular linked-list.
289 
290     if (sTxPacketQueueTail == NULL)
291     {
292         packet->mNext      = packet;
293         sTxPacketQueueTail = packet;
294     }
295     else
296     {
297         packet->mNext             = sTxPacketQueueTail->mNext;
298         sTxPacketQueueTail->mNext = packet;
299         sTxPacketQueueTail        = packet;
300     }
301 
302     otLogDebgPlat("[trel] EnqueuePacket([%s]:%u) - %s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
303                   BufferToString(aBuffer, aLength));
304 
305 exit:
306     return;
307 }
308 
309 //---------------------------------------------------------------------------------------------------------------------
310 // trelDnssd
311 //
312 // The functions below are tied to mDNS or DNS-SD library being used on
313 // a device and need to be implemented per project/platform. A weak empty
314 // implementation is provided here which describes the expected
315 // behavior. They need to be overridden during project/platform
316 // integration.
317 
trelDnssdInitialize(const char * aTrelNetif)318 OT_TOOL_WEAK void trelDnssdInitialize(const char *aTrelNetif)
319 {
320     // This function initialize the TREL DNS-SD module on the given
321     // TREL Network Interface.
322 
323     OT_UNUSED_VARIABLE(aTrelNetif);
324 }
325 
trelDnssdStartBrowse(void)326 OT_TOOL_WEAK void trelDnssdStartBrowse(void)
327 {
328     // This function initiates an ongoing DNS-SD browse on the service
329     // name "_trel._udp" within the local browsing domain to discover
330     // other devices supporting TREL. The ongoing browse will produce
331     // two different types of events: `add` events and `remove` events.
332     // When the browse is started, it should produce an `add` event for
333     // every TREL peer currently present on the network. Whenever a
334     // TREL peer goes offline, a "remove" event should be produced.
335     // `Remove` events are not guaranteed, however. When a TREL service
336     // instance is discovered, a new ongoing DNS-SD query for an AAAA
337     // record MUST be started on the hostname indicated in the SRV
338     // record of the discovered instance. If multiple host IPv6
339     // addressees are discovered for a peer, one with highest scope
340     // among all addresses MUST be reported (if there are multiple
341     // address at same scope, one must be selected randomly).
342     //
343     // The platform MUST signal back the discovered peer info using
344     // `otPlatTrelHandleDiscoveredPeerInfo()` callback. This callback
345     // MUST be invoked when a new peer is discovered, or when there is
346     // a change in an existing entry (e.g., new TXT record or new port
347     // number or new IPv6 address), or when the peer is removed.
348 }
349 
trelDnssdStopBrowse(void)350 OT_TOOL_WEAK void trelDnssdStopBrowse(void)
351 {
352     // This function stops the ongoing DNS-SD browse started from an
353     // earlier call to `trelDnssdStartBrowse()`.
354 }
355 
trelDnssdRegisterService(uint16_t aPort,const uint8_t * aTxtData,uint8_t aTxtLength)356 OT_TOOL_WEAK void trelDnssdRegisterService(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
357 {
358     // This function registers a new service to be advertised using
359     // DNS-SD.
360     //
361     // The service name is "_trel._udp". The platform should use its own
362     // hostname, which when combined with the service name and the
363     // local DNS-SD domain name will produce the full service instance
364     // name, for example "example-host._trel._udp.local.".
365     //
366     // The domain under which the service instance name appears will
367     // be 'local' for mDNS, and will be whatever domain is used for
368     // service registration in the case of a non-mDNS local DNS-SD
369     // service.
370     //
371     // A subsequent call to this function updates the previous service.
372     // It is used to update the TXT record data and/or the port
373     // number.
374     //
375     // The `aTxtData` buffer is not persisted after the return from this
376     // function. The platform layer MUST not keep the pointer and
377     // instead copy the content if needed.
378 
379     OT_UNUSED_VARIABLE(aPort);
380     OT_UNUSED_VARIABLE(aTxtData);
381     OT_UNUSED_VARIABLE(aTxtLength);
382 }
383 
trelDnssdRemoveService(void)384 OT_TOOL_WEAK void trelDnssdRemoveService(void)
385 {
386     // This function removes any previously registered "_trel._udp"
387     // service using `platTrelRegisterService()`. Device must stop
388     // advertising TREL service after this call.
389 }
390 
trelDnssdUpdateFdSet(fd_set * aReadFdSet,fd_set * aWriteFdSet,int * aMaxFd,struct timeval * aTimeout)391 OT_TOOL_WEAK void trelDnssdUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout)
392 {
393     // This function can be used to update the file descriptor sets
394     // by DNS-SD layer (if needed).
395 
396     OT_UNUSED_VARIABLE(aReadFdSet);
397     OT_UNUSED_VARIABLE(aWriteFdSet);
398     OT_UNUSED_VARIABLE(aMaxFd);
399     OT_UNUSED_VARIABLE(aTimeout);
400 }
401 
trelDnssdProcess(otInstance * aInstance,const fd_set * aReadFdSet,const fd_set * aWriteFdSet)402 OT_TOOL_WEAK void trelDnssdProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
403 {
404     // This function performs processing by DNS-SD (if needed).
405 
406     OT_UNUSED_VARIABLE(aInstance);
407     OT_UNUSED_VARIABLE(aReadFdSet);
408     OT_UNUSED_VARIABLE(aWriteFdSet);
409 }
410 
411 //---------------------------------------------------------------------------------------------------------------------
412 // otPlatTrel
413 
otPlatTrelEnable(otInstance * aInstance,uint16_t * aUdpPort)414 void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort)
415 {
416     OT_UNUSED_VARIABLE(aInstance);
417 
418     VerifyOrExit(!IsSystemDryRun());
419 
420     assert(sInitialized);
421 
422     VerifyOrExit(!sEnabled);
423 
424     PrepareSocket(*aUdpPort);
425     trelDnssdStartBrowse();
426 
427     sEnabled = true;
428 
429 exit:
430     return;
431 }
432 
otPlatTrelDisable(otInstance * aInstance)433 void otPlatTrelDisable(otInstance *aInstance)
434 {
435     OT_UNUSED_VARIABLE(aInstance);
436 
437     VerifyOrExit(!IsSystemDryRun());
438 
439     assert(sInitialized);
440     VerifyOrExit(sEnabled);
441 
442     close(sSocket);
443     sSocket = -1;
444     trelDnssdStopBrowse();
445     trelDnssdRemoveService();
446     sEnabled = false;
447 
448 exit:
449     return;
450 }
451 
otPlatTrelSend(otInstance * aInstance,const uint8_t * aUdpPayload,uint16_t aUdpPayloadLen,const otSockAddr * aDestSockAddr)452 void otPlatTrelSend(otInstance *      aInstance,
453                     const uint8_t *   aUdpPayload,
454                     uint16_t          aUdpPayloadLen,
455                     const otSockAddr *aDestSockAddr)
456 {
457     OT_UNUSED_VARIABLE(aInstance);
458 
459     VerifyOrExit(!IsSystemDryRun());
460 
461     VerifyOrExit(sEnabled);
462 
463     assert(aUdpPayloadLen <= TREL_MAX_PACKET_SIZE);
464 
465     // We try to send the packet immediately. If it fails (e.g.,
466     // network is down) `SendPacket()` returns `OT_ERROR_ABORT`. If
467     // the send operation would block (e.g., socket is not yet ready
468     // or is out of buffer) we get `OT_ERROR_INVALID_STATE`. In that
469     // case we enqueue the packet to send it later when socket becomes
470     // ready.
471 
472     if ((sTxPacketQueueTail != NULL) ||
473         (SendPacket(aUdpPayload, aUdpPayloadLen, aDestSockAddr) == OT_ERROR_INVALID_STATE))
474     {
475         EnqueuePacket(aUdpPayload, aUdpPayloadLen, aDestSockAddr);
476     }
477 
478 exit:
479     return;
480 }
481 
otPlatTrelRegisterService(otInstance * aInstance,uint16_t aPort,const uint8_t * aTxtData,uint8_t aTxtLength)482 void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
483 {
484     OT_UNUSED_VARIABLE(aInstance);
485     VerifyOrExit(!IsSystemDryRun());
486 
487     trelDnssdRegisterService(aPort, aTxtData, aTxtLength);
488 
489 exit:
490     return;
491 }
492 
493 //---------------------------------------------------------------------------------------------------------------------
494 // platformTrel system
495 
platformTrelInit(const char * aTrelUrl)496 void platformTrelInit(const char *aTrelUrl)
497 {
498     otLogDebgPlat("[trel] platformTrelInit(aTrelUrl:\"%s\")", aTrelUrl != nullptr ? aTrelUrl : "");
499 
500     assert(!sInitialized);
501 
502     if (aTrelUrl != nullptr)
503     {
504         ot::Posix::RadioUrl url(aTrelUrl);
505         strncpy(sInterfaceName, url.GetPath(), sizeof(sInterfaceName) - 1);
506         sInterfaceName[sizeof(sInterfaceName) - 1] = '\0';
507     }
508 
509     trelDnssdInitialize(sInterfaceName);
510 
511     InitPacketQueue();
512     sInitialized = true;
513 }
514 
platformTrelDeinit(void)515 void platformTrelDeinit(void)
516 {
517     VerifyOrExit(sInitialized);
518 
519     otPlatTrelDisable(nullptr);
520     sInterfaceName[0] = '\0';
521     sInitialized      = false;
522     otLogDebgPlat("[trel] platformTrelDeinit()");
523 
524 exit:
525     return;
526 }
527 
platformTrelUpdateFdSet(fd_set * aReadFdSet,fd_set * aWriteFdSet,int * aMaxFd,struct timeval * aTimeout)528 void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout)
529 {
530     assert((aReadFdSet != NULL) && (aWriteFdSet != NULL) && (aMaxFd != NULL) && (aTimeout != NULL));
531 
532     VerifyOrExit(sEnabled);
533 
534     FD_SET(sSocket, aReadFdSet);
535 
536     if (sTxPacketQueueTail != NULL)
537     {
538         FD_SET(sSocket, aWriteFdSet);
539     }
540 
541     if (*aMaxFd < sSocket)
542     {
543         *aMaxFd = sSocket;
544     }
545 
546     trelDnssdUpdateFdSet(aReadFdSet, aWriteFdSet, aMaxFd, aTimeout);
547 
548 exit:
549     return;
550 }
551 
platformTrelProcess(otInstance * aInstance,const fd_set * aReadFdSet,const fd_set * aWriteFdSet)552 void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
553 {
554     VerifyOrExit(sEnabled);
555 
556     if (FD_ISSET(sSocket, aWriteFdSet))
557     {
558         SendQueuedPackets();
559     }
560 
561     if (FD_ISSET(sSocket, aReadFdSet))
562     {
563         ReceivePacket(sSocket, aInstance);
564     }
565 
566     trelDnssdProcess(aInstance, aReadFdSet, aWriteFdSet);
567 
568 exit:
569     return;
570 }
571 
572 #endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
573