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 Checksum Computation                              */
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_icmp.h"
29 #include "nx_icmpv6.h"
30 #include "nx_ip.h"
31 
32 
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _nx_ip_checksum_compute                           PORTABLE C        */
38 /*                                                           6.1          */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    Yuxin Zhou, Microsoft Corporation                                   */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function computes the checksum from the supplied packet        */
46 /*    pointer and IP address fields required for the pseudo header.       */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    packet_ptr                            Pointer to packet             */
51 /*    protocol                              Protocol type                 */
52 /*    data_length                           Size of the protocol payload  */
53 /*    src_ip_addr                           IPv4 or IPv6 address, used in */
54 /*                                             constructing pseudo header.*/
55 /*    dest_ip_addr                          IPv4 or IPv6 address, used in */
56 /*                                             constructing pseudo header.*/
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    computed checksum                                                   */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    None                                                                */
65 /*                                                                        */
66 /*  CALLED BY                                                             */
67 /*                                                                        */
68 /*    NetX Duo internal routines                                          */
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 /*                                                                        */
78 /**************************************************************************/
_nx_ip_checksum_compute(NX_PACKET * packet_ptr,ULONG protocol,UINT data_length,ULONG * src_ip_addr,ULONG * dest_ip_addr)79 USHORT  _nx_ip_checksum_compute(NX_PACKET *packet_ptr, ULONG protocol,
80                                 UINT data_length, ULONG *src_ip_addr,
81                                 ULONG *dest_ip_addr)
82 {
83 
84 ULONG      checksum = 0;
85 USHORT     tmp;
86 USHORT    *short_ptr;
87 ULONG     *long_ptr;
88 #ifndef NX_DISABLE_PACKET_CHAIN
89 ULONG      packet_size;
90 #endif /* NX_DISABLE_PACKET_CHAIN */
91 NX_PACKET *current_packet;
92 ALIGN_TYPE end_ptr;
93 #ifdef FEATURE_NX_IPV6
94 UINT       i;
95 #endif
96 
97     /* For computing TCP/UDP/ICMPv6, we need to include the pseudo header.
98        The ICMPv4 checksum does not cover the pseudo header. */
99     if ((protocol == NX_PROTOCOL_UDP) ||
100 #ifdef FEATURE_NX_IPV6
101         (protocol == NX_PROTOCOL_ICMPV6) ||
102 #endif /* FEATURE_NX_IPV6 */
103         (protocol == NX_PROTOCOL_TCP))
104     {
105 
106     USHORT *src_ip_short, *dest_ip_short;
107 
108         checksum = protocol;
109 
110         /* The addresses must not be null.  */
111         NX_ASSERT((src_ip_addr != NX_NULL) && (dest_ip_addr != NX_NULL));
112 
113         /*lint -e{929} -e{740} suppress cast of pointer to pointer, since it is necessary  */
114         src_ip_short = (USHORT *)src_ip_addr;
115 
116         /*lint -e{929} -e{740} suppress cast of pointer to pointer, since it is necessary  */
117         dest_ip_short = (USHORT *)dest_ip_addr;
118 
119 
120         checksum += src_ip_short[0];
121         checksum += src_ip_short[1];
122         checksum += dest_ip_short[0];
123         checksum += dest_ip_short[1];
124 
125 #ifdef FEATURE_NX_IPV6
126 
127         /* Note that the IPv6 address is 128 bits/4 words
128            compared with the 32 IPv4 address.*/
129         if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
130         {
131 
132             for (i = 2; i < 8; i++)
133             {
134 
135                 checksum += dest_ip_short[i];
136                 checksum += src_ip_short[i];
137             }
138         }
139 #endif /* FEATURE_NX_IPV6 */
140 
141         /* Take care of data length */
142         checksum += data_length;
143 
144         /* Fold a 4-byte value into a two byte value */
145         checksum = (checksum >> 16) + (checksum & 0xFFFF);
146 
147         /* Do it again in case previous operation generates an overflow */
148         checksum = (checksum >> 16) + (checksum & 0xFFFF);
149 
150         /* Convert to network byte order. */
151         tmp = (USHORT)checksum;
152         NX_CHANGE_USHORT_ENDIAN(tmp);
153         checksum = tmp;
154     }
155 
156     /* Now we need to go through the payloads */
157 
158     /* Setup the pointer to the start of the packet.  */
159     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
160     long_ptr =  (ULONG *)packet_ptr -> nx_packet_prepend_ptr;
161 
162     /* Initialize the current packet to the input packet pointer.  */
163     current_packet =  packet_ptr;
164 
165 #ifndef NX_DISABLE_PACKET_CHAIN
166     /* Loop the packet. */
167     while (current_packet)
168     {
169 
170         /* Calculate current packet size. */
171         /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
172         packet_size = (ULONG)(current_packet -> nx_packet_append_ptr - current_packet -> nx_packet_prepend_ptr);
173 
174         /* Calculate the end address in this packet. */
175         if (data_length > (UINT)packet_size)
176         {
177 
178             /*lint -e{927} -e{923} -e{826} suppress cast of pointer to pointer, since it is necessary  */
179             end_ptr = ((ALIGN_TYPE)current_packet -> nx_packet_append_ptr) & (ALIGN_TYPE)(~3);
180         }
181         else
182         {
183 #endif /* NX_DISABLE_PACKET_CHAIN */
184             /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
185             end_ptr = (ALIGN_TYPE)current_packet -> nx_packet_prepend_ptr + data_length - 3;
186 #ifndef NX_DISABLE_PACKET_CHAIN
187         }
188 #endif /* NX_DISABLE_PACKET_CHAIN */
189 
190         /* Set the start address in this packet. */
191         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
192         long_ptr = (ULONG *)current_packet -> nx_packet_prepend_ptr;
193 
194         /*lint -e{946} suppress pointer subtraction, since it is necessary. */
195         if ((ALIGN_TYPE)long_ptr < end_ptr)
196         {
197 
198             /* Calculate the data_length. */
199             /*lint -e{923} suppress cast of pointer to ULONG.  */
200             data_length -= (UINT)(((end_ptr + 3) & (ALIGN_TYPE)(~3llu)) - (ALIGN_TYPE)long_ptr);
201 
202             /* Loop to calculate the packet's checksum.  */
203             /*lint -e{946} suppress pointer subtraction, since it is necessary. */
204             while ((ALIGN_TYPE)long_ptr < end_ptr)
205             {
206                 checksum += (*long_ptr & NX_LOWER_16_MASK);
207                 checksum += (*long_ptr >> NX_SHIFT_BY_16);
208                 long_ptr++;
209             }
210         }
211 #ifndef NX_DISABLE_PACKET_CHAIN
212 
213         /* Determine if we are at the end of the current packet.  */
214         if ((data_length > 0) && (current_packet -> nx_packet_next))
215         {
216 
217             /* Is append_ptr two bytes aligned but not four bytes aligned? */
218             /*lint -e{923} suppress cast of pointer to ULONG.  */
219             if ((((ALIGN_TYPE)current_packet -> nx_packet_append_ptr) & 3) == 2)
220             {
221 
222                 /* Yes it is. Process the last two bytes in chaining packets. */
223                 /*lint -e{929} -e{740} suppress cast of pointer to pointer, since it is necessary  */
224                 short_ptr = (USHORT *)long_ptr;
225 
226                 /*lint -e{929} -e{740} suppress cast of pointer to pointer, since it is necessary  */
227                 checksum += *short_ptr;
228                 data_length -= 2;
229             }
230 
231             /* We have crossed the packet boundary.  Move to the next packet
232                structure.  */
233             current_packet =  current_packet -> nx_packet_next;
234         }
235         else
236         {
237 
238             /* End the loop.  */
239             current_packet = NX_NULL;
240         }
241     }
242 #endif /* NX_DISABLE_PACKET_CHAIN */
243 
244     /* Determine if there is only one byte left. */
245     if (data_length)
246     {
247 
248         /* Set the short_ptr. */
249         short_ptr = (USHORT *)(long_ptr);
250 
251         /* Check the data length.  */
252         if (data_length == 1)
253         {
254             *((UCHAR *)short_ptr + 1) = 0;
255         }
256         else if (data_length == 3)
257         {
258             checksum += *short_ptr;
259             short_ptr++;
260 
261             *((UCHAR *)short_ptr + 1) = 0;
262         }
263 
264         checksum += *short_ptr;
265     }
266 
267     /* Fold a 4-byte value into a two byte value */
268     checksum = (checksum >> 16) + (checksum & 0xFFFF);
269 
270     /* Do it again in case previous operation generates an overflow */
271     checksum = (checksum >> 16) + (checksum & 0xFFFF);
272 
273     /* Convert to host byte order. */
274     tmp = (USHORT)checksum;
275     NX_CHANGE_USHORT_ENDIAN(tmp);
276 
277     /* Return the computed checksum.  */
278     return(tmp);
279 }
280 
281