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