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