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