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 Protocol (IP)                                              */
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_ip.h"
29 #include "nx_tcp.h"
30 #include "nx_udp.h"
31 #include "nx_icmp.h"
32 #include "nx_igmp.h"
33 
34 
35 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
36 /**************************************************************************/
37 /*                                                                        */
38 /*  FUNCTION                                               RELEASE        */
39 /*                                                                        */
40 /*    _nx_ip_packet_checksum_compute                      PORTABLE C      */
41 /*                                                           6.2.1        */
42 /*  AUTHOR                                                                */
43 /*                                                                        */
44 /*    Yuxin Zhou, Microsoft Corporation                                   */
45 /*                                                                        */
46 /*  DESCRIPTION                                                           */
47 /*                                                                        */
48 /*    This function calculates checksum for packet need to be fragmented. */
49 /*    Only checksum upon IP layer is calculate.                           */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    packet_ptr                            Packet pointer                */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    None                                                                */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    nx_ip_checksum_compute                Compute UDP header checksum   */
62 /*                                                                        */
63 /*  CALLED BY                                                             */
64 /*                                                                        */
65 /*    _nx_ipv6_packet_send                                                */
66 /*    _nx_ip_driver_packet_send                                           */
67 /*    _nx_ip_fragment_packet                                              */
68 /*    _nx_ipv6_fragment_process                                           */
69 /*                                                                        */
70 /*  RELEASE HISTORY                                                       */
71 /*                                                                        */
72 /*    DATE              NAME                      DESCRIPTION             */
73 /*                                                                        */
74 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
75 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
76 /*                                            resulting in version 6.1    */
77 /*  03-08-2023     Tiejun Zhou              Modified comment(s), and      */
78 /*                                            fixed compiler warnings,    */
79 /*                                            resulting in version 6.2.1  */
80 /*                                                                        */
81 /**************************************************************************/
_nx_ip_packet_checksum_compute(NX_PACKET * packet_ptr)82 VOID  _nx_ip_packet_checksum_compute(NX_PACKET *packet_ptr)
83 {
84 ULONG             next_protocol;
85 UCHAR            *org_prepend_ptr;
86 ULONG             checksum;
87 ULONG             val;
88 UCHAR             is_done = NX_FALSE;
89 ULONG             ip_src_addr[4];
90 ULONG             ip_dst_addr[4];
91 ULONG             data_length = 0;
92 NX_TCP_HEADER    *tcp_header_ptr;
93 NX_UDP_HEADER    *udp_header_ptr;
94 #ifndef NX_DISABLE_IPV4
95 ULONG             ip_header_length;
96 NX_IPV4_HEADER   *ip_header_ptr;
97 NX_ICMP_HEADER   *icmpv4_header_ptr;
98 NX_IGMP_HEADER   *igmp_header_ptr;
99 #endif /* NX_DISABLE_IPV4 */
100 #ifdef FEATURE_NX_IPV6
101 USHORT            short_val;
102 NX_ICMPV6_HEADER *icmpv6_header_ptr;
103 NX_IPV6_HEADER   *ipv6_header_ptr;
104 #endif
105 
106     /* Get IP version. */
107 #ifndef NX_DISABLE_IPV4
108     if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
109     {
110         next_protocol = NX_PROTOCOL_IPV4;
111     }
112 #endif /* NX_DISABLE_IPV4 */
113 #ifdef FEATURE_NX_IPV6
114     if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
115     {
116         next_protocol = NX_PROTOCOL_IPV6;
117     }
118 #endif
119 
120     /* Store original prepend_ptr. */
121     org_prepend_ptr = packet_ptr -> nx_packet_prepend_ptr;
122 
123     /* Loop to process headers. */
124     while (!is_done)
125     {
126         switch (next_protocol)
127         {
128 #ifndef NX_DISABLE_IPV4
129         case NX_PROTOCOL_IPV4:
130         {
131 
132             /* It's assumed that the IP link driver has positioned the top pointer in the
133                packet to the start of the IP address... so that's where we will start.  */
134             /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
135             ip_header_ptr = (NX_IPV4_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
136 
137             /* Pick up the first word in the IP header. */
138             val = ip_header_ptr -> nx_ip_header_word_0;
139 
140             /* Convert to host byte order. */
141             NX_CHANGE_ULONG_ENDIAN(val);
142 
143             /* Obtain IP header length. */
144             ip_header_length =  (val & NX_IP_LENGTH_MASK) >> 24;
145 
146             /* Check if IPv4 checksum is enabled. */
147             if (packet_ptr -> nx_packet_interface_capability_flag & NX_INTERFACE_CAPABILITY_IPV4_TX_CHECKSUM)
148             {
149 
150                 checksum = _nx_ip_checksum_compute(packet_ptr, NX_IP_VERSION_V4,
151                                                    /* length is the size of IP header, including options */
152                                                    ip_header_length << 2,
153                                                    /* IPv4 header checksum doesn't care src/dest addresses */
154                                                    NULL, NULL);
155 
156                 val = (ULONG)(~checksum);
157                 val = val & NX_LOWER_16_MASK;
158 
159                 /* Convert to network byte order. */
160                 NX_CHANGE_ULONG_ENDIAN(val);
161 
162                 /* Now store the checksum in the IP header.  */
163                 ip_header_ptr -> nx_ip_header_word_2 =  ip_header_ptr -> nx_ip_header_word_2 | val;
164 
165                 /* Clear checksum flag. */
166                 packet_ptr -> nx_packet_interface_capability_flag  &= (ULONG)(~NX_INTERFACE_CAPABILITY_IPV4_TX_CHECKSUM);
167             }
168 
169 
170             /* Get src and dst addresses. */
171             ip_src_addr[0] = ip_header_ptr -> nx_ip_header_source_ip;
172             ip_dst_addr[0] = ip_header_ptr -> nx_ip_header_destination_ip;
173             NX_CHANGE_ULONG_ENDIAN(ip_src_addr[0]);
174             NX_CHANGE_ULONG_ENDIAN(ip_dst_addr[0]);
175 
176             /* Get next protocol. */
177             val = ip_header_ptr -> nx_ip_header_word_2;
178             NX_CHANGE_ULONG_ENDIAN(val);
179             next_protocol = (val >> 16) & 0xFF;
180 
181             /* Remove IPv4 header. */
182             packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr + (ip_header_length << 2);
183             data_length = packet_ptr -> nx_packet_length - (ip_header_length << 2);
184             break;
185         }
186 #endif /* NX_DISABLE_IPV4 */
187 
188         case NX_PROTOCOL_TCP:
189         {
190 
191             /* Check if TCP checksum is enabled. */
192             if (packet_ptr -> nx_packet_interface_capability_flag  & NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM)
193             {
194 
195                 /* Calculate the TCP checksum without protection.  */
196                 checksum =  _nx_ip_checksum_compute(packet_ptr, NX_PROTOCOL_TCP,
197                                                     data_length,
198                                                     ip_src_addr, ip_dst_addr);
199 
200                 /* Pickup the pointer to the head of the TCP packet.  */
201                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
202                 tcp_header_ptr =  (NX_TCP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
203 
204                 checksum = ~checksum & NX_LOWER_16_MASK;
205 
206                 /* Move the checksum into header.  */
207                 NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_4);
208                 tcp_header_ptr -> nx_tcp_header_word_4 |=  (checksum << NX_SHIFT_BY_16);
209                 NX_CHANGE_ULONG_ENDIAN(tcp_header_ptr -> nx_tcp_header_word_4);
210 
211                 /* Clear checksum flag. */
212                 packet_ptr -> nx_packet_interface_capability_flag  &= (ULONG)(~NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM);
213             }
214 
215             /* No necessary to process next protocol. */
216             is_done = NX_TRUE;
217             break;
218         }
219 
220         case NX_PROTOCOL_UDP:
221         {
222 
223             /* Check if UDP checksum is enabled. */
224             if (packet_ptr -> nx_packet_interface_capability_flag  & NX_INTERFACE_CAPABILITY_UDP_TX_CHECKSUM)
225             {
226 
227                 /* Calculate the UDP checksum without protection.  */
228                 checksum =  _nx_ip_checksum_compute(packet_ptr, NX_PROTOCOL_UDP,
229                                                     data_length,
230                                                     ip_src_addr, ip_dst_addr);
231 
232                 /* Pickup the pointer to the head of the UDP packet.  */
233                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
234                 udp_header_ptr = (NX_UDP_HEADER *)(packet_ptr -> nx_packet_prepend_ptr);
235 
236                 /* Move the checksum into header.  */
237                 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_1);
238                 udp_header_ptr -> nx_udp_header_word_1 = udp_header_ptr -> nx_udp_header_word_1 | (~checksum & NX_LOWER_16_MASK);
239                 NX_CHANGE_ULONG_ENDIAN(udp_header_ptr -> nx_udp_header_word_1);
240 
241                 /* Clear checksum flag. */
242                 packet_ptr -> nx_packet_interface_capability_flag  &= (ULONG)(~NX_INTERFACE_CAPABILITY_UDP_TX_CHECKSUM);
243             }
244 
245             /* No necessary to process next protocol. */
246             is_done = NX_TRUE;
247             break;
248         }
249 
250 #ifndef NX_DISABLE_IPV4
251         case NX_PROTOCOL_ICMP:
252         {
253 
254             /* Check if ICMPv4 checksum is enabled. */
255             if (packet_ptr -> nx_packet_interface_capability_flag  & NX_INTERFACE_CAPABILITY_ICMPV4_TX_CHECKSUM)
256             {
257 
258                 /* Calculate the ICMPv4 checksum without protection.  */
259                 checksum =  _nx_ip_checksum_compute(packet_ptr, NX_IP_ICMP,
260                                                     data_length,
261                                                     /* ICMPV4 header checksum doesn't care src/dest addresses */
262                                                     NULL, NULL);
263 
264                 /* Pickup the pointer to the head of the ICMPv4 packet.  */
265                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
266                 icmpv4_header_ptr =  (NX_ICMP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
267 
268                 /* Move the checksum into header.  */
269                 NX_CHANGE_ULONG_ENDIAN(icmpv4_header_ptr -> nx_icmp_header_word_0);
270                 icmpv4_header_ptr -> nx_icmp_header_word_0 =  icmpv4_header_ptr -> nx_icmp_header_word_0 | (~checksum & NX_LOWER_16_MASK);
271                 NX_CHANGE_ULONG_ENDIAN(icmpv4_header_ptr -> nx_icmp_header_word_0);
272 
273                 /* Clear checksum flag. */
274                 packet_ptr -> nx_packet_interface_capability_flag  &= (ULONG)(~NX_INTERFACE_CAPABILITY_ICMPV4_TX_CHECKSUM);
275             }
276 
277             /* No necessary to process next protocol. */
278             is_done = NX_TRUE;
279             break;
280         }
281 
282         case NX_PROTOCOL_IGMP:
283         {
284 
285             /* Check if IGMP checksum is enabled. */
286             if (packet_ptr -> nx_packet_interface_capability_flag  & NX_INTERFACE_CAPABILITY_IGMP_TX_CHECKSUM)
287             {
288 
289                 /* Pickup the pointer to the head of the IGMP packet.  */
290                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
291                 igmp_header_ptr =  (NX_IGMP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
292 
293                 /* Change the endian.  */
294                 NX_CHANGE_ULONG_ENDIAN(igmp_header_ptr -> nx_igmp_header_word_0);
295                 NX_CHANGE_ULONG_ENDIAN(igmp_header_ptr -> nx_igmp_header_word_1);
296 
297                 /* Calculate the checksum.  */
298                 val =       igmp_header_ptr -> nx_igmp_header_word_0;
299                 checksum =  (val >> NX_SHIFT_BY_16);
300                 checksum += (val & NX_LOWER_16_MASK);
301                 val =      igmp_header_ptr -> nx_igmp_header_word_1;
302                 checksum += (val >> NX_SHIFT_BY_16);
303                 checksum += (val & NX_LOWER_16_MASK);
304 
305                 /* Add in the carry bits into the checksum.  */
306                 checksum = (checksum >> NX_SHIFT_BY_16) + (checksum & NX_LOWER_16_MASK);
307 
308                 /* Do it again in case previous operation generates an overflow.  */
309                 checksum = (checksum >> NX_SHIFT_BY_16) + (checksum & NX_LOWER_16_MASK);
310 
311                 /* Place the checksum into the first header word.  */
312                 igmp_header_ptr -> nx_igmp_header_word_0 =  igmp_header_ptr -> nx_igmp_header_word_0 | (~checksum & NX_LOWER_16_MASK);
313 
314                 /* Change the endian.  */
315                 NX_CHANGE_ULONG_ENDIAN(igmp_header_ptr -> nx_igmp_header_word_0);
316                 NX_CHANGE_ULONG_ENDIAN(igmp_header_ptr -> nx_igmp_header_word_1);
317 
318                 /* Clear checksum flag. */
319                 packet_ptr -> nx_packet_interface_capability_flag  &= (ULONG)(~NX_INTERFACE_CAPABILITY_IGMP_TX_CHECKSUM);
320             }
321 
322             /* No necessary to process next protocol. */
323             is_done = NX_TRUE;
324             break;
325         }
326 #endif /* NX_DISABLE_IPV4 */
327 
328 #ifdef FEATURE_NX_IPV6
329         case NX_PROTOCOL_ICMPV6:
330         {
331 
332             /* Check if ICMPv6 checksum is enabled. */
333             if (packet_ptr -> nx_packet_interface_capability_flag  & NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM)
334             {
335 
336                 /* Calculate the ICMPv6 checksum without protection.  */
337                 checksum =  _nx_ip_checksum_compute(packet_ptr, NX_PROTOCOL_ICMPV6,
338                                                     data_length,
339                                                     ip_src_addr, ip_dst_addr);
340 
341                 /* Pickup the pointer to the head of the ICMPv6 packet.  */
342                 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
343                 icmpv6_header_ptr =  (NX_ICMPV6_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
344 
345                 short_val = (USHORT) ~checksum;
346 
347                 /* Move the checksum into header.  */
348                 NX_CHANGE_USHORT_ENDIAN(short_val);
349                 icmpv6_header_ptr -> nx_icmpv6_header_checksum = short_val;
350 
351                 /* Clear checksum flag. */
352                 packet_ptr -> nx_packet_interface_capability_flag  &= (ULONG)(~NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM);
353             }
354 
355             /* No necessary to process next protocol. */
356             is_done = NX_TRUE;
357             break;
358         }
359 
360         case NX_PROTOCOL_IPV6:
361         {
362 
363             /* Points to the base of IPv6 header. */
364             /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
365             ipv6_header_ptr = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
366 
367             /* Get src and dst addresses. */
368             COPY_IPV6_ADDRESS(ipv6_header_ptr -> nx_ip_header_source_ip, ip_src_addr);
369             COPY_IPV6_ADDRESS(ipv6_header_ptr -> nx_ip_header_destination_ip, ip_dst_addr);
370             NX_IPV6_ADDRESS_CHANGE_ENDIAN(ip_src_addr);
371             NX_IPV6_ADDRESS_CHANGE_ENDIAN(ip_dst_addr);
372 
373             /* Get next protocol. */
374             val = ipv6_header_ptr -> nx_ip_header_word_1;
375             NX_CHANGE_ULONG_ENDIAN(val);
376             next_protocol = (val >> 8) & 0xFF;
377 
378             /* Remove IPv6 header. */
379             packet_ptr -> nx_packet_prepend_ptr += (ULONG)sizeof(NX_IPV6_HEADER);
380             data_length = packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV6_HEADER);
381             break;
382         }
383 #endif
384 
385         default:
386             /* Unsupported protocol. */
387             is_done = NX_TRUE;
388             break;
389         }
390     }
391 
392 
393     /* Restore original prepend_ptr. */
394     packet_ptr -> nx_packet_prepend_ptr = org_prepend_ptr;
395     return;
396 }
397 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
398 
399