xref: /aosp_15_r20/external/openthread/examples/platforms/simulation/infra_if.c (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
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