1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX Component */
16 /** */
17 /** Internet Control Message Protocol (ICMP) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "nx_api.h"
28 #include "nx_packet.h"
29 #include "nx_ipv6.h"
30 #include "nx_icmpv6.h"
31
32 #ifdef NX_IPSEC_ENABLE
33 #include "nx_ipsec.h"
34 #endif /* NX_IPSEC_ENABLE */
35
36
37 #ifdef FEATURE_NX_IPV6
38
39
40 /**************************************************************************/
41 /* */
42 /* FUNCTION RELEASE */
43 /* */
44 /* _nx_icmpv6_process_na PORTABLE C */
45 /* 6.3.0 */
46 /* AUTHOR */
47 /* */
48 /* Yuxin Zhou, Microsoft Corporation */
49 /* */
50 /* DESCRIPTION */
51 /* */
52 /* This internal function processes incoming neighbor advertisement */
53 /* messages. It also updates the ND cache and the default router list. */
54 /* */
55 /* INPUT */
56 /* */
57 /* ip_ptr IP stack instance */
58 /* packet_ptr Received NA packet */
59 /* */
60 /* OUTPUT */
61 /* */
62 /* None */
63 /* */
64 /* CALLS */
65 /* */
66 /* _nx_packet_release Release packet back to pool */
67 /* _nx_nd_cache_add Add entry to cache table */
68 /* _nx_icmpv6_send_queued_packets Send out packets queued waiting */
69 /* for physical mapping to IP */
70 /* address */
71 /* _nx_icmpv6_validate_neighbor_message Validate ICMPv6 packet */
72 /* _nx_icmpv6_DAD_failure Mark IP address status invalid */
73 /* _nx_nd_cache_find_entry Find entry based on dest address*/
74 /* _nxd_ipv6_find_default_router_from_address */
75 /* Find default router by address */
76 /* _nxd_ipv6_default_router_delete Clear the default router bound */
77 /* to the IP */
78 /* */
79 /* CALLED BY */
80 /* */
81 /* _nx_icmpv6_process */
82 /* */
83 /* RELEASE HISTORY */
84 /* */
85 /* DATE NAME DESCRIPTION */
86 /* */
87 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
88 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
89 /* resulting in version 6.1 */
90 /* 10-31-2023 Bo Chen Modified comment(s), improved */
91 /* packet length verification, */
92 /* resulting in version 6.3.0 */
93 /* */
94 /**************************************************************************/
_nx_icmpv6_process_na(NX_IP * ip_ptr,NX_PACKET * packet_ptr)95 VOID _nx_icmpv6_process_na(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
96 {
97
98 ND_CACHE_ENTRY *nd_entry = NX_NULL;
99 NX_ICMPV6_ND *nd_ptr;
100 NX_ICMPV6_OPTION *option_ptr = NX_NULL;
101 INT error = 0;
102 INT lla_same = 0;
103 UINT option_length;
104 NX_IPV6_HEADER *ipv6_header_ptr;
105 #ifndef NX_DISABLE_IPV6_DAD
106 UINT i;
107 #endif /* NX_DISABLE_IPV6_DAD */
108
109 /* Add debug information. */
110 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
111
112 #ifndef NX_DISABLE_RX_SIZE_CHECKING
113 /* Check packet length is at least sizeof(NX_ICMPV6_ND). */
114 if ((packet_ptr -> nx_packet_length < sizeof(NX_ICMPV6_ND))
115 #ifndef NX_DISABLE_PACKET_CHAIN
116 || (packet_ptr -> nx_packet_next) /* Ignore chained packet. */
117 #endif /* NX_DISABLE_PACKET_CHAIN */
118 )
119 {
120 #ifndef NX_DISABLE_ICMP_INFO
121
122 /* Increment the ICMP invalid packet error. */
123 ip_ptr -> nx_ip_icmp_invalid_packets++;
124 #endif /* NX_DISABLE_ICMP_INFO */
125
126 /* Release the packet and we are done. */
127 _nx_packet_release(packet_ptr);
128 return;
129 }
130 #endif /* NX_DISABLE_RX_SIZE_CHECKING */
131
132 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
133 nd_ptr = (NX_ICMPV6_ND *)(packet_ptr -> nx_packet_prepend_ptr);
134
135 /* Take care of endian-ness. */
136 NX_IPV6_ADDRESS_CHANGE_ENDIAN(nd_ptr -> nx_icmpv6_nd_targetAddress);
137 NX_CHANGE_ULONG_ENDIAN(nd_ptr -> nx_icmpv6_nd_flag);
138
139 /* Validate the neighbor advertisement message. */
140 if (_nx_icmpv6_validate_neighbor_message(packet_ptr) != NX_SUCCESS)
141 {
142 error = 1;
143 }
144 else
145 {
146
147 /* Points to the IPv6 header. */
148 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
149 ipv6_header_ptr = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header;
150
151 /* Find the option field. */
152 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary */
153 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(nd_ptr, sizeof(NX_ICMPV6_ND));
154 option_length = (UINT)packet_ptr -> nx_packet_length - (UINT)sizeof(NX_ICMPV6_ND);
155
156 /* Find the TLLA option */
157 while (option_length > 0)
158 {
159 /* Check if this is a Target LLA option. */
160 if (option_ptr -> nx_icmpv6_option_type == ICMPV6_OPTION_TYPE_TRG_LINK_ADDR)
161 {
162 break;
163 }
164
165 /* Get the next option. */
166 option_length -= ((UINT)(option_ptr -> nx_icmpv6_option_length) << 3);
167
168 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary */
169 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(option_ptr, ((option_ptr -> nx_icmpv6_option_length) << 3));
170 }
171
172 /* Check for no option included. */
173 if (option_length == 0)
174 {
175
176 option_ptr = NX_NULL;
177 }
178
179 /* Determine the NA packet destination type. */
180 /* Is the destination a multicast address? */
181 if ((ipv6_header_ptr -> nx_ip_header_destination_ip[0] & (ULONG)0xFF000000) == (ULONG)0xFF000000)
182 {
183
184 /* Yes; Were there any options in the NA packet? */
185 if (!option_ptr)
186 {
187
188 /* No, this is an invalid NA packet (No TLLA). */
189 error = 1;
190 }
191 }
192
193 #ifndef NX_DISABLE_IPV6_DAD
194
195 /* Find the same address as target address in IPv6 address structure.
196 Assume target address is 4-byte aligned.*/
197 for (i = 0; i < NX_MAX_IPV6_ADDRESSES; i++)
198 {
199
200 if (CHECK_IPV6_ADDRESSES_SAME(ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address,
201 nd_ptr -> nx_icmpv6_nd_targetAddress))
202 {
203 if (ip_ptr -> nx_ipv6_address[i].nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_TENTATIVE)
204 {
205
206 /* Sender sends a NS in response to a DAD request we sent out.
207 Mark DAD failure. */
208 _nx_icmpv6_DAD_failure(ip_ptr, &ip_ptr -> nx_ipv6_address[i]);
209
210 #ifndef NX_DISABLE_ICMP_INFO
211
212 /* Increment the ICMP invalid packet error. */
213 ip_ptr -> nx_ip_icmp_invalid_packets++;
214 #endif /* NX_DISABLE_ICMP_INFO */
215
216 /* Release the packet and we are done. */
217 _nx_packet_release(packet_ptr);
218 return;
219 }
220
221 break;
222 }
223 }
224 #endif /* NX_DISABLE_IPV6_DAD */
225
226 /* Find the ND entry */
227 if (_nx_nd_cache_find_entry(ip_ptr, nd_ptr -> nx_icmpv6_nd_targetAddress, &nd_entry) != NX_SUCCESS)
228 {
229 /* The entry does not exist, it indicates that we are not
230 expecting this NA. So silently ignore this packet,
231 according to RFC 2461 7.2.5 */
232 error = 1;
233 }
234 }
235
236 /* Do not process the NA any further if any errors detected. */
237 if (error)
238 {
239
240 #ifndef NX_DISABLE_ICMP_INFO
241
242 /* Increment the ICMP invalid packet error. */
243 ip_ptr -> nx_ip_icmp_invalid_packets++;
244 #endif /* NX_DISABLE_ICMP_INFO */
245
246 /* Release the packet and we are done. */
247 _nx_packet_release(packet_ptr);
248 return;
249 }
250
251 /* Check whether or not supplied LLA is the same as the cached one */
252 if (option_ptr)
253 {
254
255 /* Compare the link-layer address. */
256 USHORT *new_lla, *lla;
257
258 /*lint -e{929} suppress cast of pointer to pointer, since it is necessary */
259 new_lla = (USHORT *)&option_ptr -> nx_icmpv6_option_data;
260
261 /*lint -e{927} suppress cast of pointer to pointer, since it is necessary */
262 /*lint -e{644} suppress variable might not be initialized, since "nd_entry" was initialized in _nx_nd_cache_find_entry. */
263 lla = (USHORT *)nd_entry -> nx_nd_cache_mac_addr;
264 if ((new_lla[0] == lla[0]) && (new_lla[1] == lla[1]) && (new_lla[2] == lla[2])) /* lgtm[cpp/overflow-buffer] */
265 {
266
267 /* No change in LLA. */
268 lla_same = 1;
269 }
270 }
271
272 /* Determine if the entry is a router. */
273 if (nd_ptr -> nx_icmpv6_nd_flag & 0x80000000)
274 {
275
276 /* Yes; Does the corresonding ND entry have the IsRouter flag set? */
277 if (nd_entry -> nx_nd_cache_is_router == NX_NULL)
278 {
279
280 NX_IPV6_DEFAULT_ROUTER_ENTRY *rt_entry;
281
282 /* No; Find the default router entry. */
283 rt_entry =
284 _nxd_ipv6_find_default_router_from_address(ip_ptr, nd_ptr -> nx_icmpv6_nd_targetAddress);
285
286 /* Check the default router. */
287 if (rt_entry)
288 {
289
290 /* Set the IsRouter flag in the ND entry. */
291 nd_entry -> nx_nd_cache_is_router = rt_entry;
292
293 /* Set this link as the corresponding ND entry. */
294 rt_entry -> nx_ipv6_default_router_entry_neighbor_cache_ptr = (VOID *)nd_entry;
295 }
296 }
297 }
298 else
299 {
300
301 /*
302 * The neighbor advertisement message indicates that it is not a router.
303 * However if our ND cache still marks it as a router, that means the neighbor is
304 * no longer acting as a router, and we shall clean up our records.
305 */
306 if (nd_entry -> nx_nd_cache_is_router)
307 {
308
309 NXD_ADDRESS router_address;
310 UINT clear_router_flag = 1;
311
312 /* Only if the TLLA option indicates the TLLA is unchanged! */
313 if (option_ptr && !lla_same)
314 {
315
316 /* And only if the override bit not is set. */
317 if ((nd_ptr -> nx_icmpv6_nd_flag & 0x20000000) == 0)
318 {
319 clear_router_flag = 0;
320 }
321 }
322
323 /* Did we decide to clear the router status of this cache entry? */
324 if (clear_router_flag)
325 {
326
327 /* Yes, Ok to clear the cache entry router status! */
328
329 /* The IsRouter points to the entry in the default router table.
330 We first remove the link between the router table entry and the nd cache entry. */
331 nd_entry -> nx_nd_cache_is_router -> nx_ipv6_default_router_entry_neighbor_cache_ptr = NX_NULL;
332
333
334 /* Remove the entry from the default router list. */
335 router_address.nxd_ip_version = NX_IP_VERSION_V6;
336 router_address.nxd_ip_address.v6[0] = nd_ptr -> nx_icmpv6_nd_targetAddress[0];
337 router_address.nxd_ip_address.v6[1] = nd_ptr -> nx_icmpv6_nd_targetAddress[1];
338 router_address.nxd_ip_address.v6[2] = nd_ptr -> nx_icmpv6_nd_targetAddress[2];
339 router_address.nxd_ip_address.v6[3] = nd_ptr -> nx_icmpv6_nd_targetAddress[3];
340
341 /* We must remove the entry from the default router list. */
342 _nxd_ipv6_default_router_delete(ip_ptr, &router_address);
343
344 /* Set the router flag to NULL. */
345 nd_entry -> nx_nd_cache_is_router = NX_NULL;
346 }
347 }
348 }
349
350 /*
351 * An ND entry exists. If the cache entry is in incomplete state,
352 * add the NA contains LLA option, if the NA is unsolicitated,
353 * add the LLA to the cache in STALE state.
354 */
355 if ((nd_entry -> nx_nd_cache_nd_status == ND_CACHE_STATE_INCOMPLETE) ||
356 (nd_entry -> nx_nd_cache_nd_status == ND_CACHE_STATE_CREATED))
357 {
358
359 /* Find the target link layer options. */
360 if (option_ptr)
361 {
362
363 /* If the solicitation flag is set, set state to REACHABLE; if not,
364 set state to STALE.*/
365 /*lint -e{929} -e{740} -e{826} suppress cast of pointer to pointer, since it is necessary */
366 _nx_nd_cache_add(ip_ptr, nd_ptr -> nx_icmpv6_nd_targetAddress,
367 packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached,
368 (CHAR *)&option_ptr -> nx_icmpv6_option_data, 0,
369 nd_ptr -> nx_icmpv6_nd_flag & 0x40000000 ? ND_CACHE_STATE_REACHABLE : ND_CACHE_STATE_STALE,
370 packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr, &nd_entry);
371
372 /* Any queued packets?. */
373 if (nd_entry -> nx_nd_cache_packet_waiting_head)
374 {
375
376 /* Send the queued packets out. */
377 _nx_icmpv6_send_queued_packets(ip_ptr, nd_entry);
378 }
379 }
380
381 /* All done. Release the packet and return. */
382 _nx_packet_release(packet_ptr);
383 return;
384 }
385
386 /* When we get there, we have an ND entry that has valid (REACHABLE) LLA. */
387
388 if ((nd_ptr -> nx_icmpv6_nd_flag & 0x20000000) == 0 && option_ptr && (!lla_same))
389 {
390 /*
391 * Override bit is clear.
392 * If the link layer address is different from the one in our cache entry,
393 * and the entry is REACHABLE, update the entry to STALE.
394 */
395 if (nd_entry -> nx_nd_cache_nd_status == ND_CACHE_STATE_REACHABLE)
396 {
397
398 nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_STALE;
399 nd_entry -> nx_nd_cache_timer_tick = 0;
400 }
401 }
402 else
403 {
404
405 /* Processing according to RFC2461 7.2.5. */
406 /* If LLA is supplied and is different from cache value, update the cache. */
407 if (option_ptr && !lla_same)
408 {
409 USHORT *new_lla, *lla;
410
411 /*lint -e{929} -e{927} suppress cast of pointer to pointer, since it is necessary */
412 new_lla = (USHORT *)&option_ptr -> nx_icmpv6_option_data;
413
414 /*lint -e{927} suppress cast of pointer to pointer, since it is necessary */
415 lla = (USHORT *)nd_entry -> nx_nd_cache_mac_addr;
416 lla[0] = new_lla[0];
417 lla[1] = new_lla[1]; /* lgtm[cpp/overflow-buffer] */
418 lla[2] = new_lla[2]; /* lgtm[cpp/overflow-buffer] */
419 }
420 if (nd_ptr -> nx_icmpv6_nd_flag & 0x40000000) /* S bit is set, force cache entry to REACHABLE */
421 {
422
423 nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_REACHABLE;
424 nd_entry -> nx_nd_cache_timer_tick = ip_ptr -> nx_ipv6_reachable_timer;
425 }
426 else if (option_ptr && (!lla_same)) /* S bit is clear and either TLLA is not supplied or it is different. */
427 {
428 nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_STALE;
429 nd_entry -> nx_nd_cache_timer_tick = 0;
430 }
431 }
432
433 _nx_packet_release(packet_ptr);
434 return;
435 }
436
437 #endif /* FEATURE_NX_IPV6 */
438
439