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