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_ip.h"
30 #include "nx_ipv6.h"
31 #include "nx_icmpv6.h"
32 
33 
34 #ifdef FEATURE_NX_IPV6
35 static const ULONG _nx_ipv6_unspecified_address[4] = {0, 0, 0, 0};
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _nx_icmpv6_send_ns                                  PORTABLE C      */
42 /*                                                           6.1          */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    Yuxin Zhou, Microsoft Corporation                                   */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*   This function sends out an ICMPv6 Neighbor Solicitation (NS) message.*/
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    ip_ptr                                Pointer to IP control block   */
54 /*    targetIPAddr                          Target IPv6 Address           */
55 /*    send_slla                             Send Source Link Layer Address*/
56 /*    outgoing_address                      IP interface to transmit the  */
57 /*                                                 packet out on          */
58 /*    sendUnicast                           Send out a unicast NS         */
59 /*    NDCacheEntry                          Pointer to ND cache entry     */
60 /*                                                                        */
61 /*  OUTPUT                                                                */
62 /*                                                                        */
63 /*    None                                                                */
64 /*                                                                        */
65 /*  CALLS                                                                 */
66 /*                                                                        */
67 /*    _nx_ip_checksum_compute               Computer ICMP checksum        */
68 /*    _nx_ipv6_packet_send                  Send packet out               */
69 /*    _nx_packet_allocate                   Packet allocate function      */
70 /*    _nx_packet_release                    Packet release function       */
71 /*    _nx_ipv6_header_add                   Add IPv6 header               */
72 /*    (ip_link_driver)                      User supplied link driver     */
73 /*                                                                        */
74 /*  CALLED BY                                                             */
75 /*                                                                        */
76 /*    _nx_icmpv6_packet_process             Main ICMP packet pocess       */
77 /*    _nx_icmpv6_perform_DAD                Procedure for Duplicate       */
78 /*                                             Address Detection.         */
79 /*    _nx_ipv6_packet_send                  IPv6 packet transmit process  */
80 /*    _nx_nd_cache_periodic_update          ND Cache timeout routine.     */
81 /*                                                                        */
82 /*  RELEASE HISTORY                                                       */
83 /*                                                                        */
84 /*    DATE              NAME                      DESCRIPTION             */
85 /*                                                                        */
86 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
87 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
88 /*                                            resulting in version 6.1    */
89 /*                                                                        */
90 /**************************************************************************/
_nx_icmpv6_send_ns(NX_IP * ip_ptr,ULONG * neighbor_IP_address,INT send_slla,NXD_IPV6_ADDRESS * outgoing_address,INT sendUnicast,ND_CACHE_ENTRY * NDCacheEntry)91 VOID _nx_icmpv6_send_ns(NX_IP                 *ip_ptr,
92                         ULONG                 *neighbor_IP_address,
93                         INT                    send_slla,
94                         NXD_IPV6_ADDRESS      *outgoing_address,
95                         INT                    sendUnicast,
96                         ND_CACHE_ENTRY        *NDCacheEntry)
97 {
98 
99 NX_PACKET    *pkt_ptr;
100 USHORT        checksum;
101 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
102 UINT          compute_checksum = 1;
103 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
104 NX_ICMPV6_ND *nd_ptr;
105 ULONG        *src_address;
106 ULONG         dest_address[4];
107 NX_IP_DRIVER  driver_request;
108 
109 
110     /* Allocate a packet to build the ICMPv6 NS message in.  */
111 #ifdef NX_ENABLE_DUAL_PACKET_POOL
112     /* Allocate from auxiliary packet pool first. */
113     if (_nx_packet_allocate(ip_ptr -> nx_ip_auxiliary_packet_pool, &pkt_ptr, NX_IPv6_ICMP_PACKET, NX_NO_WAIT))
114     {
115         if (ip_ptr -> nx_ip_auxiliary_packet_pool != ip_ptr -> nx_ip_default_packet_pool)
116 #endif /* NX_ENABLE_DUAL_PACKET_POOL */
117         {
118             if (_nx_packet_allocate(ip_ptr -> nx_ip_default_packet_pool, &pkt_ptr, NX_IPv6_ICMP_PACKET, NX_NO_WAIT))
119             {
120 
121                 /* Error getting packet, so just get out!  */
122                 return;
123             }
124         }
125 #ifdef NX_ENABLE_DUAL_PACKET_POOL
126         else
127         {
128 
129             /* Error getting packet, so just get out!  */
130             return;
131         }
132     }
133 #endif /* NX_ENABLE_DUAL_PACKET_POOL */
134 
135     /* Add debug information. */
136     NX_PACKET_DEBUG(__FILE__, __LINE__, pkt_ptr);
137 
138     /* Mark the packet as IPv6 packet. */
139     /*lint -e{644} suppress variable might not be initialized, since "pkt_ptr" was initialized in _nx_packet_allocate. */
140     pkt_ptr -> nx_packet_ip_version = NX_IP_VERSION_V6;
141 
142     /* Setup the size of the ICMPv6 NS message */
143     pkt_ptr -> nx_packet_length = sizeof(NX_ICMPV6_ND);
144 
145     /* Add 8 more bytes if sending source link layer address. */
146     if (send_slla)
147     {
148         pkt_ptr -> nx_packet_length += 8;
149     }
150 
151     /* Check to see if the packet has enough room to fill with NS.  */
152     if ((UINT)(pkt_ptr -> nx_packet_data_end - pkt_ptr -> nx_packet_prepend_ptr) < pkt_ptr -> nx_packet_length)
153     {
154 
155         /* Error getting packet, so just get out!  */
156         _nx_packet_release(pkt_ptr);
157         return;
158     }
159 
160     /* Setup the append pointer to the end of the message. */
161     pkt_ptr -> nx_packet_append_ptr = pkt_ptr -> nx_packet_prepend_ptr + pkt_ptr -> nx_packet_length;
162 
163     /* Set up the ND message in the buffer. */
164     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
165     nd_ptr = (NX_ICMPV6_ND *)(pkt_ptr -> nx_packet_prepend_ptr);
166     nd_ptr -> nx_icmpv6_nd_header.nx_icmpv6_header_type = NX_ICMPV6_NEIGHBOR_SOLICITATION_TYPE;
167     nd_ptr -> nx_icmpv6_nd_header.nx_icmpv6_header_code = 0;
168     nd_ptr -> nx_icmpv6_nd_header.nx_icmpv6_header_checksum = 0;
169     nd_ptr -> nx_icmpv6_nd_flag = 0;
170 
171     /* copy the target IP address */
172     COPY_IPV6_ADDRESS(neighbor_IP_address, nd_ptr -> nx_icmpv6_nd_targetAddress);
173 
174     /* Convert the IP address to network byte order. */
175     NX_IPV6_ADDRESS_CHANGE_ENDIAN(nd_ptr -> nx_icmpv6_nd_targetAddress);
176 
177     if (sendUnicast)
178     {
179 
180         COPY_IPV6_ADDRESS(neighbor_IP_address, dest_address);
181     }
182     else
183     {
184 
185         /* Set up the next hop address, which is the target host's Solicited-Node
186            Multicast Address.  The address is formed by taking the last 24 bits of
187            the target IP address, in the form of:
188            0xFF02:0000:0000:0000:0000:0001:FFxx:xxxx */
189         SET_SOLICITED_NODE_MULTICAST_ADDRESS(dest_address, neighbor_IP_address);
190     }
191 
192     /* Set up source IP address to use for this packet.
193        If the global address is not valid yet, we use the unspecified address (::)
194        Otherwise the global address is used */
195     if (outgoing_address -> nxd_ipv6_address_state == NX_IPV6_ADDR_STATE_VALID)
196     {
197 
198         src_address = outgoing_address -> nxd_ipv6_address;
199     }
200     else
201     {
202 
203         /*lint -e{929} suppress cast of pointer to pointer, since it is necessary  */
204         src_address = (ULONG *)_nx_ipv6_unspecified_address;
205     }
206 
207     pkt_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr = outgoing_address;
208 
209     /* outgoing_address -> nxd_ipv6_address_attached can not be NULL. */
210     NX_ASSERT(outgoing_address -> nxd_ipv6_address_attached != NX_NULL);
211 
212     if (send_slla)  /* Need to send SLLA option */
213     {
214 
215     USHORT           *mac_addr;
216     NX_ICMPV6_OPTION *nd_options;
217 
218         /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary  */
219         nd_options = (NX_ICMPV6_OPTION *)NX_UCHAR_POINTER_ADD(nd_ptr, sizeof(NX_ICMPV6_ND));
220 
221         /* Fill in the options field */
222         nd_options -> nx_icmpv6_option_type = 1;
223         nd_options -> nx_icmpv6_option_length = 1;
224 
225         /* Fill in the source MAC address */
226         mac_addr = &nd_options ->  nx_icmpv6_option_data;
227         mac_addr[0] = (USHORT)(outgoing_address -> nxd_ipv6_address_attached -> nx_interface_physical_address_msw);
228         mac_addr[1] = (USHORT)((outgoing_address -> nxd_ipv6_address_attached -> nx_interface_physical_address_lsw & 0xFFFF0000) >> 16); /* lgtm[cpp/overflow-buffer] */
229         mac_addr[2] = (USHORT)(outgoing_address -> nxd_ipv6_address_attached -> nx_interface_physical_address_lsw & 0x0000FFFF); /* lgtm[cpp/overflow-buffer] */
230 
231         /* Byte swapping. */
232         NX_CHANGE_USHORT_ENDIAN(mac_addr[0]);
233         NX_CHANGE_USHORT_ENDIAN(mac_addr[1]); /* lgtm[cpp/overflow-buffer] */
234         NX_CHANGE_USHORT_ENDIAN(mac_addr[2]); /* lgtm[cpp/overflow-buffer] */
235     }
236 
237 #ifdef NX_DISABLE_ICMPV6_TX_CHECKSUM
238     compute_checksum = 0;
239 #endif /* NX_DISABLE_ICMPV6_TX_CHECKSUM */
240 
241 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
242     if (outgoing_address -> nxd_ipv6_address_attached -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM)
243     {
244         compute_checksum = 0;
245     }
246 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
247 #if defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
248     if (compute_checksum)
249 #endif /* defined(NX_DISABLE_ICMPV6_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
250     {
251         /* Compute checksum.  The return value is already in network byte order */
252         checksum = _nx_ip_checksum_compute(pkt_ptr, NX_PROTOCOL_ICMPV6, (UINT)pkt_ptr -> nx_packet_length, src_address, dest_address);
253 
254         checksum = (USHORT)(~checksum);
255 
256         /* Byte swapping. */
257         NX_CHANGE_USHORT_ENDIAN(checksum);
258 
259         nd_ptr -> nx_icmpv6_nd_header.nx_icmpv6_header_checksum = checksum;
260     }
261 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
262     else
263     {
264         pkt_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM;
265     }
266 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
267 
268     /* Add IPv6 header. */
269     if (_nx_ipv6_header_add(ip_ptr, &pkt_ptr, NX_PROTOCOL_ICMPV6, pkt_ptr -> nx_packet_length,
270                             255, src_address, dest_address, NX_NULL) != NX_SUCCESS)
271     {
272 
273         /* Failed to add header. */
274         return;
275     }
276 
277     /* Build the driver request. */
278     driver_request.nx_ip_driver_ptr                  = ip_ptr;
279     driver_request.nx_ip_driver_command              = NX_LINK_PACKET_SEND;
280     driver_request.nx_ip_driver_packet               = pkt_ptr;
281     driver_request.nx_ip_driver_interface            = outgoing_address -> nxd_ipv6_address_attached;
282     if (sendUnicast)
283     {
284     UCHAR *mac_addr;
285         mac_addr = NDCacheEntry -> nx_nd_cache_mac_addr;
286 
287         /* Set unicast destination MAC. */
288         driver_request.nx_ip_driver_physical_address_msw = ((ULONG)mac_addr[0] << 8) | mac_addr[1];
289         driver_request.nx_ip_driver_physical_address_lsw =
290             ((ULONG)mac_addr[2] << 24) | ((ULONG)mac_addr[3] << 16) | ((ULONG)mac_addr[4] << 8) | mac_addr[5];
291     }
292     else
293     {
294 
295         /*lint -e{644} suppress variable might not be initialized, since dest_address was initialized. */
296         driver_request.nx_ip_driver_physical_address_msw = 0x00003333;
297         driver_request.nx_ip_driver_physical_address_lsw = dest_address[3];
298     }
299 
300 #ifndef NX_DISABLE_IP_INFO
301 
302     /* Increment the IP packet sent count.  */
303     ip_ptr -> nx_ip_total_packets_sent++;
304 
305     /* Increment the IP bytes sent count.  */
306     ip_ptr -> nx_ip_total_bytes_sent +=  pkt_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV6_HEADER);
307 #endif
308 
309     /* Add debug information. */
310     NX_PACKET_DEBUG(__FILE__, __LINE__, pkt_ptr);
311 
312     /* Driver entry must not be NULL. */
313     NX_ASSERT(outgoing_address -> nxd_ipv6_address_attached -> nx_interface_link_driver_entry != NX_NULL);
314 
315     /* Send the IP packet out on the network via the attached driver.  */
316     (outgoing_address -> nxd_ipv6_address_attached -> nx_interface_link_driver_entry)(&driver_request);
317 }
318 
319 #endif /* FEATURE_NX_IPV6 */
320 
321