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