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