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