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