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