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