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