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