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