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