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