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_packet.h"
31 
32 
33 #ifndef NX_DISABLE_FRAGMENTATION
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _nx_ip_fragment_packet                              PORTABLE C      */
39 /*                                                           6.1          */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    Yuxin Zhou, Microsoft Corporation                                   */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function breaks the supplied packet into fragments and sends   */
47 /*    them out through the associated IP driver.  This function uses the  */
48 /*    already built IP header and driver request structure for each       */
49 /*    packet fragment.                                                    */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    driver_req_ptr                        Pointer to driver request     */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    None                                                                */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _nx_ip_packet_checksum_compute        Compute checksum              */
62 /*    _nx_packet_allocate                   Allocate packet for fragment  */
63 /*    _nx_packet_transmit_release           Transmit packet release       */
64 /*    (ip_link_driver)                      User supplied link driver     */
65 /*                                                                        */
66 /*  CALLED BY                                                             */
67 /*                                                                        */
68 /*    _nx_arp_packet_receive                Received ARP packet processing*/
69 /*    _nx_ip_packet_send                    Send an IP packet             */
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_fragment_packet(struct NX_IP_DRIVER_STRUCT * driver_req_ptr)80 VOID  _nx_ip_fragment_packet(struct NX_IP_DRIVER_STRUCT *driver_req_ptr)
81 {
82 
83 #ifndef NX_DISABLE_IPV4
84 UINT            status;
85 ULONG           checksum;
86 ULONG           temp;
87 UCHAR          *source_ptr;
88 ULONG           remaining_bytes;
89 ULONG           fragment_size;
90 ULONG           copy_size;
91 ULONG           copy_remaining_size;
92 ULONG           fragment_offset = 0;
93 NX_IP_DRIVER    driver_request;
94 NX_PACKET      *source_packet;
95 NX_PACKET      *fragment_packet;
96 NX_IPV4_HEADER *source_header_ptr;
97 NX_IPV4_HEADER *fragment_header_ptr;
98 NX_IP          *ip_ptr;
99 #if defined(NX_DISABLE_IP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY)
100 UINT            compute_checksum = 1;
101 #endif /* defined(NX_DISABLE_IP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) */
102 
103 #ifdef NX_DISABLE_IP_TX_CHECKSUM
104     compute_checksum = 0;
105 #endif /* NX_DISABLE_IP_TX_CHECKSUM */
106 
107     /* Setup the local driver request packet that will be used for each
108        fragment.  There will be a unique packet pointer for each request, but
109        otherwise all the other fields will remain constant.  */
110     driver_request =  *driver_req_ptr;
111 
112     /* Setup the IP pointer.  */
113     ip_ptr =  driver_req_ptr -> nx_ip_driver_ptr;
114 
115 #ifndef NX_DISABLE_IP_INFO
116 
117     /* Increment the total number of fragment requests.  */
118     ip_ptr -> nx_ip_total_fragment_requests++;
119 #endif
120 
121     /* Pickup the source packet pointer.  */
122     source_packet =  driver_req_ptr -> nx_ip_driver_packet;
123 
124     /* Add debug information. */
125     NX_PACKET_DEBUG(__FILE__, __LINE__, source_packet);
126 
127 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
128 
129     /* Compute checksum for upper layer protocol. */
130     if (source_packet -> nx_packet_interface_capability_flag)
131     {
132         _nx_ip_packet_checksum_compute(source_packet);
133     }
134 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
135 
136     /* Build a pointer to the source IP header.  */
137     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
138     source_header_ptr =  (NX_IPV4_HEADER *)source_packet -> nx_packet_prepend_ptr;
139 
140     /* Endian swapping logic.  If NX_LITTLE_ENDIAN is specified, these macros will
141        swap the endian of the IP header.  */
142     NX_CHANGE_ULONG_ENDIAN(source_header_ptr -> nx_ip_header_word_0);
143     NX_CHANGE_ULONG_ENDIAN(source_header_ptr -> nx_ip_header_word_1);
144     NX_CHANGE_ULONG_ENDIAN(source_header_ptr -> nx_ip_header_word_2);
145     NX_CHANGE_ULONG_ENDIAN(source_header_ptr -> nx_ip_header_source_ip);
146     NX_CHANGE_ULONG_ENDIAN(source_header_ptr -> nx_ip_header_destination_ip);
147 
148     /* Pickup the length of the packet and the starting pointer.  */
149     remaining_bytes =  (source_packet -> nx_packet_length - (ULONG)sizeof(NX_IPV4_HEADER));
150     source_ptr =  source_packet -> nx_packet_prepend_ptr + sizeof(NX_IPV4_HEADER);
151 
152     /* Derive the fragment size.  */
153     fragment_size =  source_packet -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_ip_mtu_size - (ULONG)sizeof(NX_IPV4_HEADER);
154     fragment_size =  (fragment_size / NX_IP_ALIGN_FRAGS) * NX_IP_ALIGN_FRAGS;
155 
156     /* Loop to break the source packet into fragments and send each out through
157        the associated driver.  */
158     while (remaining_bytes)
159     {
160         /* Allocate a packet from the default packet pool.  */
161         status =  _nx_packet_allocate(ip_ptr -> nx_ip_default_packet_pool, &fragment_packet,
162                                       NX_IPv4_PACKET, TX_NO_WAIT);
163 
164         /* Determine if there is a packet available.  */
165         if (status)
166         {
167 
168 #ifndef NX_DISABLE_IP_INFO
169 
170             /* Increment the fragment failure count.  */
171             ip_ptr -> nx_ip_fragment_failures++;
172 
173             /* Increment the IP send packets dropped count.  */
174             ip_ptr -> nx_ip_send_packets_dropped++;
175 
176             /* Increment the IP transmit resource error count.  */
177             ip_ptr -> nx_ip_transmit_resource_errors++;
178 #endif
179 
180             /* Error, not enough packets to perform the fragmentation...  release the
181                source packet and return.  */
182             _nx_packet_transmit_release(driver_req_ptr -> nx_ip_driver_packet);
183             return;
184         }
185 
186         /* Add debug information. */
187         NX_PACKET_DEBUG(__FILE__, __LINE__, fragment_packet);
188 
189         /* Set the proper interface. */
190         /*lint -e{644} suppress variable might not be initialized, since "fragment_packet" was initialized in _nx_packet_allocate. */
191         fragment_packet -> nx_packet_address.nx_packet_interface_ptr = driver_req_ptr -> nx_ip_driver_packet -> nx_packet_address.nx_packet_interface_ptr;
192 
193         /* Calculate the size of this fragment.  */
194         if (remaining_bytes > fragment_size)
195         {
196             copy_remaining_size =  fragment_size;
197             remaining_bytes -= fragment_size;
198         }
199         else
200         {
201             copy_remaining_size = remaining_bytes;
202             remaining_bytes = 0;
203         }
204 
205         /* Copy data.  */
206         while (copy_remaining_size)
207         {
208 
209             /* We need to copy the remaining bytes into the new packet and then move to the next
210                packet.  */
211             /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
212             if (copy_remaining_size > (ULONG)(source_packet -> nx_packet_append_ptr - source_ptr))
213             {
214                 copy_size = (ULONG)(source_packet -> nx_packet_append_ptr - source_ptr);
215             }
216             else
217             {
218                 copy_size = copy_remaining_size;
219             }
220 
221             status = _nx_packet_data_append(fragment_packet, source_ptr, copy_size, ip_ptr -> nx_ip_default_packet_pool, NX_NO_WAIT);
222 
223             /* Determine if there is a packet available.  */
224             if (status)
225             {
226 
227 #ifndef NX_DISABLE_IP_INFO
228 
229                 /* Increment the fragment failure count.  */
230                 ip_ptr -> nx_ip_fragment_failures++;
231 
232                 /* Increment the IP send packets dropped count.  */
233                 ip_ptr -> nx_ip_send_packets_dropped++;
234 
235                 /* Increment the IP transmit resource error count.  */
236                 ip_ptr -> nx_ip_transmit_resource_errors++;
237 #endif
238 
239                 /* Error, not enough packets to perform the fragmentation...  release the
240                    source packet and return.  */
241                 _nx_packet_transmit_release(driver_req_ptr -> nx_ip_driver_packet);
242                 _nx_packet_release(fragment_packet);
243                 return;
244             }
245 
246             /* Reduce the remaining size. */
247             copy_remaining_size -= copy_size;
248 
249             /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
250             if (copy_size == (ULONG)(source_packet -> nx_packet_append_ptr - source_ptr))
251             {
252 
253                 /* Move to the next physical packet in the source message.  */
254                 /* Determine if there is a next packet.  */
255                 if (source_packet -> nx_packet_next)
256                 {
257 
258                     /* Move to the next physical packet in the source message.  */
259                     source_packet =  source_packet -> nx_packet_next;
260 
261                     /* Setup new source pointer.  */
262                     source_ptr =  source_packet -> nx_packet_prepend_ptr;
263                 }
264                 else if (remaining_bytes)
265                 {
266 
267                     /* Error, no next packet but current packet is exhausted and there are
268                        remaining bytes.  */
269 
270 #ifndef NX_DISABLE_IP_INFO
271 
272                     /* Increment the invalid transmit packet count.  */
273                     ip_ptr -> nx_ip_invalid_transmit_packets++;
274 
275                     /* Increment the fragment failures count.  */
276                     ip_ptr -> nx_ip_fragment_failures++;
277 #endif
278 
279                     /* Error, not enough packets to perform the fragmentation...  release the
280                        source packet and return.  */
281                     _nx_packet_transmit_release(driver_req_ptr -> nx_ip_driver_packet);
282                     _nx_packet_release(fragment_packet);
283                     return;
284                 }
285             }
286             else
287             {
288 
289                 /* Copy finished. */
290                 source_ptr += copy_size;
291             }
292         }
293 
294         /* Setup the fragment packet pointers.  */
295         fragment_packet -> nx_packet_prepend_ptr = fragment_packet -> nx_packet_prepend_ptr - sizeof(NX_IPV4_HEADER);
296         fragment_packet -> nx_packet_length += (ULONG)sizeof(NX_IPV4_HEADER);
297 
298         /* Setup the fragment's IP header.  */
299         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
300         fragment_header_ptr =  (NX_IPV4_HEADER *)fragment_packet -> nx_packet_prepend_ptr;
301 
302         /* Setup the new IP header.  */
303         fragment_header_ptr -> nx_ip_header_word_0 =          (source_header_ptr -> nx_ip_header_word_0 & ~NX_LOWER_16_MASK) |
304             fragment_packet -> nx_packet_length;
305         fragment_header_ptr -> nx_ip_header_word_1 =          source_header_ptr -> nx_ip_header_word_1 | (fragment_offset / 8);
306         fragment_header_ptr -> nx_ip_header_word_2 =          source_header_ptr -> nx_ip_header_word_2 & ~NX_LOWER_16_MASK;
307         fragment_header_ptr -> nx_ip_header_source_ip =       source_header_ptr -> nx_ip_header_source_ip;
308         fragment_header_ptr -> nx_ip_header_destination_ip =  source_header_ptr -> nx_ip_header_destination_ip;
309 
310         /* Determine if this is the last fragment.  */
311         if (remaining_bytes)
312         {
313 
314             /* Not the last fragment, so set the more fragments bit.  */
315             fragment_header_ptr -> nx_ip_header_word_1 =  fragment_header_ptr -> nx_ip_header_word_1 | NX_IP_MORE_FRAGMENT;
316         }
317 
318 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
319         if (fragment_packet -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_IPV4_TX_CHECKSUM)
320         {
321             compute_checksum = 0;
322         }
323 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
324 
325 #if defined(NX_DISABLE_IP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY)
326         if (compute_checksum)
327 #endif /* defined(NX_DISABLE_IP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) */
328         {
329 
330             /* Build the IP checksum for this fragment.  */
331             temp =       fragment_header_ptr -> nx_ip_header_word_0;
332             checksum =   (temp >> NX_SHIFT_BY_16) + (temp & NX_LOWER_16_MASK);
333             temp =       fragment_header_ptr -> nx_ip_header_word_1;
334             checksum +=  (temp >> NX_SHIFT_BY_16) + (temp & NX_LOWER_16_MASK);
335             temp =       fragment_header_ptr -> nx_ip_header_word_2;
336             checksum +=  (temp >> NX_SHIFT_BY_16);
337             temp =       fragment_header_ptr -> nx_ip_header_source_ip;
338             checksum +=  (temp >> NX_SHIFT_BY_16) + (temp & NX_LOWER_16_MASK);
339             temp =       fragment_header_ptr -> nx_ip_header_destination_ip;
340             checksum +=  (temp >> NX_SHIFT_BY_16) + (temp & NX_LOWER_16_MASK);
341 
342             /* Add in the carry bits into the checksum.  */
343             checksum = (checksum >> NX_SHIFT_BY_16) + (checksum & NX_LOWER_16_MASK);
344 
345             /* Do it again in case previous operation generates an overflow.  */
346             checksum = (checksum >> NX_SHIFT_BY_16) + (checksum & NX_LOWER_16_MASK);
347 
348             /* Now store the checksum in the IP fragment header.  */
349             fragment_header_ptr -> nx_ip_header_word_2 =  fragment_header_ptr -> nx_ip_header_word_2 | (NX_LOWER_16_MASK & (~checksum));
350         }
351 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
352         else
353         {
354             fragment_packet -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_IPV4_TX_CHECKSUM;
355         }
356 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
357 
358         /* Endian swapping logic.  If NX_LITTLE_ENDIAN is specified, these macros will
359            swap the endian of the IP header.  */
360         NX_CHANGE_ULONG_ENDIAN(fragment_header_ptr -> nx_ip_header_word_0);
361         NX_CHANGE_ULONG_ENDIAN(fragment_header_ptr -> nx_ip_header_word_1);
362         NX_CHANGE_ULONG_ENDIAN(fragment_header_ptr -> nx_ip_header_word_2);
363         NX_CHANGE_ULONG_ENDIAN(fragment_header_ptr -> nx_ip_header_source_ip);
364         NX_CHANGE_ULONG_ENDIAN(fragment_header_ptr -> nx_ip_header_destination_ip);
365 
366 #ifndef NX_DISABLE_IP_INFO
367         /* Increment the IP fragments sent count.  */
368         ip_ptr -> nx_ip_total_fragments_sent++;
369 
370         /* Increment the IP packet sent count.  */
371         ip_ptr -> nx_ip_total_packets_sent++;
372 
373         /* Increment the IP bytes sent count.  */
374         ip_ptr -> nx_ip_total_bytes_sent += fragment_packet -> nx_packet_length - (ULONG)sizeof(NX_IPV4_HEADER);
375 #endif
376 
377         /* Send the packet to the associated driver for output.  */
378         driver_request.nx_ip_driver_packet =   fragment_packet;
379 
380         /* If trace is enabled, insert this event into the trace buffer.  */
381         NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_IO_DRIVER_PACKET_SEND, ip_ptr, fragment_packet, fragment_packet -> nx_packet_length, 0, NX_TRACE_INTERNAL_EVENTS, 0, 0);
382 
383         /* Add debug information. */
384         NX_PACKET_DEBUG(__FILE__, __LINE__, fragment_packet);
385 
386         (fragment_packet -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_link_driver_entry)(&driver_request);
387 
388         /* Increase offset. */
389         fragment_offset += fragment_size;
390     }
391 
392 #ifndef NX_DISABLE_IP_INFO
393 
394     /* Increment the total number of successful fragment requests.  */
395     ip_ptr -> nx_ip_successful_fragment_requests++;
396 #endif
397 
398     /* The original packet has been sent out in fragments... release it!  */
399     _nx_packet_transmit_release(driver_req_ptr -> nx_ip_driver_packet);
400 #else
401     NX_PARAMETER_NOT_USED(driver_req_ptr);
402 #endif /* NX_DISABLE_IPV4 */
403 }
404 #endif /* NX_DISABLE_FRAGMENTATION */
405 
406