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