1 /*
2 * Copyright (c) 2024, 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/icmp6.h>
32 #include <openthread/ip6.h>
33 #include <openthread/logging.h>
34 #include <openthread/platform/infra_if.h>
35
36 #include "simul_utils.h"
37 #include "lib/platform/exit_code.h"
38 #include "utils/code_utils.h"
39
40 #if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
41
42 #define DEBUG_LOG 0
43
44 #if DEBUG_LOG
45 #define LOG(...) otLogNotePlat("[infra-if] "__VA_ARGS__)
46 #else
47 #define LOG(...) \
48 do \
49 { \
50 } while (0)
51 #endif
52
53 #define INFRA_IF_SIM_PORT 9800
54 #define INFRA_IF_MAX_PACKET_SIZE 1800
55 #define INFRA_IF_MAX_PENDING_TX 64
56 #define INFRA_IF_NEIGHBOR_ADVERT_SIZE 24
57
58 typedef struct Message
59 {
60 uint32_t mIfIndex;
61 otIp6Address mSrc;
62 otIp6Address mDst;
63 uint16_t mDataLength;
64 uint8_t mData[INFRA_IF_MAX_PACKET_SIZE];
65 } Message;
66
67 static bool sInitialized = false;
68 static otIp6Address sIp6Address;
69 static otIp6Address sLinkLocalAllNodes;
70 static otIp6Address sLinkLocalAllRouters;
71 static utilsSocket sSocket;
72 static uint16_t sPortOffset = 0;
73 static uint8_t sNumPendingTx = 0;
74 static Message sPendingTx[INFRA_IF_MAX_PENDING_TX];
75
76 //---------------------------------------------------------------------------------------------------------------------
77
addressesMatch(const otIp6Address * aFirstAddr,const otIp6Address * aSecondAddr)78 static bool addressesMatch(const otIp6Address *aFirstAddr, const otIp6Address *aSecondAddr)
79 {
80 return memcmp(aFirstAddr, aSecondAddr, sizeof(otIp6Address)) == 0;
81 }
82
getMessageSize(const Message * aMessage)83 static uint16_t getMessageSize(const Message *aMessage)
84 {
85 return (uint16_t)(&aMessage->mData[aMessage->mDataLength] - (const uint8_t *)aMessage);
86 }
87
sendPendingTxMessages(void)88 static void sendPendingTxMessages(void)
89 {
90 for (uint8_t i = 0; i < sNumPendingTx; i++)
91 {
92 utilsSendOverSocket(&sSocket, &sPendingTx[i], getMessageSize(&sPendingTx[i]));
93 }
94
95 sNumPendingTx = 0;
96 }
97
sendNeighborAdvert(const Message * aNsMessage)98 static void sendNeighborAdvert(const Message *aNsMessage)
99 {
100 Message *message;
101 uint8_t index;
102
103 assert(sNumPendingTx < INFRA_IF_MAX_PENDING_TX);
104
105 message = &sPendingTx[sNumPendingTx++];
106
107 message->mIfIndex = aNsMessage->mIfIndex;
108 message->mSrc = sIp6Address;
109 message->mDst = aNsMessage->mSrc;
110
111 // Neighbor Advertisement Message (RFC 4861)
112 //
113 // 0 1 2 3
114 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
115 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
116 // | Type | Code | Checksum |
117 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
118 // |R|S|O| Reserved |
119 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
120 // | |
121 // + +
122 // | |
123 // + Target Address +
124 // | |
125 // + +
126 // | |
127 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
128
129 index = 0;
130 memset(message->mData, 0, INFRA_IF_NEIGHBOR_ADVERT_SIZE);
131
132 message->mData[index++] = OT_ICMP6_TYPE_NEIGHBOR_ADVERT; // Type.
133 index += 3; // Code is zero. Checksum (uint16) as zero.
134 message->mData[index++] = 0xd0; // Flags, set R and S bits.
135 index += 3; // Skip over the reserved bytes.
136 memcpy(&message->mData[index], &sIp6Address, sizeof(sIp6Address)); // Set the target address field.
137 index += sizeof(sIp6Address);
138
139 assert(index == INFRA_IF_NEIGHBOR_ADVERT_SIZE);
140
141 message->mDataLength = INFRA_IF_NEIGHBOR_ADVERT_SIZE;
142 }
143
processMessage(otInstance * aInstance,Message * aMessage,uint16_t aLength)144 static void processMessage(otInstance *aInstance, Message *aMessage, uint16_t aLength)
145 {
146 OT_UNUSED_VARIABLE(aInstance);
147
148 otEXPECT(aLength > 0);
149 otEXPECT(getMessageSize(aMessage) == aLength);
150 otEXPECT(aMessage->mDataLength > 0);
151
152 // Validate the dest address.
153 otEXPECT(addressesMatch(&aMessage->mDst, &sIp6Address) || addressesMatch(&aMessage->mDst, &sLinkLocalAllNodes) ||
154 addressesMatch(&aMessage->mDst, &sLinkLocalAllRouters));
155
156 if (aMessage->mData[0] == OT_ICMP6_TYPE_NEIGHBOR_SOLICIT)
157 {
158 LOG("Received NS, responding with NA");
159 sendNeighborAdvert(aMessage);
160 }
161 else
162 {
163 LOG("Received msg, len:%u", aMessage->mDataLength);
164 otPlatInfraIfRecvIcmp6Nd(aInstance, aMessage->mIfIndex, &aMessage->mSrc, aMessage->mData,
165 aMessage->mDataLength);
166 }
167
168 exit:
169 return;
170 }
171
172 //---------------------------------------------------------------------------------------------------------------------
173 // otPlatInfraIf
174
otPlatInfraIfHasAddress(uint32_t aInfraIfIndex,const otIp6Address * aAddress)175 bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress)
176 {
177 OT_UNUSED_VARIABLE(aInfraIfIndex);
178
179 return addressesMatch(aAddress, &sIp6Address);
180 }
181
otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,const otIp6Address * aDestAddress,const uint8_t * aBuffer,uint16_t aBufferLength)182 otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
183 const otIp6Address *aDestAddress,
184 const uint8_t *aBuffer,
185 uint16_t aBufferLength)
186 {
187 otError error = OT_ERROR_FAILED;
188 Message *message;
189
190 otEXPECT(sInitialized);
191 otEXPECT(sNumPendingTx < INFRA_IF_MAX_PENDING_TX);
192
193 message = &sPendingTx[sNumPendingTx++];
194
195 message->mIfIndex = aInfraIfIndex;
196 message->mSrc = sIp6Address;
197 message->mDst = *aDestAddress;
198
199 assert(aBufferLength <= INFRA_IF_MAX_PACKET_SIZE);
200 message->mDataLength = aBufferLength;
201 memcpy(message->mData, aBuffer, aBufferLength);
202 error = OT_ERROR_NONE;
203
204 LOG("otPlatInfraIfSendIcmp6Nd() msg-len:%u", aBufferLength);
205
206 exit:
207 return error;
208 }
209
otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)210 otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)
211 {
212 OT_UNUSED_VARIABLE(aInfraIfIndex);
213
214 return OT_ERROR_NONE;
215 }
216
217 //---------------------------------------------------------------------------------------------------------------------
218 // platformInfraIf
219
platformInfraIfInit(void)220 void platformInfraIfInit(void)
221 {
222 char *str;
223
224 otEXPECT(!sInitialized);
225
226 sInitialized = true;
227
228 memset(&sIp6Address, 0, sizeof(sIp6Address));
229 sIp6Address.mFields.m8[0] = 0xfe;
230 sIp6Address.mFields.m8[1] = 0x80;
231 sIp6Address.mFields.m8[15] = (uint8_t)(gNodeId & 0xff);
232
233 // "ff02::01"
234 memset(&sLinkLocalAllNodes, 0, sizeof(sLinkLocalAllNodes));
235 sLinkLocalAllNodes.mFields.m8[0] = 0xff;
236 sLinkLocalAllNodes.mFields.m8[1] = 0x02;
237 sLinkLocalAllNodes.mFields.m8[15] = 0x01;
238
239 // "ff02::02"
240 memset(&sLinkLocalAllRouters, 0, sizeof(sLinkLocalAllRouters));
241 sLinkLocalAllRouters.mFields.m8[0] = 0xff;
242 sLinkLocalAllRouters.mFields.m8[1] = 0x02;
243 sLinkLocalAllRouters.mFields.m8[15] = 0x02;
244
245 str = getenv("PORT_OFFSET");
246
247 if (str != NULL)
248 {
249 char *endptr;
250
251 sPortOffset = (uint16_t)strtol(str, &endptr, 0);
252
253 if (*endptr != '\0')
254 {
255 fprintf(stderr, "\r\nInvalid PORT_OFFSET: %s\r\n", str);
256 DieNow(OT_EXIT_FAILURE);
257 }
258
259 sPortOffset *= (MAX_NETWORK_SIZE + 1);
260 }
261
262 utilsInitSocket(&sSocket, INFRA_IF_SIM_PORT + sPortOffset);
263
264 exit:
265 return;
266 }
267
platformInfraIfDeinit(void)268 void platformInfraIfDeinit(void)
269 {
270 otEXPECT(sInitialized);
271 sInitialized = false;
272 utilsDeinitSocket(&sSocket);
273
274 exit:
275 return;
276 }
277
platformInfraIfUpdateFdSet(fd_set * aReadFdSet,fd_set * aWriteFdSet,int * aMaxFd)278 void platformInfraIfUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd)
279 {
280 otEXPECT(sInitialized);
281
282 utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
283
284 if (sNumPendingTx > 0)
285 {
286 utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd);
287 }
288
289 exit:
290 return;
291 }
292
platformInfraIfProcess(otInstance * aInstance,const fd_set * aReadFdSet,const fd_set * aWriteFdSet)293 void platformInfraIfProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
294 {
295 OT_UNUSED_VARIABLE(aInstance);
296
297 otEXPECT(sInitialized);
298
299 if ((sNumPendingTx > 0) && utilsCanSocketSend(&sSocket, aWriteFdSet))
300 {
301 sendPendingTxMessages();
302 }
303
304 if (utilsCanSocketReceive(&sSocket, aReadFdSet))
305 {
306 Message message;
307 uint16_t len;
308
309 message.mDataLength = 0;
310
311 len = utilsReceiveFromSocket(&sSocket, &message, sizeof(message), NULL);
312 processMessage(aInstance, &message, len);
313 }
314
315 exit:
316 return;
317 }
318
319 //---------------------------------------------------------------------------------------------------------------------
320 // Provide weak implementation (used for RCP builds).
321 // `OPENTHREAD_RADIO` is not available in simulation platform
322
otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance,uint32_t aInfraIfIndex,const otIp6Address * aSrcAddress,const uint8_t * aBuffer,uint16_t aBufferLength)323 OT_TOOL_WEAK void otPlatInfraIfRecvIcmp6Nd(otInstance *aInstance,
324 uint32_t aInfraIfIndex,
325 const otIp6Address *aSrcAddress,
326 const uint8_t *aBuffer,
327 uint16_t aBufferLength)
328 {
329 OT_UNUSED_VARIABLE(aInstance);
330 OT_UNUSED_VARIABLE(aInfraIfIndex);
331 OT_UNUSED_VARIABLE(aSrcAddress);
332 OT_UNUSED_VARIABLE(aBuffer);
333 OT_UNUSED_VARIABLE(aBufferLength);
334
335 fprintf(stderr, "\n\r Weak otPlatInfraIfRecvIcmp6Nd is being used\n\r");
336 DieNow(OT_EXIT_FAILURE);
337 }
338
339 #endif // OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
340