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 (ICMPv4) */
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_icmp.h"
31
32 #ifdef NX_IPSEC_ENABLE
33 #include "nx_ipsec.h"
34 #endif /* NX_IPSEC_ENABLE */
35
36 #if !defined(NX_DISABLE_IPV4) && !defined(NX_DISABLE_ICMPV4_ERROR_MESSAGE)
37 /**************************************************************************/
38 /* */
39 /* FUNCTION RELEASE */
40 /* */
41 /* _nx_icmpv4_send_error_message PORTABLE C */
42 /* 6.1 */
43 /* AUTHOR */
44 /* */
45 /* Yuxin Zhou, Microsoft Corporation */
46 /* */
47 /* DESCRIPTION */
48 /* */
49 /* This function is called by various IPv4 components to send an */
50 /* error message when necessary. */
51 /* */
52 /* INPUT */
53 /* */
54 /* ip_ptr IP stack instance */
55 /* offending_packet The packet that caused the */
56 /* error. */
57 /* word1 ICMPv4 error message header */
58 /* field, progarmmed by */
59 /* the caller. */
60 /* error_pointer Pointer to the byte that */
61 /* caused the error */
62 /* */
63 /* OUTPUT */
64 /* */
65 /* None */
66 /* */
67 /* CALLS */
68 /* */
69 /* _nx_ip_checksum_compute Computer ICMP checksum */
70 /* _nx_ip_packet_send Send ICMP packet out */
71 /* _nx_packet_allocate Packet allocate */
72 /* _nx_packet_release Release packet back to pool */
73 /* _nx_ip_route_find Find outgoing interface for */
74 /* sending packet */
75 /* */
76 /* CALLED BY */
77 /* */
78 /* */
79 /* RELEASE HISTORY */
80 /* */
81 /* DATE NAME DESCRIPTION */
82 /* */
83 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
84 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
85 /* resulting in version 6.1 */
86 /* */
87 /**************************************************************************/
_nx_icmpv4_send_error_message(NX_IP * ip_ptr,NX_PACKET * offending_packet,ULONG word1,ULONG error_pointer)88 VOID _nx_icmpv4_send_error_message(NX_IP *ip_ptr, NX_PACKET *offending_packet,
89 ULONG word1, ULONG error_pointer)
90 {
91
92 NX_PACKET *pkt_ptr;
93 USHORT checksum;
94 #if defined(NX_DISABLE_ICMPV4_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
95 UINT compute_checksum = 1;
96 #endif /* defined(NX_DISABLE_ICMPV4_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
97 NX_ICMPV4_ERROR *icmpv4_error;
98 NX_IPV4_HEADER *ip_header_ptr;
99 UINT ip_header_size;
100 UINT bytes_to_copy, i;
101 ULONG src_ip;
102 ULONG next_hop_address = NX_NULL;
103 ULONG *src_packet, *dest_packet;
104 NX_INTERFACE *if_ptr;
105
106 #ifdef NX_IPSEC_ENABLE
107 VOID *sa = NX_NULL;
108 UINT ret = 0;
109 ULONG data_offset;
110 NXD_ADDRESS src_addr;
111 NXD_ADDRESS dest_addr;
112 #endif /* NX_IPSEC_ENABLE */
113
114
115 /* Add debug information. */
116 NX_PACKET_DEBUG(__FILE__, __LINE__, offending_packet);
117
118 /* Do not send ICMPv4 error message if ICMPv4 is not enabled. */
119 if (ip_ptr -> nx_ip_icmpv4_packet_process == NX_NULL)
120 {
121 return;
122 }
123
124 /* Find out the source and destination IP addresses of the offending packet. */
125 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
126 ip_header_ptr = (NX_IPV4_HEADER *)(offending_packet -> nx_packet_ip_header);
127 src_ip = ip_header_ptr -> nx_ip_header_source_ip;
128
129 /* Get the incoming interface. */
130 if_ptr = offending_packet -> nx_packet_address.nx_packet_interface_ptr;
131
132 /* An ICMP error message MUST NOT be sent as the result of receiving:
133 RFC1122, Section3.2.2, Page39. */
134
135 /* A datagram destined to an IP broadcast or IP multicast address. */
136 if ((ip_header_ptr -> nx_ip_header_destination_ip == NX_IP_LIMITED_BROADCAST) ||
137 ((ip_header_ptr -> nx_ip_header_destination_ip & NX_IP_CLASS_D_MASK) == NX_IP_CLASS_D_TYPE))
138 {
139 return;
140 }
141
142 /* A datagram sent as a link-layer broadcast. */
143 if (((ip_header_ptr -> nx_ip_header_destination_ip & if_ptr -> nx_interface_ip_network_mask) ==
144 if_ptr -> nx_interface_ip_network) &&
145 ((ip_header_ptr -> nx_ip_header_destination_ip & ~(if_ptr -> nx_interface_ip_network_mask)) ==
146 ~(if_ptr -> nx_interface_ip_network_mask)))
147 {
148 return;
149 }
150
151 /* A non-initial fragment. */
152 if (ip_header_ptr -> nx_ip_header_word_1 & NX_IP_OFFSET_MASK)
153 {
154 return;
155 }
156
157 /* A datagram whose source address does not define a single host,
158 e.g., a zero address, a loopback address, a broadcast address,
159 a multicast address, or a Class E address. */
160 if ((ip_header_ptr -> nx_ip_header_source_ip == 0) ||
161 ((ip_header_ptr -> nx_ip_header_source_ip >= NX_IP_LOOPBACK_FIRST) &&
162 (ip_header_ptr -> nx_ip_header_source_ip <= NX_IP_LOOPBACK_LAST)) ||
163 (ip_header_ptr -> nx_ip_header_source_ip == NX_IP_LIMITED_BROADCAST) ||
164 ((ip_header_ptr -> nx_ip_header_source_ip & NX_IP_CLASS_D_MASK) == NX_IP_CLASS_D_TYPE))
165 {
166 return;
167 }
168
169 /* Allocate a packet to build the ICMPv4 error message in. */
170 if (_nx_packet_allocate(ip_ptr -> nx_ip_default_packet_pool, &pkt_ptr, NX_IPv4_ICMP_PACKET, NX_NO_WAIT))
171 {
172
173 /* Error getting packet, so just get out! */
174 return;
175 }
176
177 /* Add debug information. */
178 NX_PACKET_DEBUG(__FILE__, __LINE__, pkt_ptr);
179
180 /* Mark the packet as IPv4. */
181 /*lint -e{644} suppress variable might not be initialized, since "pkt_ptr" was initialized in _nx_packet_allocate. */
182 pkt_ptr -> nx_packet_ip_version = NX_IP_VERSION_V4;
183
184 /* Size of the message is ICMPv4 */
185 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
186 icmpv4_error = (NX_ICMPV4_ERROR *)(pkt_ptr -> nx_packet_prepend_ptr);
187 icmpv4_error -> nx_icmpv4_error_header.nx_icmpv4_header_type = (UCHAR)((word1 >> 24) & 0xFF);
188 icmpv4_error -> nx_icmpv4_error_header.nx_icmpv4_header_code = (UCHAR)((word1 >> 16) & 0xFF);
189 icmpv4_error -> nx_icmpv4_error_header.nx_icmpv4_header_checksum = 0;
190 icmpv4_error -> nx_icmpv4_error_pointer = (error_pointer << 24);
191
192 /* Change to network byte order. */
193 NX_CHANGE_ULONG_ENDIAN(icmpv4_error -> nx_icmpv4_error_pointer);
194
195 /* IP Header + 64 bits (64 bits = 2 ULONGs) of Data Datagram. */
196 ip_header_size = ((ip_header_ptr -> nx_ip_header_word_0 & 0x0F000000) >> 24);
197 bytes_to_copy = (UINT)((ip_header_size + 2) * sizeof(ULONG));
198
199 /* Set the packet length and pointers. The length will be increased to include
200 the IPv4 header in the IP send function. The Prepend function will be similarly
201 updated in the IP send function. */
202 pkt_ptr -> nx_packet_length = bytes_to_copy + (ULONG)sizeof(NX_ICMPV4_ERROR);
203 pkt_ptr -> nx_packet_append_ptr = pkt_ptr -> nx_packet_prepend_ptr + pkt_ptr -> nx_packet_length;
204
205 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
206 src_packet = (ULONG *)(offending_packet -> nx_packet_ip_header);
207
208 /*lint -e{923} suppress cast between pointer and ULONG, since it is necessary */
209 dest_packet = (ULONG *)NX_UCHAR_POINTER_ADD(icmpv4_error, sizeof(NX_ICMPV4_ERROR));
210
211 /* Endian swap the incoming IPv4 normal header to network byte order. */
212 for (i = 0; i < NX_IP_NORMAL_LENGTH; i++)
213 {
214 NX_CHANGE_ULONG_ENDIAN(*src_packet);
215 src_packet++;
216 }
217
218 /* Reset the packet pointer to the received packet IP header. */
219 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
220 src_packet = (ULONG *)(offending_packet -> nx_packet_ip_header);
221
222 /* Copy the data from the received packet to the ICMPv4 error packet. */
223 for (; bytes_to_copy > 0; bytes_to_copy -= 4)
224 {
225
226 *dest_packet++ = *src_packet++;
227 }
228
229 /* Get the IP header pointer. */
230 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
231 src_packet = (ULONG *)(offending_packet -> nx_packet_ip_header);
232
233 /* Endian swap the IPv4 normal header back to host byte order. */
234 for (i = 0; i < NX_IP_NORMAL_LENGTH; i++)
235 {
236 NX_CHANGE_ULONG_ENDIAN(*src_packet);
237 src_packet++;
238 }
239
240 /* Use the corresponding interface address as sender's address. */
241 pkt_ptr -> nx_packet_address.nx_packet_interface_ptr = offending_packet -> nx_packet_address.nx_packet_interface_ptr;
242
243 /* Figure out the best interface to send the ICMP packet on. */
244 _nx_ip_route_find(ip_ptr, src_ip,
245 &pkt_ptr -> nx_packet_address.nx_packet_interface_ptr,
246 &next_hop_address);
247
248 #ifdef NX_IPSEC_ENABLE
249
250 /* Check for possible SA match. */
251 if (ip_ptr -> nx_ip_packet_egress_sa_lookup != NX_NULL) /* IPsec is enabled. */
252 {
253
254 /* Set up IP address. */
255 src_addr.nxd_ip_version = NX_IP_VERSION_V4;
256 src_addr.nxd_ip_address.v4 = pkt_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_ip_address;
257 dest_addr.nxd_ip_version = NX_IP_VERSION_V4;
258 dest_addr.nxd_ip_address.v4 = src_ip;
259
260 /* If the SA has not been set. */
261 ret = ip_ptr -> nx_ip_packet_egress_sa_lookup(ip_ptr, /* IP ptr */
262 &src_addr, /* src_addr */
263 &dest_addr, /* dest_addr */
264 NX_PROTOCOL_ICMP, /* protocol */
265 0, /* src_port */
266 0, /* dest_port */
267 &data_offset, &sa,
268 ((word1 >> 16) & 0xFFFF));
269 if (ret == NX_IPSEC_TRAFFIC_BYPASS)
270 {
271 sa = NX_NULL;
272 data_offset = 0;
273 }
274 else if (ret == NX_IPSEC_TRAFFIC_DROP || ret == NX_IPSEC_TRAFFIC_PENDING_IKEV2)
275 {
276
277 /* IPSec SA disallows this packet. Drop the packet and return. */
278 _nx_packet_release(pkt_ptr);
279
280 return;
281 }
282 }
283
284 pkt_ptr -> nx_packet_ipsec_sa_ptr = sa;
285
286 #endif /* NX_IPSEC_ENABLE */
287
288 #ifdef NX_DISABLE_ICMPV4_TX_CHECKSUM
289 compute_checksum = 0;
290 #endif /* NX_DISABLE_ICMPV4_TX_CHECKSUM */
291
292 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
293 if (pkt_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_ICMPV4_TX_CHECKSUM)
294 {
295 compute_checksum = 0;
296 }
297 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
298
299 #ifdef NX_IPSEC_ENABLE
300 if ((sa != NX_NULL) && (((NX_IPSEC_SA *)sa) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE))
301 {
302 compute_checksum = 1;
303 }
304 #endif /* NX_IPSEC_ENABLE */
305 #if defined(NX_DISABLE_ICMPV4_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
306 if (compute_checksum)
307 #endif /* defined(NX_DISABLE_ICMPV4_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
308 {
309
310 /* Compute the checksum of the ICMP packet. */
311 checksum = _nx_ip_checksum_compute(pkt_ptr, NX_IP_ICMP,
312 (UINT)pkt_ptr -> nx_packet_length,
313 /* ICMPV4 checksum does not include
314 src/dest addresses */
315 NX_NULL, NX_NULL);
316
317 icmpv4_error -> nx_icmpv4_error_header.nx_icmpv4_header_checksum = (USHORT)(~checksum);
318
319 /* Swap to network byte order. */
320 NX_CHANGE_USHORT_ENDIAN(icmpv4_error -> nx_icmpv4_error_header.nx_icmpv4_header_checksum);
321 }
322 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
323 else
324 {
325 pkt_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_ICMPV4_TX_CHECKSUM;
326 }
327 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
328
329 /* Send the ICMP packet to the IP component. The time to live is set to 255. */
330 /*lint -e{644} suppress variable might not be initialized, since "next_hop_address" was initialized in _nx_ip_route_find. */
331 _nx_ip_packet_send(ip_ptr, pkt_ptr, src_ip,
332 NX_IP_NORMAL, 255, NX_IP_ICMP, NX_FRAGMENT_OKAY, next_hop_address);
333
334 return;
335 }
336 #endif /* !NX_DISABLE_IPV4 && !NX_DISABLE_ICMPV4_ERROR_MESSAGE */
337
338