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 /** NetX Component                                                        */
15 /**                                                                       */
16 /**   Internet Protocol version 6 (IPv6)                                  */
17 /**                                                                       */
18 /**************************************************************************/
19 /**************************************************************************/
20 #define NX_SOURCE_CODE
21 
22 
23 /* Include necessary system files.  */
24 
25 #include "tx_api.h"
26 #include "nx_api.h"
27 #include "nx_ip.h"
28 #include "nx_ipv6.h"
29 #include "nx_packet.h"
30 
31 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
32 #include "nx_icmpv6.h"
33 #endif
34 
35 #if defined(FEATURE_NX_IPV6) && !defined(NX_DISABLE_FRAGMENTATION)
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _nx_ipv6_fragment_process                           PORTABLE C      */
42 /*                                                           6.1          */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    Yuxin Zhou, Microsoft Corporation                                   */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function breaks the supplied packet into fragments and sends   */
50 /*    them out through the associated IP driver.  This function uses the  */
51 /*    already built IP header and driver request structure for each       */
52 /*    packet fragment.                                                    */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    driver_req_ptr                        Pointer to driver request     */
57 /*    mtu                                   Maximum transfer unit         */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    None                                                                */
62 /*                                                                        */
63 /*                                                                        */
64 /*  CALLS                                                                 */
65 /*                                                                        */
66 /*    _nx_ip_packet_checksum_compute        Compute checksum              */
67 /*    _nx_packet_allocate                   Allocate packet               */
68 /*    _nx_ipv6_packet_copy                  Copy packet                   */
69 /*    _nx_packet_release                    Release packet                */
70 /*                                                                        */
71 /*  CALLED BY                                                             */
72 /*                                                                        */
73 /*    _nx_icmpv6_send_queued_packets                                      */
74 /*    _nx_ipv6_packet_send                                                */
75 /*                                                                        */
76 /*  NOTE                                                                  */
77 /*                                                                        */
78 /*    None                                                                */
79 /*                                                                        */
80 /*  RELEASE HISTORY                                                       */
81 /*                                                                        */
82 /*    DATE              NAME                      DESCRIPTION             */
83 /*                                                                        */
84 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
85 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
86 /*                                            resulting in version 6.1    */
87 /*                                                                        */
88 /**************************************************************************/
_nx_ipv6_fragment_process(struct NX_IP_DRIVER_STRUCT * driver_req_ptr,UINT mtu)89 VOID _nx_ipv6_fragment_process(struct NX_IP_DRIVER_STRUCT *driver_req_ptr, UINT mtu)
90 {
91 
92 NX_PACKET                      *first_fragment, *source_packet, *previous_packet;
93 UCHAR                          *fragmentable_ptr, *last_header_location;
94 UINT                            packet_length, unfragmentable_size;
95 ULONG                           packet_id;
96 NX_IP_DRIVER                    driver_request;
97 NX_IP                          *ip_ptr;
98 UCHAR                           next_header;
99 UCHAR                           hdr_ext_len;
100 UINT                            fragment_offset = 0;
101 INT                             error = 0;
102 NX_IPV6_HEADER_FRAGMENT_OPTION *fragment_option;
103 INT                             last_fragment = 0;
104 NX_IPV6_HEADER                 *ipv6_header;
105 ULONG                           word_1;
106 ULONG                           val;
107 NX_PACKET_POOL                 *pool_ptr;
108 
109 
110     first_fragment = NX_NULL;
111 
112     /* Setup the local driver request packet that will be used for each
113        fragment.  There will be a unique packet pointer for each request, but
114        otherwise all the other fields will remain constant.  */
115     driver_request = *driver_req_ptr;
116 
117     /* Setup the IP pointer. */
118     ip_ptr = driver_req_ptr -> nx_ip_driver_ptr;
119 
120 #ifndef NX_DISABLE_IP_INFO
121 
122     /* Increment the total number of fragment requests.  */
123     ip_ptr -> nx_ip_total_fragment_requests++;
124 #endif
125 
126     /* Source_packet points a packet (or a chain of packets) that already has IP header
127        constructed.  The prepend pointer should point to the IPv6 header. */
128     packet_id = ip_ptr -> nx_ip_packet_id++;
129 
130     /* Byte swap packet_id */
131     NX_CHANGE_ULONG_ENDIAN(packet_id);
132 
133     /* Pickup the source packet pointer.  */
134     source_packet = driver_req_ptr -> nx_ip_driver_packet;
135     source_packet -> nx_packet_last = source_packet;
136     source_packet -> nx_packet_ip_header = source_packet -> nx_packet_prepend_ptr;
137     pool_ptr = source_packet -> nx_packet_pool_owner;
138 
139     /* Add debug information. */
140     NX_PACKET_DEBUG(__FILE__, __LINE__, source_packet);
141 
142 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
143 
144     /* Compute checksum for upper layer protocol. */
145     if (source_packet -> nx_packet_interface_capability_flag)
146     {
147         _nx_ip_packet_checksum_compute(source_packet);
148     }
149 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
150 
151     /* Find out the unfragmentable part. */
152     fragmentable_ptr = source_packet -> nx_packet_prepend_ptr + sizeof(NX_IPV6_HEADER);
153     last_header_location  = (source_packet -> nx_packet_prepend_ptr + 6);
154     next_header = *last_header_location;
155 
156 #ifdef NX_ENABLE_IPV6_PATH_MTU_DISCOVERY
157 
158     /* RFC 1981 Section 4 requires that we include a fragment header if we received a
159        packet from a host with an MTU less than the minimum required path MTU, regardless
160        if we decide to fragment the packet (which we aren't). */
161     if (mtu < NX_MINIMUM_IPV6_PATH_MTU)
162     {
163 
164         /* So reset the mtu to the minimum packet size. */
165         mtu = NX_MINIMUM_IPV6_PATH_MTU;
166     }
167 
168 #endif
169 
170     /* Fragment Option header appears after Hop-by-hop and routing headers.  So we need
171        to skip these headers if they are present. */
172     while ((next_header == NX_PROTOCOL_NEXT_HEADER_HOP_BY_HOP) ||
173            (next_header == NX_PROTOCOL_NEXT_HEADER_ROUTING))
174     {
175 
176         /* Move to the next header */
177         hdr_ext_len = *(fragmentable_ptr + 1);
178 
179         last_header_location = fragmentable_ptr;
180 
181         next_header = *fragmentable_ptr;
182 
183         /*lint -e{923} suppress cast between pointer and UINT.  */
184         fragmentable_ptr = NX_UCHAR_POINTER_ADD(fragmentable_ptr, ((hdr_ext_len + 1) << 3));
185     }
186 
187     /* If hdr_ext_len == 0, there are no optional headers in the unfragmentable region. */
188     *last_header_location = NX_PROTOCOL_NEXT_HEADER_FRAGMENT;
189 
190     /* Change the very last "next_header" to
191        compute the unfragmentable size which includes MAC header, IPv6 header,
192        and any unfragmentable header, but not the fragment header option. */
193     /*lint -e{923} suppress cast between pointer and UINT.  */
194     unfragmentable_size = (UINT)((ALIGN_TYPE)fragmentable_ptr - (ALIGN_TYPE)source_packet -> nx_packet_prepend_ptr);
195 
196     /* Compute the fragmentable size. */
197     packet_length = (UINT)(source_packet -> nx_packet_length - unfragmentable_size);
198 
199     /* Add the size of the fragment option header.
200        This number is going to be used for finding the starting position of
201        the fragmentable part. */
202     /* unfragmentable_size += sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);*/
203 
204     /* Now fragmentable_ptr points to the begining of fragmentable part of 'remaining_pkt' */
205     /* Also packet_prepend_ptr points to the data (fragmentable) area.  */
206 
207     /* The fragmentable pointer starts from the first packet.*/
208     while (packet_length)
209     {
210 
211     NX_PACKET *new_packet;
212     UINT       fragment_size;
213     UINT       remaining_bytes;
214     UINT       nx_packet_size;
215 
216         /*
217            Determine the fragment size. Take the MTU size, minus the unfragmentable
218            portion, and round down to multiple of 8-bytes, then add the unfragmentable
219            portion back.   This is the size of each fragment, excluding the last fragment.
220          */
221         fragment_size = (mtu - unfragmentable_size) & 0xFFF8;
222         fragment_size -= (ULONG)sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
223 
224         if (fragment_size >= packet_length)
225         {
226             fragment_size = packet_length;
227             last_fragment = 1;
228         }
229 
230         /* Compute the remaining packet length */
231         packet_length -= fragment_size;
232 
233         /* Figure out the number of bytes (fragmentable part + unfragmentable part)
234            of this frame. */
235         remaining_bytes = fragment_size + unfragmentable_size + (UINT)sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION) + NX_PHYSICAL_HEADER;
236 
237         /* find the size of each nx packet. */
238         nx_packet_size = (UINT)((pool_ptr -> nx_packet_pool_payload_size) & 0xFFFC);
239 
240         if (nx_packet_size > (mtu + NX_PHYSICAL_HEADER))
241         {
242             nx_packet_size = (mtu + NX_PHYSICAL_HEADER);
243         }
244 
245         /* Make sure we have enough packets for this fragment. */
246         do
247         {
248 
249             /* Allocate a packet from the default packet pool.  */
250             if (_nx_packet_allocate(pool_ptr, &new_packet,
251                                     0, TX_NO_WAIT))
252             {
253                 error = 1;
254                 break;
255             }
256 
257             /* Add debug information. */
258             NX_PACKET_DEBUG(__FILE__, __LINE__, new_packet);
259 
260             /*lint -e{644} suppress variable might not be initialized, since "new_packet" was initialized in _nx_packet_allocate. */
261             new_packet -> nx_packet_ip_version = NX_IP_VERSION_V6;
262 
263             if (first_fragment == NX_NULL)
264             {
265                 first_fragment = new_packet;
266                 first_fragment -> nx_packet_last = new_packet;
267 
268                 /* first_fragment -> nx_packet_length = fragment_size + unfragmentable_size. */
269                 /* May need to configure additional header information. */
270             }
271             else
272             {
273                 first_fragment -> nx_packet_last -> nx_packet_next = new_packet;
274                 first_fragment -> nx_packet_last = new_packet;
275             }
276             /* Establish the "usable" size of the packet.
277                The actual copy routine uses this information to figure out how many
278                bytes to transer to the fragmented packets.*/
279 
280             /* The true packet size is set in the first packet. */
281             if (nx_packet_size > remaining_bytes)
282             {
283                 /* This is going to be the last packet we need. */
284                 new_packet -> nx_packet_length = remaining_bytes;
285                 remaining_bytes = 0;
286             }
287             else
288             {
289                 new_packet -> nx_packet_length = nx_packet_size;
290                 remaining_bytes -= nx_packet_size;
291             }
292         } while (remaining_bytes);
293 
294         if (error)
295         {
296             break;
297         }
298 
299         /* We have all the packets ready.  Need to copy data. */
300 
301         /* First step:  copy the unfragmentable part. */
302         /* Save the state from last iteration. */
303         previous_packet = source_packet -> nx_packet_last;
304 
305         source_packet -> nx_packet_last = source_packet;
306         source_packet -> nx_packet_prepend_ptr = source_packet -> nx_packet_ip_header;
307 
308         first_fragment -> nx_packet_last = first_fragment;
309 
310         first_fragment -> nx_packet_prepend_ptr += NX_PHYSICAL_HEADER;
311         first_fragment -> nx_packet_append_ptr += NX_PHYSICAL_HEADER;
312 
313         /* For the first packet, the prepend pointer is already at the begining of the IP header. */
314         if (_nx_ipv6_packet_copy(source_packet, first_fragment, unfragmentable_size))
315         {
316             break;
317         }
318 
319         /* Fill in the fragment header area.  Be careful here: we assume the unfragmentable part does not
320            span over multiple packets. */
321         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
322         fragment_option = (NX_IPV6_HEADER_FRAGMENT_OPTION *)first_fragment -> nx_packet_last -> nx_packet_append_ptr;
323         first_fragment -> nx_packet_append_ptr += sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
324         first_fragment -> nx_packet_length += (ULONG)sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
325 
326         fragment_option -> nx_ipv6_header_fragment_option_reserved = 0;
327         fragment_option -> nx_ipv6_header_fragment_option_next_header = next_header;
328 
329         if (!last_fragment)
330         {
331             fragment_option -> nx_ipv6_header_fragment_option_offset_flag = (USHORT)(fragment_offset + 1);
332         }
333         else
334         {
335             fragment_option -> nx_ipv6_header_fragment_option_offset_flag = (USHORT)fragment_offset;
336         }
337 
338         /* Convert to network byte order. */
339         NX_CHANGE_USHORT_ENDIAN(fragment_option -> nx_ipv6_header_fragment_option_offset_flag);
340 
341         fragment_option -> nx_ipv6_header_fragment_option_packet_id = packet_id;
342 
343         /* Restore the nx_packet_last and the prepend pointer within the last packet. */
344         source_packet -> nx_packet_last = previous_packet;
345         source_packet -> nx_packet_last -> nx_packet_prepend_ptr = fragmentable_ptr;
346 
347         /* Copy the rest of the frame. */
348         if (_nx_ipv6_packet_copy(source_packet, first_fragment, fragment_size))
349         {
350             break;
351         }
352 
353         /*
354            Set up the IP frame length.  first_fragment -> nx_packet_prepend_ptr points to the
355            beginning of the IPv6 header.
356          */
357         /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
358         ipv6_header = (NX_IPV6_HEADER *)first_fragment -> nx_packet_prepend_ptr;
359 
360         /* Pick up the 2nd word in the IP header. */
361         val = ipv6_header -> nx_ip_header_word_1;
362 
363         /* Convert to host byte order. */
364         NX_CHANGE_ULONG_ENDIAN(val);
365 
366         val = val & 0x0000FFFF;
367 
368         word_1 = (ULONG)(((fragment_size + unfragmentable_size - sizeof(NX_IPV6_HEADER)) + sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION)) << 16);
369         word_1 = val | word_1;
370 
371         /* Convert to network byte order. */
372         NX_CHANGE_ULONG_ENDIAN(word_1);
373 
374         ipv6_header -> nx_ip_header_word_1 = word_1;
375 
376         fragmentable_ptr = source_packet -> nx_packet_last -> nx_packet_prepend_ptr;
377 
378         fragment_offset += fragment_size;
379 
380         /* This fragment is ready to be transmitted. */
381         /* Send the packet to the associated driver for output.  */
382         first_fragment -> nx_packet_length = unfragmentable_size + fragment_size;
383         first_fragment -> nx_packet_length += (ULONG)sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
384         driver_request.nx_ip_driver_packet =   first_fragment;
385 
386 #ifndef NX_DISABLE_IP_INFO
387 
388         /* Increment the IP fragments sent count.  */
389         ip_ptr -> nx_ip_total_fragments_sent++;
390 
391         /* Increment the IP packet sent count.  */
392         ip_ptr -> nx_ip_total_packets_sent++;
393 
394         /* Increment the IP bytes sent count.  */
395         ip_ptr -> nx_ip_total_bytes_sent += first_fragment -> nx_packet_length - (ULONG)sizeof(NX_IPV6_HEADER_FRAGMENT_OPTION);
396 #endif /* !NX_DISABLE_IP_INFO */
397 
398         /* Add debug information. */
399         NX_PACKET_DEBUG(__FILE__, __LINE__, source_packet);
400 
401         (source_packet -> nx_packet_address.nx_packet_ipv6_address_ptr -> nxd_ipv6_address_attached -> nx_interface_link_driver_entry)(&driver_request);
402 
403         first_fragment = NX_NULL;
404     }
405 
406     /* Release the original packet. */
407     _nx_packet_transmit_release(source_packet);
408 
409     /* In case the fragmentation process fails above, frist_fragment
410        still contains partial data. Free these fragments before
411        exiting this function. */
412     if (first_fragment)
413     {
414         _nx_packet_release(first_fragment);
415     }
416 
417     return;
418 }
419 
420 #endif /* FEATURE_NX_IPV6 && NX_DISABLE_FRAGMENTATION*/
421 
422