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