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