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 #ifndef NX_DISABLE_IPV4
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _nx_ip_driver_packet_send PORTABLE C */
37 /* 6.1 */
38 /* AUTHOR */
39 /* */
40 /* Yuxin Zhou, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function sends an IP packet to the appropriate link driver. */
45 /* */
46 /* INPUT */
47 /* */
48 /* ip_ptr Pointer to IP control block */
49 /* packet_ptr Pointer to packet to send */
50 /* destination_ip Destination IP address */
51 /* fragment Don't fragment bit */
52 /* next_hop_address Next Hop address */
53 /* */
54 /* OUTPUT */
55 /* */
56 /* None */
57 /* */
58 /* CALLS */
59 /* */
60 /* (_nx_arp_entry_allocate) ARP entry allocate service */
61 /* (_nx_arp_packet_send) Send an ARP packet */
62 /* _nx_ip_packet_deferred_receive Receive loopback packet */
63 /* _nx_packet_copy Copy packet to input packet */
64 /* _nx_packet_transmit_release Release transmit packet */
65 /* (nx_ip_fragment_processing) Fragment processing */
66 /* (ip_link_driver) User supplied link driver */
67 /* _nx_ip_packet_checksum_compute Compute checksum */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* NetX Source Code */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
78 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
79 /* resulting in version 6.1 */
80 /* */
81 /**************************************************************************/
_nx_ip_driver_packet_send(NX_IP * ip_ptr,NX_PACKET * packet_ptr,ULONG destination_ip,ULONG fragment,ULONG next_hop_address)82 VOID _nx_ip_driver_packet_send(NX_IP *ip_ptr, NX_PACKET *packet_ptr, ULONG destination_ip, ULONG fragment, ULONG next_hop_address)
83 {
84
85 TX_INTERRUPT_SAVE_AREA
86 NX_IP_DRIVER driver_request;
87 UINT index;
88 ULONG network_mask;
89 ULONG network;
90 UCHAR loopback = NX_FALSE;
91 NX_ARP *arp_ptr;
92 NX_PACKET *last_packet;
93 NX_PACKET *remove_packet;
94 NX_PACKET *packet_copy;
95 UINT queued_count;
96
97 /* Add debug information. */
98 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
99
100 /* Initialize the driver request. */
101 driver_request.nx_ip_driver_ptr = ip_ptr;
102 driver_request.nx_ip_driver_packet = packet_ptr;
103 driver_request.nx_ip_driver_interface = packet_ptr -> nx_packet_address.nx_packet_interface_ptr;
104 driver_request.nx_ip_driver_command = NX_LINK_PACKET_SEND;
105
106 /* Determine if physical mapping is needed by the link driver. */
107 if (packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_address_mapping_needed)
108 {
109
110 /* Get the network and network mask.*/
111 network_mask = packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_ip_network_mask;
112 network = packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_ip_network;
113
114 /* Determine if an IP limited or directed broadcast is requested. */
115 if ((destination_ip == NX_IP_LIMITED_BROADCAST) ||
116 (((destination_ip & network_mask) == network) &&
117 ((destination_ip & ~network_mask) == ~network_mask)))
118 {
119
120 /* Build the driver request. */
121 driver_request.nx_ip_driver_command = NX_LINK_PACKET_BROADCAST;
122 driver_request.nx_ip_driver_physical_address_msw = 0xFFFFUL;
123 driver_request.nx_ip_driver_physical_address_lsw = 0xFFFFFFFFUL;
124 }
125 /* Determine if we have a loopback address. */
126 else if (destination_ip == packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_ip_address)
127 {
128 loopback = NX_TRUE;
129 driver_request.nx_ip_driver_interface = NX_NULL;
130 }
131 /* Determine if we have a class D multicast address. */
132 else if ((destination_ip & NX_IP_CLASS_D_MASK) == NX_IP_CLASS_D_TYPE)
133 {
134
135 /* Yes, we have a class D multicast address. Derive the physical mapping from
136 the class D address. */
137
138 /* Determine if the group address has been joined in this IP instance. */
139 index = 0;
140 while (index < NX_MAX_MULTICAST_GROUPS)
141 {
142
143 /* Determine if the destination address matches the requested address. */
144 if (ip_ptr -> nx_ipv4_multicast_entry[index].nx_ipv4_multicast_join_list == destination_ip)
145 {
146
147 /* Yes, break the loop! */
148 break;
149 }
150
151 /* Increment the join list index. */
152 index++;
153 }
154
155 /* Determine if the group was joined by this IP instance. */
156 if (index < NX_MAX_MULTICAST_GROUPS)
157 {
158
159 /* Determine if the group has loopback enabled. */
160 if (ip_ptr -> nx_ipv4_multicast_entry[index].nx_ipv4_multicast_loopback_enable)
161 {
162 loopback = NX_TRUE;
163 }
164 }
165
166 /* Build the driver request. Derive the physical mapping from
167 the class D address. */
168 driver_request.nx_ip_driver_physical_address_msw = NX_IP_MULTICAST_UPPER;
169 driver_request.nx_ip_driver_physical_address_lsw = NX_IP_MULTICAST_LOWER | (destination_ip & NX_IP_MULTICAST_MASK);
170 }
171 else
172 {
173
174 NX_PARAMETER_NOT_USED(fragment);
175 /* Look into the ARP Routing Table to derive the physical address. */
176
177 /* If we get here, the packet destination is a unicast address. */
178 destination_ip = next_hop_address;
179
180 /* Calculate the hash index for the destination IP address. */
181 index = (UINT)((destination_ip + (destination_ip >> 8)) & NX_ARP_TABLE_MASK);
182
183 /* Determine if there is an entry for this IP address. */
184 arp_ptr = ip_ptr -> nx_ip_arp_table[index];
185
186 /* Loop to look for an ARP match. */
187 while (arp_ptr)
188 {
189
190 /* Determine if this arp entry matches the destination IP address. */
191 if (arp_ptr -> nx_arp_ip_address == destination_ip)
192 {
193
194 /* Yes, we found a match. Get out of the loop! */
195 break;
196 }
197
198 /* Move to the next active ARP entry. */
199 arp_ptr = arp_ptr -> nx_arp_active_next;
200
201 /* Determine if we are at the end of the ARP list. */
202 if (arp_ptr == ip_ptr -> nx_ip_arp_table[index])
203 {
204 /* Clear the ARP pointer. */
205 arp_ptr = NX_NULL;
206 break;
207 }
208 }
209
210 /* Determine if we actually found a matching and effective ARP entry. */
211 if ((arp_ptr) && (arp_ptr -> nx_arp_physical_address_msw | arp_ptr -> nx_arp_physical_address_lsw))
212 {
213
214 /* Disable interrupts temporarily. */
215 TX_DISABLE
216
217 /* Yes, we have a physical mapping. Copy the physical address into the driver
218 request structure. */
219 driver_request.nx_ip_driver_physical_address_msw = arp_ptr -> nx_arp_physical_address_msw;
220 driver_request.nx_ip_driver_physical_address_lsw = arp_ptr -> nx_arp_physical_address_lsw;
221
222 /* Move this ARP entry to the head of the list. */
223 ip_ptr -> nx_ip_arp_table[index] = arp_ptr;
224
225 /* Restore interrupts. */
226 TX_RESTORE
227 }
228 else
229 {
230
231 /* Determine if fragmentation is needed before queue the packet on the ARP waiting queue. */
232 if (packet_ptr -> nx_packet_length > packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_ip_mtu_size)
233 {
234
235 #ifndef NX_DISABLE_FRAGMENTATION
236 /* Check the DF bit flag. */
237 if ((ip_ptr -> nx_ip_fragment_processing == NX_NULL) || (fragment != NX_FRAGMENT_OKAY))
238 #endif
239 {
240
241 #ifndef NX_DISABLE_IP_INFO
242
243 /* Increment the IP send packets dropped count. */
244 ip_ptr -> nx_ip_send_packets_dropped++;
245 #endif
246 /* Just release the packet. */
247 _nx_packet_transmit_release(packet_ptr);
248
249 /* Return... nothing more can be done! */
250 return;
251 }
252 }
253
254 /* Determine if we actually found a matching ARP entry. */
255 if (arp_ptr)
256 {
257
258 /* Yes, we have an existing ARP mapping entry. */
259
260 /* Disable interrupts temporarily. */
261 TX_DISABLE
262
263 /* Ensure the current packet's queue next pointer to NULL. */
264 packet_ptr -> nx_packet_queue_next = NX_NULL;
265
266 /* Determine if the queue is empty. */
267 if (arp_ptr -> nx_arp_packets_waiting == NX_NULL)
268 {
269
270 /* Yes, we have an empty ARP packet queue. Simply place the
271 packet at the head of the list. */
272 arp_ptr -> nx_arp_packets_waiting = packet_ptr;
273
274 /* Add debug information. */
275 NX_PACKET_DEBUG(NX_PACKET_ARP_WAITING_QUEUE, __LINE__, packet_ptr);
276
277 /* Restore interrupts. */
278 TX_RESTORE
279 }
280 else
281 {
282
283 /* Determine how many packets are on the ARP entry's packet
284 queue and remember the last packet in the queue. We know
285 there is at least one on the queue and another that is
286 going to be queued. */
287 last_packet = arp_ptr -> nx_arp_packets_waiting;
288 queued_count = 1;
289 while (last_packet -> nx_packet_queue_next)
290 {
291
292 /* Increment the queued count. */
293 queued_count++;
294
295 /* Move to the next packet in the queue. */
296 last_packet = last_packet -> nx_packet_queue_next;
297 }
298
299 /* Add debug information. */
300 NX_PACKET_DEBUG(NX_PACKET_ARP_WAITING_QUEUE, __LINE__, packet_ptr);
301
302 /* Place the packet at the end of the list. */
303 last_packet -> nx_packet_queue_next = packet_ptr;
304
305 /* Default the remove packet pointer to NULL. */
306 remove_packet = NX_NULL;
307
308 /* Determine if the packets queued has exceeded the queue
309 depth. */
310 if (queued_count >= NX_ARP_MAX_QUEUE_DEPTH)
311 {
312
313 /* Save the packet pointer at the head of the list. */
314 remove_packet = arp_ptr -> nx_arp_packets_waiting;
315
316 /* Remove the packet from the ARP queue. */
317 arp_ptr -> nx_arp_packets_waiting = remove_packet -> nx_packet_queue_next;
318
319 /* Clear the remove packet queue next pointer. */
320 remove_packet -> nx_packet_queue_next = NX_NULL;
321
322 #ifndef NX_DISABLE_IP_INFO
323
324 /* Increment the IP transmit resource error count. */
325 ip_ptr -> nx_ip_transmit_resource_errors++;
326
327 /* Increment the IP send packets dropped count. */
328 ip_ptr -> nx_ip_send_packets_dropped++;
329 #endif
330 }
331
332 /* Restore interrupts. */
333 TX_RESTORE
334
335 /* Determine if there is a packet to remove. */
336 if (remove_packet)
337 {
338
339 /* Yes, the packet queue depth for this ARP entry was exceeded
340 so release the packet that was removed from the queue. */
341 _nx_packet_transmit_release(remove_packet);
342 }
343 }
344 }
345 else
346 {
347
348 /* No ARP entry was found. We need to allocate a new ARP entry, populate it, and
349 initiate an ARP request to get the specific physical mapping. */
350
351 /* Allocate a new ARP entry. */
352 if ((!ip_ptr -> nx_ip_arp_allocate) ||
353 ((ip_ptr -> nx_ip_arp_allocate)(ip_ptr, &(ip_ptr -> nx_ip_arp_table[index]), NX_FALSE)))
354 {
355
356 /* Error, release the protection and the packet. */
357
358 #ifndef NX_DISABLE_IP_INFO
359
360 /* Increment the IP transmit resource error count. */
361 ip_ptr -> nx_ip_transmit_resource_errors++;
362
363 /* Increment the IP send packets dropped count. */
364 ip_ptr -> nx_ip_send_packets_dropped++;
365 #endif
366
367 /* Release the packet. */
368 _nx_packet_transmit_release(packet_ptr);
369
370 /* Just return! */
371 return;
372 }
373
374 /* Otherwise, setup a pointer to the new ARP entry. */
375 arp_ptr = (ip_ptr -> nx_ip_arp_table[index]) -> nx_arp_active_previous;
376
377 /* Setup the IP address and clear the physical mapping. */
378 arp_ptr -> nx_arp_ip_address = destination_ip;
379 arp_ptr -> nx_arp_physical_address_msw = 0;
380 arp_ptr -> nx_arp_physical_address_lsw = 0;
381 arp_ptr -> nx_arp_entry_next_update = NX_ARP_UPDATE_RATE;
382 arp_ptr -> nx_arp_retries = 0;
383 arp_ptr -> nx_arp_ip_interface = packet_ptr -> nx_packet_address.nx_packet_interface_ptr;
384
385 /* Ensure the queue next pointer is NULL for the packet before it
386 is placed on the ARP waiting queue. */
387 packet_ptr -> nx_packet_queue_next = NX_NULL;
388
389 /* Add debug information. */
390 NX_PACKET_DEBUG(NX_PACKET_ARP_WAITING_QUEUE, __LINE__, packet_ptr);
391
392 /* Queue the packet for output. */
393 arp_ptr -> nx_arp_packets_waiting = packet_ptr;
394
395 /* Call ARP send to send an ARP request. */
396 (ip_ptr -> nx_ip_arp_packet_send)(ip_ptr, destination_ip, packet_ptr -> nx_packet_address.nx_packet_interface_ptr);
397 }
398
399 /* Just return! */
400 return;
401 }
402 }
403 }
404 else
405 {
406
407 /* This IP instance does not require any IP-to-physical mapping. */
408
409 /* Determine if we have a loopback address. */
410 if ((((destination_ip >= NX_IP_LOOPBACK_FIRST) &&
411 (destination_ip <= NX_IP_LOOPBACK_LAST))) ||
412 (destination_ip == packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_ip_address))
413 {
414
415 /* Yes, we have an internal loopback address. */
416 loopback = NX_TRUE;
417 driver_request.nx_ip_driver_interface = NX_NULL;
418 }
419 }
420
421 /* Check whether the packet should be loop back. */
422 if (loopback == NX_TRUE)
423 {
424
425 /* Copy the packet so it can be enqueued properly by the receive
426 processing. */
427 if (_nx_packet_copy(packet_ptr, &packet_copy, ip_ptr -> nx_ip_default_packet_pool, NX_NO_WAIT) == NX_SUCCESS)
428 {
429
430 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
431
432 /* Compute checksum for upper layer protocol. */
433 /*lint --e{644} suppress variable might not be initialized, since "packet_copy" was initialized as long as return value is NX_SUCCESS. */
434 if (packet_copy -> nx_packet_interface_capability_flag)
435 {
436 _nx_ip_packet_checksum_compute(packet_copy);
437 }
438 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
439
440 #ifndef NX_DISABLE_IP_INFO
441
442 /* Increment the IP packet sent count. */
443 ip_ptr -> nx_ip_total_packets_sent++;
444
445 /* Increment the IP bytes sent count. */
446 ip_ptr -> nx_ip_total_bytes_sent += packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV4_HEADER);
447 #endif
448
449 #ifdef NX_IPSEC_ENABLE
450 /* Clear the ipsec sa pointer. */
451 packet_copy -> nx_packet_ipsec_sa_ptr = NX_NULL;
452 #endif /* NX_IPSEC_ENABLE */
453
454 /* Add debug information. */
455 /*lint --e{644} suppress variable might not be initialized, since "packet_copy" was initialized as long as return value is NX_SUCCESS. */
456 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_copy);
457
458 /* Send the packet to this IP's receive processing like it came in from the
459 driver. */
460 _nx_ip_packet_deferred_receive(ip_ptr, packet_copy);
461 }
462 #ifndef NX_DISABLE_IP_INFO
463 else
464 {
465
466 /* Increment the IP send packets dropped count. */
467 ip_ptr -> nx_ip_send_packets_dropped++;
468
469 /* Increment the IP transmit resource error count. */
470 ip_ptr -> nx_ip_transmit_resource_errors++;
471 }
472 #endif
473 }
474
475 /* Check whether the packet should be sent through driver. */
476 if (driver_request.nx_ip_driver_interface)
477 {
478
479 /* Determine if fragmentation is needed. */
480 if (packet_ptr -> nx_packet_length > packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_ip_mtu_size)
481 {
482
483 #ifndef NX_DISABLE_FRAGMENTATION
484 /* Check the DF bit flag. */
485 if ((ip_ptr -> nx_ip_fragment_processing) && (fragment != NX_DONT_FRAGMENT))
486 {
487
488 /* Fragmentation is needed, call the IP fragment processing routine. */
489 (ip_ptr -> nx_ip_fragment_processing)(&driver_request);
490 }
491 else
492 #endif /* NX_DISABLE_FRAGMENTATION */
493 {
494
495 #ifndef NX_DISABLE_IP_INFO
496
497 /* Increment the IP send packets dropped count. */
498 ip_ptr -> nx_ip_send_packets_dropped++;
499 #endif
500 /* Just release the packet. */
501 _nx_packet_transmit_release(packet_ptr);
502 }
503
504 /* In either case, this packet send is complete, just return. */
505 return;
506 }
507
508 #ifndef NX_DISABLE_IP_INFO
509
510 /* Increment the IP packet sent count. */
511 ip_ptr -> nx_ip_total_packets_sent++;
512
513 /* Increment the IP bytes sent count. */
514 ip_ptr -> nx_ip_total_bytes_sent += packet_ptr -> nx_packet_length - (ULONG)sizeof(NX_IPV4_HEADER);
515 #endif
516
517 /* If trace is enabled, insert this event into the trace buffer. */
518 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_IO_DRIVER_PACKET_SEND, ip_ptr, packet_ptr, packet_ptr -> nx_packet_length, 0, NX_TRACE_INTERNAL_EVENTS, 0, 0);
519
520 /* Add debug information. */
521 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
522
523 /* Driver entry must not be NULL. */
524 NX_ASSERT(packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_link_driver_entry != NX_NULL);
525
526 /* Broadcast packet. */
527 (packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_link_driver_entry)(&driver_request);
528 }
529 else
530 {
531
532 /* Release the transmit packet. */
533 _nx_packet_transmit_release(packet_ptr);
534 }
535 }
536 #endif /* !NX_DISABLE_IPV4 */
537
538