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