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