1 /*
2 * Copyright (c) 2019-21, 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 #include "platform-simulation.h"
30
31 #include <openthread/random_noncrypto.h>
32 #include <openthread/platform/trel.h>
33
34 #include "utils/code_utils.h"
35
36 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
37
38 // Change DEBUG_LOG to all extra logging
39 #define DEBUG_LOG 0
40
41 // The IPv4 group for receiving
42 #define TREL_SIM_GROUP "224.0.0.116"
43 #define TREL_SIM_PORT 9200
44
45 #define TREL_MAX_PACKET_SIZE 1800
46
47 #define TREL_MAX_PENDING_TX 64
48
49 #define TREL_MAX_SERVICE_TXT_DATA_LEN 128
50
51 typedef enum MessageType
52 {
53 TREL_DATA_MESSAGE,
54 TREL_DNSSD_BROWSE_MESSAGE,
55 TREL_DNSSD_ADD_SERVICE_MESSAGE,
56 TREL_DNSSD_REMOVE_SERVICE_MESSAGE,
57 } MessageType;
58
59 typedef struct Message
60 {
61 MessageType mType;
62 otSockAddr mSockAddr; // Destination (when TREL_DATA_MESSAGE), or peer addr (when DNS-SD service)
63 uint16_t mDataLength; // mData length
64 uint8_t mData[TREL_MAX_PACKET_SIZE]; // TREL UDP packet (when TREL_DATA_MESSAGE), or service TXT data.
65 } Message;
66
67 static uint8_t sNumPendingTx = 0;
68 static Message sPendingTx[TREL_MAX_PENDING_TX];
69
70 static int sTxFd = -1;
71 static int sRxFd = -1;
72 static uint16_t sPortOffset = 0;
73 static bool sEnabled = false;
74 static uint16_t sUdpPort;
75
76 static bool sServiceRegistered = false;
77 static uint16_t sServicePort;
78 static uint8_t sServiceTxtLength;
79 static char sServiceTxtData[TREL_MAX_SERVICE_TXT_DATA_LEN];
80
81 #if DEBUG_LOG
dumpBuffer(const void * aBuffer,uint16_t aLength)82 static void dumpBuffer(const void *aBuffer, uint16_t aLength)
83 {
84 const uint8_t *buffer = (const uint8_t *)aBuffer;
85 fprintf(stderr, "[ (len:%d) ", aLength);
86
87 while (aLength--)
88 {
89 fprintf(stderr, "%02x ", *buffer++);
90 }
91
92 fprintf(stderr, "]");
93 }
94
messageTypeToString(MessageType aType)95 static const char *messageTypeToString(MessageType aType)
96 {
97 const char *str = "unknown";
98
99 switch (aType)
100 {
101 case TREL_DATA_MESSAGE:
102 str = "data";
103 break;
104 case TREL_DNSSD_BROWSE_MESSAGE:
105 str = "browse";
106 break;
107 case TREL_DNSSD_ADD_SERVICE_MESSAGE:
108 str = "add-service";
109 break;
110 case TREL_DNSSD_REMOVE_SERVICE_MESSAGE:
111 str = "remove-service";
112 break;
113 }
114
115 return str;
116 }
117 #endif
118
initFds(void)119 static void initFds(void)
120 {
121 int fd;
122 int one = 1;
123 struct sockaddr_in sockaddr;
124 struct ip_mreqn mreq;
125
126 memset(&sockaddr, 0, sizeof(sockaddr));
127
128 otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)"));
129
130 sUdpPort = (uint16_t)(TREL_SIM_PORT + sPortOffset + gNodeId);
131 sockaddr.sin_family = AF_INET;
132 sockaddr.sin_port = htons(sUdpPort);
133 sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
134
135 otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)) != -1,
136 perror("setsockopt(sTxFd, IP_MULTICAST_IF)"));
137
138 otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)) != -1,
139 perror("setsockopt(sTxFd, IP_MULTICAST_LOOP)"));
140
141 otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sTxFd)"));
142
143 // Tx fd is successfully initialized.
144 sTxFd = fd;
145
146 otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sRxFd)"));
147
148 otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1,
149 perror("setsockopt(sRxFd, SO_REUSEADDR)"));
150 otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1,
151 perror("setsockopt(sRxFd, SO_REUSEPORT)"));
152
153 memset(&mreq, 0, sizeof(mreq));
154 inet_pton(AF_INET, TREL_SIM_GROUP, &mreq.imr_multiaddr);
155
156 // Always use loopback device to send simulation packets.
157 mreq.imr_address.s_addr = inet_addr("127.0.0.1");
158
159 otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)) != -1,
160 perror("setsockopt(sRxFd, IP_MULTICAST_IF)"));
161 otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1,
162 perror("setsockopt(sRxFd, IP_ADD_MEMBERSHIP)"));
163
164 sockaddr.sin_family = AF_INET;
165 sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset));
166 sockaddr.sin_addr.s_addr = inet_addr(TREL_SIM_GROUP);
167
168 otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)"));
169
170 // Rx fd is successfully initialized.
171 sRxFd = fd;
172
173 exit:
174 if (sRxFd == -1 || sTxFd == -1)
175 {
176 exit(EXIT_FAILURE);
177 }
178 }
179
deinitFds(void)180 static void deinitFds(void)
181 {
182 if (sRxFd != -1)
183 {
184 close(sRxFd);
185 }
186
187 if (sTxFd != -1)
188 {
189 close(sTxFd);
190 }
191 }
192
getMessageSize(const Message * aMessage)193 static uint16_t getMessageSize(const Message *aMessage)
194 {
195 return (uint16_t)(&aMessage->mData[aMessage->mDataLength] - (const uint8_t *)aMessage);
196 }
197
sendPendingTxMessages(void)198 static void sendPendingTxMessages(void)
199 {
200 ssize_t rval;
201 struct sockaddr_in sockaddr;
202
203 memset(&sockaddr, 0, sizeof(sockaddr));
204 sockaddr.sin_family = AF_INET;
205 inet_pton(AF_INET, TREL_SIM_GROUP, &sockaddr.sin_addr);
206
207 sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset));
208
209 for (uint8_t i = 0; i < sNumPendingTx; i++)
210 {
211 uint16_t size = getMessageSize(&sPendingTx[i]);
212
213 #if DEBUG_LOG
214 fprintf(stderr, "\r\n[trel-sim] Sending message (num:%d, type:%s, port:%u)\r\n", i,
215 messageTypeToString(sPendingTx[i].mType), sPendingTx[i].mSockAddr.mPort);
216 #endif
217
218 rval = sendto(sTxFd, &sPendingTx[i], size, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
219
220 if (rval < 0)
221 {
222 perror("sendto(sTxFd)");
223 exit(EXIT_FAILURE);
224 }
225 }
226
227 sNumPendingTx = 0;
228 }
229
sendBrowseMessage(void)230 static void sendBrowseMessage(void)
231 {
232 Message *message;
233
234 assert(sNumPendingTx < TREL_MAX_PENDING_TX);
235 message = &sPendingTx[sNumPendingTx++];
236
237 message->mType = TREL_DNSSD_BROWSE_MESSAGE;
238 message->mDataLength = 0;
239
240 #if DEBUG_LOG
241 fprintf(stderr, "\r\n[trel-sim] sendBrowseMessage()\r\n");
242 #endif
243 }
244
sendServiceMessage(MessageType aType)245 static void sendServiceMessage(MessageType aType)
246 {
247 Message *message;
248
249 assert((aType == TREL_DNSSD_ADD_SERVICE_MESSAGE) || (aType == TREL_DNSSD_REMOVE_SERVICE_MESSAGE));
250
251 assert(sNumPendingTx < TREL_MAX_PENDING_TX);
252 message = &sPendingTx[sNumPendingTx++];
253
254 message->mType = aType;
255 memset(&message->mSockAddr, 0, sizeof(otSockAddr));
256 message->mSockAddr.mPort = sServicePort;
257 message->mDataLength = sServiceTxtLength;
258 memcpy(message->mData, sServiceTxtData, sServiceTxtLength);
259
260 #if DEBUG_LOG
261 fprintf(stderr, "\r\n[trel-sim] sendServiceMessage(%s): service-port:%u, txt-len:%u\r\n",
262 aType == TREL_DNSSD_ADD_SERVICE_MESSAGE ? "add" : "remove", sServicePort, sServiceTxtLength);
263 #endif
264 }
265
processMessage(otInstance * aInstance,Message * aMessage,uint16_t aLength)266 static void processMessage(otInstance *aInstance, Message *aMessage, uint16_t aLength)
267 {
268 otPlatTrelPeerInfo peerInfo;
269
270 #if DEBUG_LOG
271 fprintf(stderr, "\r\n[trel-sim] processMessage(len:%u, type:%s, port:%u)\r\n", aLength,
272 messageTypeToString(aMessage->mType), aMessage->mSockAddr.mPort);
273 #endif
274
275 otEXPECT(aLength > 0);
276 otEXPECT(getMessageSize(aMessage) == aLength);
277
278 switch (aMessage->mType)
279 {
280 case TREL_DATA_MESSAGE:
281 otEXPECT(aMessage->mSockAddr.mPort == sUdpPort);
282 otPlatTrelHandleReceived(aInstance, aMessage->mData, aMessage->mDataLength);
283 break;
284
285 case TREL_DNSSD_BROWSE_MESSAGE:
286 sendServiceMessage(TREL_DNSSD_ADD_SERVICE_MESSAGE);
287 break;
288
289 case TREL_DNSSD_ADD_SERVICE_MESSAGE:
290 case TREL_DNSSD_REMOVE_SERVICE_MESSAGE:
291 memset(&peerInfo, 0, sizeof(peerInfo));
292 peerInfo.mRemoved = (aMessage->mType == TREL_DNSSD_REMOVE_SERVICE_MESSAGE);
293 peerInfo.mTxtData = aMessage->mData;
294 peerInfo.mTxtLength = (uint8_t)(aMessage->mDataLength);
295 peerInfo.mSockAddr = aMessage->mSockAddr;
296 otPlatTrelHandleDiscoveredPeerInfo(aInstance, &peerInfo);
297 break;
298 }
299
300 exit:
301 return;
302 }
303
304 //---------------------------------------------------------------------------------------------------------------------
305 // otPlatTrel
306
otPlatTrelEnable(otInstance * aInstance,uint16_t * aUdpPort)307 void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort)
308 {
309 OT_UNUSED_VARIABLE(aInstance);
310
311 *aUdpPort = sUdpPort;
312
313 #if DEBUG_LOG
314 fprintf(stderr, "\r\n[trel-sim] otPlatTrelEnable() *aUdpPort=%u\r\n", *aUdpPort);
315 #endif
316
317 if (!sEnabled)
318 {
319 sEnabled = true;
320 sendBrowseMessage();
321 }
322 }
323
otPlatTrelDisable(otInstance * aInstance)324 void otPlatTrelDisable(otInstance *aInstance)
325 {
326 OT_UNUSED_VARIABLE(aInstance);
327
328 #if DEBUG_LOG
329 fprintf(stderr, "\r\n[trel-sim] otPlatTrelDisable()\r\n");
330 #endif
331
332 if (sEnabled)
333 {
334 sEnabled = false;
335
336 if (sServiceRegistered)
337 {
338 sendServiceMessage(TREL_DNSSD_REMOVE_SERVICE_MESSAGE);
339 sServiceRegistered = false;
340 }
341 }
342 }
343
otPlatTrelRegisterService(otInstance * aInstance,uint16_t aPort,const uint8_t * aTxtData,uint8_t aTxtLength)344 void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
345 {
346 OT_UNUSED_VARIABLE(aInstance);
347
348 assert(aTxtLength <= TREL_MAX_SERVICE_TXT_DATA_LEN);
349
350 if (sServiceRegistered)
351 {
352 sendServiceMessage(TREL_DNSSD_REMOVE_SERVICE_MESSAGE);
353 }
354
355 sServiceRegistered = true;
356 sServicePort = aPort;
357 sServiceTxtLength = aTxtLength;
358 memcpy(sServiceTxtData, aTxtData, aTxtLength);
359
360 sendServiceMessage(TREL_DNSSD_ADD_SERVICE_MESSAGE);
361
362 #if DEBUG_LOG
363 fprintf(stderr, "\r\n[trel-sim] otPlatTrelRegisterService(aPort:%d, aTxtData:", aPort);
364 dumpBuffer(aTxtData, aTxtLength);
365 fprintf(stderr, ")\r\n");
366 #endif
367 }
368
otPlatTrelSend(otInstance * aInstance,const uint8_t * aUdpPayload,uint16_t aUdpPayloadLen,const otSockAddr * aDestSockAddr)369 void otPlatTrelSend(otInstance * aInstance,
370 const uint8_t * aUdpPayload,
371 uint16_t aUdpPayloadLen,
372 const otSockAddr *aDestSockAddr)
373 {
374 OT_UNUSED_VARIABLE(aInstance);
375
376 Message *message;
377
378 assert(sNumPendingTx < TREL_MAX_PENDING_TX);
379 assert(aUdpPayloadLen <= TREL_MAX_PACKET_SIZE);
380
381 message = &sPendingTx[sNumPendingTx++];
382
383 message->mType = TREL_DATA_MESSAGE;
384 message->mSockAddr = *aDestSockAddr;
385 message->mDataLength = aUdpPayloadLen;
386 memcpy(message->mData, aUdpPayload, aUdpPayloadLen);
387
388 #if DEBUG_LOG
389 fprintf(stderr, "\r\n[trel-sim] otPlatTrelSend(len:%u, port:%u)\r\n", aUdpPayloadLen, aDestSockAddr->mPort);
390 #endif
391 }
392
393 //---------------------------------------------------------------------------------------------------------------------
394 // platformTrel system
395
platformTrelInit(uint32_t aSpeedUpFactor)396 void platformTrelInit(uint32_t aSpeedUpFactor)
397 {
398 char *str;
399
400 str = getenv("PORT_OFFSET");
401
402 if (str != NULL)
403 {
404 char *endptr;
405
406 sPortOffset = (uint16_t)strtol(str, &endptr, 0);
407
408 if (*endptr != '\0')
409 {
410 fprintf(stderr, "\r\nInvalid PORT_OFFSET: %s\r\n", str);
411 exit(EXIT_FAILURE);
412 }
413
414 sPortOffset *= (MAX_NETWORK_SIZE + 1);
415 }
416
417 initFds();
418
419 OT_UNUSED_VARIABLE(aSpeedUpFactor);
420 }
421
platformTrelDeinit(void)422 void platformTrelDeinit(void)
423 {
424 deinitFds();
425 }
426
platformTrelUpdateFdSet(fd_set * aReadFdSet,fd_set * aWriteFdSet,struct timeval * aTimeout,int * aMaxFd)427 void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
428 {
429 OT_UNUSED_VARIABLE(aTimeout);
430
431 // Always ready to receive
432 if (aReadFdSet != NULL)
433 {
434 FD_SET(sRxFd, aReadFdSet);
435
436 if (aMaxFd != NULL && *aMaxFd < sRxFd)
437 {
438 *aMaxFd = sRxFd;
439 }
440 }
441
442 if ((aWriteFdSet != NULL) && (sNumPendingTx > 0))
443 {
444 FD_SET(sTxFd, aWriteFdSet);
445
446 if (aMaxFd != NULL && *aMaxFd < sTxFd)
447 {
448 *aMaxFd = sTxFd;
449 }
450 }
451 }
452
platformTrelProcess(otInstance * aInstance,const fd_set * aReadFdSet,const fd_set * aWriteFdSet)453 void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
454 {
455 if (FD_ISSET(sTxFd, aWriteFdSet) && (sNumPendingTx > 0))
456 {
457 sendPendingTxMessages();
458 }
459
460 if (FD_ISSET(sRxFd, aReadFdSet))
461 {
462 Message message;
463 ssize_t rval;
464
465 message.mDataLength = 0;
466
467 rval = recvfrom(sRxFd, (char *)&message, sizeof(message), 0, NULL, NULL);
468
469 if (rval < 0)
470 {
471 perror("recvfrom(sRxFd)");
472 exit(EXIT_FAILURE);
473 }
474
475 processMessage(aInstance, &message, (uint16_t)(rval));
476 }
477 }
478
479 //---------------------------------------------------------------------------------------------------------------------
480
481 // This is added for RCP build to be built ok
otPlatTrelHandleReceived(otInstance * aInstance,uint8_t * aBuffer,uint16_t aLength)482 OT_TOOL_WEAK void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength)
483 {
484 OT_UNUSED_VARIABLE(aInstance);
485 OT_UNUSED_VARIABLE(aBuffer);
486 OT_UNUSED_VARIABLE(aLength);
487
488 assert(false);
489 }
490
otPlatTrelHandleDiscoveredPeerInfo(otInstance * aInstance,const otPlatTrelPeerInfo * aInfo)491 OT_TOOL_WEAK void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo)
492 {
493 OT_UNUSED_VARIABLE(aInstance);
494 OT_UNUSED_VARIABLE(aInfo);
495
496 assert(false);
497 }
498
499 #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
500