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