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