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_ip.h"
31 #include "nx_ipv6.h"
32 #include "nx_icmpv6.h"
33
34 #ifdef NX_IPSEC_ENABLE
35 #include "nx_ipsec.h"
36 #endif /* NX_IPSEC_ENABLE */
37
38
39 #ifdef FEATURE_NX_IPV6
40
41
42 /**************************************************************************/
43 /* */
44 /* FUNCTION RELEASE */
45 /* */
46 /* _nx_icmpv6_process_ns PORTABLE C */
47 /* 6.1 */
48 /* AUTHOR */
49 /* */
50 /* Yuxin Zhou, Microsoft Corporation */
51 /* */
52 /* DESCRIPTION */
53 /* */
54 /* This internal function processes an incoming neighbor solicitation */
55 /* message. In response to a valid NS message, it also sends out */
56 /* a neighbor solicitation, and updates the Neighbor Cache. */
57 /* */
58 /* INPUT */
59 /* */
60 /* ip_ptr IP stack instance */
61 /* packet_ptr The echo reply packet */
62 /* */
63 /* OUTPUT */
64 /* */
65 /* None */
66 /* */
67 /* CALLS */
68 /* */
69 /* tx_mutex_get Obtain exclusive lock (on table) */
70 /* tx_mutex_put Release exclusive lock */
71 /* _nx_packet_release Release packet back to pool */
72 /* _nx_ipv6_packet_send Send IPv6 packet to remote host */
73 /* _nx_nd_cache_add Add entry to ND cache table */
74 /* _nx_nd_cache_find_entry Find ND cache entry by IP address*/
75 /* _nx_icmpv6_send_queued_packets Send packets queued waiting for */
76 /* physical mapping */
77 /* _nx_icmpv6_validate_neighbor_message */
78 /* Validate received ICMPv6 packet */
79 /* _nx_ip_checksum_compute Compute NS packet ICMP checksum */
80 /* */
81 /* CALLED BY */
82 /* */
83 /* _nx_icmpv6_process Main ICMPv6 processor */
84 /* */
85 /* RELEASE HISTORY */
86 /* */
87 /* DATE NAME DESCRIPTION */
88 /* */
89 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
90 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
91 /* resulting in version 6.1 */
92 /* */
93 /**************************************************************************/
_nx_icmpv6_process_ns(NX_IP * ip_ptr,NX_PACKET * packet_ptr)94 VOID _nx_icmpv6_process_ns(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
95 {
96
97 NX_ICMPV6_ND *nd_ptr;
98 NX_ICMPV6_OPTION *option_ptr;
99 USHORT *mac_addr;
100 UINT source_unspecified;
101 UINT error;
102 UINT option_length;
103 NX_ICMPV6_HEADER *header_ptr;
104 NX_IPV6_HEADER *ipv6_header;
105 UINT SLLA_changed = NX_FALSE;
106 UINT i;
107 NXD_IPV6_ADDRESS *interface_addr;
108 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
109 UINT compute_checksum = 1;
110 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
111 ULONG dest_address[4];
112
113
114 /* Initialize local variable: assume source address is specified. */
115 source_unspecified = NX_FALSE;
116
117 /* Assume there is no error. */
118 error = 0;
119
120 /* Get a pointer to the ICMP message header. */
121 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
122 header_ptr = (NX_ICMPV6_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
123
124 /* Get a pointer to the IPv6 header. */
125 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
126 ipv6_header = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_ip_header;
127
128 /* Get a pointer to the Neighbor Discovery message. */
129 /*lint -e{929} suppress cast of pointer to pointer, since it is necessary */
130 nd_ptr = (NX_ICMPV6_ND *)header_ptr;
131
132 /* Convert target address to host byte order. */
133 NX_IPV6_ADDRESS_CHANGE_ENDIAN(nd_ptr -> nx_icmpv6_nd_targetAddress);
134
135 /* Convert flag field to host byte order. */
136 NX_CHANGE_ULONG_ENDIAN(nd_ptr -> nx_icmpv6_nd_flag);
137
138
139 /* Validate the packet. */
140 if (_nx_icmpv6_validate_neighbor_message(packet_ptr) != NX_SUCCESS)
141 {
142 error = 1;
143 }
144
145 /* Find whether or not sender is unspecified. If sender is unspecified,
146 the sender is performing DAD process. */
147 if (CHECK_UNSPECIFIED_ADDRESS(ipv6_header -> nx_ip_header_source_ip))
148 {
149
150 /* Mark the packet source as nonsolicited. */
151 source_unspecified = NX_TRUE;
152 }
153
154 /* Find the appropriate interface to send the packet out on, based
155 on the destination address. */
156
157 /* Get a pointer to the first ipv6 address in the interface address list. */
158 interface_addr = packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr;
159
160 if (!error)
161 {
162
163 /* Loop to match IP addresses. */
164 while (interface_addr != NX_NULL)
165 {
166
167 /* Does the current address in the IP interface list match the one in the
168 ND message header? */
169 if ((CHECK_IPV6_ADDRESSES_SAME(interface_addr -> nxd_ipv6_address,
170 nd_ptr -> nx_icmpv6_nd_targetAddress)))
171 {
172
173 /* We're done matching. */
174 break;
175 }
176
177 /* Get next IPv6 address in the interface address list. */
178 interface_addr = interface_addr -> nxd_ipv6_address_next;
179 }
180 }
181
182 if (error || (interface_addr == NX_NULL))
183 {
184
185 #ifndef NX_DISABLE_ICMP_INFO
186
187 /* Increment the ICMP invalid packet error. */
188 ip_ptr -> nx_ip_icmp_invalid_packets++;
189 #endif /* NX_DISABLE_ICMP_INFO */
190
191 /* An error occurred. Release the packet. */
192 _nx_packet_release(packet_ptr);
193
194 return;
195 }
196
197 /* Have find a valid address. */
198 packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr = interface_addr;
199
200 /*
201 * Once we get here, we have two cases:
202 * (1) Sender is in the DAD process
203 * (2) Sender wants to find our MAC address.
204 *
205 * So first, we need to find out whether or not the source IP address is
206 * the unspecified address.
207 */
208
209 if (source_unspecified == NX_TRUE)
210 {
211
212 /* The sender is in DAD process. */
213
214 #ifndef NX_DISABLE_IPV6_DAD
215 /* The sender is doing a DAD on the same address as we have... */
216 if (interface_addr -> nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_TENTATIVE)
217 {
218
219 /* Our interface address is in tentative state. Therefore interface
220 address is also invalid. */
221 _nx_icmpv6_DAD_failure(ip_ptr, interface_addr);
222
223 _nx_packet_release(packet_ptr);
224 return;
225 }
226 #endif
227 /* Our address state is not in tentative mode. That means
228 we have a valid address. In this case, we should send a response */
229 }
230
231 /* Get a pointer to the ICMPv6 options. */
232 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary */
233 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(header_ptr, sizeof(NX_ICMPV6_ND));
234
235 /* We'll need to keep track of option data to parse the options. */
236 option_length = (UINT)packet_ptr -> nx_packet_length - (UINT)sizeof(NX_ICMPV6_ND);
237
238 /* Walk through the ICMPv6 options, if any. */
239 while (option_length > 0)
240 {
241
242 /* Handle the source link-layer address option for a solicited NS request. */
243 if (option_ptr -> nx_icmpv6_option_type == ICMPV6_OPTION_TYPE_SRC_LINK_ADDR)
244 {
245
246 ND_CACHE_ENTRY *nd_entry;
247 UINT status;
248
249
250 /* At this point, the source address has been verified to be
251 valid (not unspecified.) Therefore we should add
252 the source link layer address to our neighbor cache. */
253
254 /* Is the source IP address is not in our ND cache? */
255
256 status = _nx_nd_cache_find_entry(ip_ptr, ipv6_header -> nx_ip_header_source_ip, &nd_entry);
257
258 if (status != NX_SUCCESS)
259 {
260
261 /* Yes, this NS fills in the mac address so the LLA is changed. */
262 SLLA_changed = NX_TRUE;
263
264 /* No, so we create a cache entry. */
265 /*lint -e{929} suppress cast from pointer to pointer, since it is necessary */
266 /*lint -e{826} suppress cast of pointer to pointer, since it is necessary */
267 /*lint -e{740} suppress unusual cast of pointer, since it is necessary */
268 _nx_nd_cache_add(ip_ptr, ipv6_header -> nx_ip_header_source_ip,
269 interface_addr -> nxd_ipv6_address_attached,
270 (CHAR *)&option_ptr -> nx_icmpv6_option_data, 0, ND_CACHE_STATE_STALE,
271 interface_addr, &nd_entry);
272 }
273 else
274 {
275
276 /* Entry already exists. If the mac address is the same, do not update the entry. Otherwise,
277 update the entry and set the state to STALE (RFC2461 7.2.3) */
278 ULONG mac_msw, mac_lsw, new_msw, new_lsw;
279
280 /*lint -e{928} suppress cast from pointer to pointer, since it is necessary */
281 UCHAR *new_mac = (UCHAR *)&option_ptr -> nx_icmpv6_option_data;
282
283 /*lint -e{644} suppress variable might not be initialized, since "nd_entry" was initialized in _nx_nd_cache_find_entry. */
284 mac_msw = ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[0]) << 8) | (nd_entry -> nx_nd_cache_mac_addr[1]);
285 mac_lsw = ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[2]) << 24) | ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[3]) << 16) |
286 ((ULONG)(nd_entry -> nx_nd_cache_mac_addr[4]) << 8) | nd_entry -> nx_nd_cache_mac_addr[5];
287 new_msw = ((ULONG)(new_mac[0]) << 8) | (new_mac[1]);
288 new_lsw = ((ULONG)(new_mac[2]) << 24) | ((ULONG)(new_mac[3]) << 16) | ((ULONG)(new_mac[4]) << 8) | new_mac[5]; /* lgtm[cpp/overflow-buffer] */
289 if ((mac_msw != new_msw) || (mac_lsw != new_lsw)) /* If the new MAC is different from what we have in the table. */
290 {
291
292 /* This NS changes the cache entry mac address, so the LLA is changed. */
293 SLLA_changed = NX_TRUE;
294
295 /* Set the mac address. */
296 for (i = 0; i < 6; i++)
297 {
298 nd_entry -> nx_nd_cache_mac_addr[i] = new_mac[i];
299 }
300
301 /* Set the state to STALE. */
302 nd_entry -> nx_nd_cache_nd_status = ND_CACHE_STATE_STALE;
303
304 /* Set the interface. */
305 nd_entry -> nx_nd_cache_interface_ptr = interface_addr -> nxd_ipv6_address_attached;
306 }
307
308 if (nd_entry -> nx_nd_cache_packet_waiting_head) /* There are packets waiting to be transmitted */
309 {
310
311 /* Ok to transmit the packets now. */
312 _nx_icmpv6_send_queued_packets(ip_ptr, nd_entry);
313 }
314 }
315 }
316
317 option_length -= ((UINT)(option_ptr -> nx_icmpv6_option_length) << 3);
318
319 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary */
320 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(option_ptr, ((option_ptr -> nx_icmpv6_option_length) << 3));
321 }
322
323 /* We may need to reset a reachable timer if there is no new LLA involved.
324 Did we change the cache LLA? */
325 if (SLLA_changed == NX_FALSE)
326 {
327
328 /* No, so if this cache state is REACHABLE, check if we should
329 reset the timer tick after receiving a NS packet from the neighbor.
330 RFC 2461 7.2.3. */
331
332 ND_CACHE_ENTRY *nd_entry;
333
334 /* Verify the source of the NS packet in our ND cache, in case
335 we have not already done so. */
336 if (_nx_nd_cache_find_entry(ip_ptr, ipv6_header -> nx_ip_header_source_ip, &nd_entry) == NX_SUCCESS)
337 {
338
339 /* Yes, is it currently in a reachable state? */
340 /*lint -e{644} suppress variable might not be initialized, since "nd_entry" was initialized in _nx_nd_cache_find_entry. */
341 if (nd_entry -> nx_nd_cache_nd_status == ND_CACHE_STATE_REACHABLE)
342 {
343 /* Ok to update the timer. */
344 nd_entry -> nx_nd_cache_timer_tick = ip_ptr -> nx_ipv6_reachable_timer;
345 }
346 }
347 }
348
349 /*
350 * A valid NS has been received. Prepare a Neighbor Advertisement packet
351 * according to RFC2461 section 7.2.4.
352 *
353 * Adjust the packet, eliminate the option part. At this point, the packet prepend_ptr points to the
354 * beginning of the ICMP message. The size of the ICMP message includes ICMP NA and the target
355 * link layer address option field (8 bytes).
356 */
357 packet_ptr -> nx_packet_append_ptr =
358 packet_ptr -> nx_packet_prepend_ptr + sizeof(NX_ICMPV6_ND) + 8;
359
360 packet_ptr -> nx_packet_length = sizeof(NX_ICMPV6_ND) + 8;
361
362 if (source_unspecified == NX_TRUE)
363 {
364
365 /* Response to an unsolicited NS: clear the S bit.*/
366 nd_ptr -> nx_icmpv6_nd_flag = (0x20000000);
367 }
368 else
369 {
370 /* Response to a normal NS: set the S bit.*/
371 nd_ptr -> nx_icmpv6_nd_flag = (0x60000000);
372 }
373
374 NX_CHANGE_ULONG_ENDIAN(nd_ptr -> nx_icmpv6_nd_flag);
375
376 /* nd_ptr -> targetAddress has been converted to host byte order.
377 We need to convert it back to network byte order. */
378 NX_IPV6_ADDRESS_CHANGE_ENDIAN(nd_ptr -> nx_icmpv6_nd_targetAddress);
379
380 header_ptr -> nx_icmpv6_header_type = NX_ICMPV6_NEIGHBOR_ADVERTISEMENT_TYPE;
381 header_ptr -> nx_icmpv6_header_code = 0;
382 header_ptr -> nx_icmpv6_header_checksum = 0;
383
384 if (source_unspecified == NX_TRUE)
385 {
386
387 /* Sender uses unspecified address. So we send NA to all node multicast address. */
388 /* RFC2461 7.2.4 */
389 dest_address[0] = 0xFF020000;
390 dest_address[1] = 0;
391 dest_address[2] = 0;
392 dest_address[3] = 0x00000001;
393 }
394 else
395 {
396
397 /* Set the packet destination IP address */
398 COPY_IPV6_ADDRESS(ipv6_header -> nx_ip_header_source_ip,
399 dest_address);
400 }
401
402 /*
403 Fill in the options. Since we are using the same packet,
404 the option_ptr is already pointing to the option field.
405 */
406 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary */
407 option_ptr = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(header_ptr, sizeof(NX_ICMPV6_ND));
408 option_ptr -> nx_icmpv6_option_type = ICMPV6_OPTION_TYPE_TRG_LINK_ADDR;
409 option_ptr -> nx_icmpv6_option_length = 1;
410 mac_addr = &option_ptr -> nx_icmpv6_option_data;
411
412 mac_addr[0] = (USHORT)(interface_addr -> nxd_ipv6_address_attached -> nx_interface_physical_address_msw);
413 mac_addr[1] = (USHORT)((interface_addr -> nxd_ipv6_address_attached -> nx_interface_physical_address_lsw & 0xFFFF0000) >> 16); /* lgtm[cpp/overflow-buffer] */
414 mac_addr[2] = (USHORT)(interface_addr -> nxd_ipv6_address_attached -> nx_interface_physical_address_lsw & 0x0000FFFF); /* lgtm[cpp/overflow-buffer] */
415
416 NX_CHANGE_USHORT_ENDIAN(mac_addr[0]);
417 NX_CHANGE_USHORT_ENDIAN(mac_addr[1]); /* lgtm[cpp/overflow-buffer] */
418 NX_CHANGE_USHORT_ENDIAN(mac_addr[2]); /* lgtm[cpp/overflow-buffer] */
419
420 #ifdef NX_DISABLE_ICMPV6_TX_CHECKSUM
421 compute_checksum = 0;
422 #endif /* NX_DISABLE_ICMPV6_TX_CHECKSUM */
423
424 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
425 if (interface_addr -> nxd_ipv6_address_attached -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM)
426 {
427 compute_checksum = 0;
428 }
429 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
430 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
431 if (compute_checksum)
432 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
433 {
434
435 /* Compute the checksum. */
436 header_ptr -> nx_icmpv6_header_checksum =
437 _nx_ip_checksum_compute(packet_ptr, NX_PROTOCOL_ICMPV6,
438 (UINT)packet_ptr -> nx_packet_length,
439 interface_addr -> nxd_ipv6_address,
440 dest_address);
441
442 /* Write the checksum to the ICMPv6 header. */
443 header_ptr -> nx_icmpv6_header_checksum = (USHORT)(~(header_ptr -> nx_icmpv6_header_checksum));
444
445 NX_CHANGE_USHORT_ENDIAN(header_ptr -> nx_icmpv6_header_checksum);
446 }
447 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
448 else
449 {
450 packet_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM;
451 }
452 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
453 /* Send out the NA reply. */
454 _nx_ipv6_packet_send(ip_ptr, packet_ptr,
455 NX_PROTOCOL_ICMPV6,
456 packet_ptr -> nx_packet_length,
457 255 /* NA message must have hop limit 255 */,
458 interface_addr -> nxd_ipv6_address,
459 dest_address);
460
461 /* (Let the driver release the packet.) */
462 return;
463 }
464
465 #endif /* FEATURE_NX_IPV6 */
466
467