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 #ifdef WIN32
12 #define     HAVE_REMOTE
13 #define     WPCAP
14 #endif
15 
16 #include    "pcap.h"
17 #include    "nx_api.h"
18 #include    "tx_thread.h"
19 #ifndef WIN32
20 #include    "pthread.h"
21 #endif
22 #ifdef      NX_ENABLE_PPPOE
23 #include    "nx_pppoe_server.h"
24 #endif
25 
26 #ifdef WIN32
27 #pragma   comment(lib, "wpcap.lib")
28 #pragma   comment(lib, "Packet.lib")
29 #pragma   comment(lib, "ws2_32.lib")
30 #endif
31 
32 /* Define zero-terminated string containing the source name to open. */
33 /* In windows, the SOURCE NAME looks like this "rpcap://\\Device\\NPF_{4C8Bxxxx-xxxx-xxxx-xxxx-xxxxxxxx8356}" */
34 /* In Linux, the SOURCE NAME looks like this "eth0" */
35 #ifndef NX_PCAP_SOURCE_NAME
36 #define NX_PCAP_SOURCE_NAME     "rpcap://\\Device\\NPF_{4C8Bxxxx-xxxx-xxxx-xxxx-xxxxxxxx8356}"
37 
38 #endif /* NX_LIBPCAP_SOURCE_NAME */
39 
40 /* Define the Link MTU. Note this is not the same as the IP MTU.  The Link MTU
41    includes the addition of the Physical Network header (usually Ethernet). This
42    should be larger than the IP instance MTU by the size of the physical header. */
43 #define NX_LINK_MTU         1514
44 #define NX_MAX_PACKET_SIZE  1536
45 
46 /* Define Ethernet address format.  This is prepended to the incoming IP
47    and ARP/RARP messages.  The frame beginning is 14 bytes, but for speed
48    purposes, we are going to assume there are 16 bytes free in front of the
49    prepend pointer and that the prepend pointer is 32-bit aligned.
50 
51    Byte Offset     Size            Meaning
52 
53    0           6           Destination Ethernet Address
54    6           6           Source Ethernet Address
55    12          2           Ethernet Frame Type, where:
56 
57    0x0800 -> IP Datagram
58    0x0806 -> ARP Request/Reply
59    0x0835 -> RARP request reply
60 
61    42          18          Padding on ARP and RARP messages only.  */
62 
63 #define NX_ETHERNET_IP              0x0800
64 #define NX_ETHERNET_ARP             0x0806
65 #define NX_ETHERNET_RARP            0x8035
66 #define NX_ETHERNET_IPV6            0x86DD
67 #define NX_ETHERNET_PPPOE_DISCOVERY 0x8863
68 #define NX_ETHERNET_PPPOE_SESSION   0x8864
69 #define NX_ETHERNET_SIZE            14
70 
71 /* For the pcap ethernet driver, physical addresses are allocated starting
72    at the preset value and then incremented before the next allocation.  */
73 
74 ULONG   nx_pcap_address_msw =  0x0011;
75 ULONG   nx_pcap_address_lsw =  0x22334457;
76 
77 static const CHAR  *nx_pcap_source_name = NX_PCAP_SOURCE_NAME;
78 
79 #ifdef WIN32
80 /* Define the Windows thread to call pcap_loop. */
81 static HANDLE       nx_pcap_receive_thread;
82 #else
83 /* Define the Linux thread to call pcap_loop. */
84 static pthread_t    nx_pcap_receive_thread;
85 #endif
86 static NX_IP        *nx_pcap_default_ip;
87 static pcap_t       *nx_pcap_fp;
88 
89 /* Define the buffer to store data that will be sent by pcap. */
90 static UCHAR        nx_pcap_send_buff[NX_MAX_PACKET_SIZE];
91 
92 
93 /* Define driver prototypes.  */
94 
95 UINT         _nx_pcap_initialize(NX_IP *ip_ptr);
96 UINT         _nx_pcap_send_packet(NX_PACKET * packet_ptr);
97 #ifdef WIN32
98 DWORD WINAPI _nx_pcap_receive_thread_entry(LPVOID  thread_input);
99 #else
100 void         *_nx_pcap_receive_thread_entry(void *arg);
101 #endif
102 VOID         _nx_lpcap_packet_receive_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
103 VOID         _nx_pcap_network_driver_output(NX_PACKET *packet_ptr);
104 VOID         _nx_pcap_network_driver(NX_IP_DRIVER *driver_req_ptr);
105 VOID         nx_pcap_cleanup();
106 
107 /* Define interface capability.  */
108 
109 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
110 #define NX_INTERFACE_CAPABILITY ( NX_INTERFACE_CAPABILITY_IPV4_RX_CHECKSUM | \
111                                   NX_INTERFACE_CAPABILITY_TCP_RX_CHECKSUM | \
112                                   NX_INTERFACE_CAPABILITY_UDP_RX_CHECKSUM | \
113                                   NX_INTERFACE_CAPABILITY_ICMPV4_RX_CHECKSUM | \
114                                   NX_INTERFACE_CAPABILITY_ICMPV6_RX_CHECKSUM  )
115 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
116 
nx_pcap_set_source_name(const CHAR * source_name)117 VOID nx_pcap_set_source_name(const CHAR *source_name)
118 {
119     nx_pcap_source_name = source_name;
120 }
121 
_nx_pcap_send_packet(NX_PACKET * packet_ptr)122 UINT _nx_pcap_send_packet(NX_PACKET * packet_ptr)
123 {
124 ULONG   size = 0;
125 
126     /* Make sure the data length is less than MTU. */
127     if(packet_ptr -> nx_packet_length > NX_MAX_PACKET_SIZE)
128         return NX_NOT_SUCCESSFUL;
129 
130     if(nx_packet_data_retrieve(packet_ptr, nx_pcap_send_buff, &size))
131         return NX_NOT_SUCCESSFUL;
132 
133     if(pcap_sendpacket(nx_pcap_fp, nx_pcap_send_buff, size) != 0)
134         return NX_NOT_SUCCESSFUL;
135 
136     return NX_SUCCESS;
137 }
138 
139 
nx_pcap_cleanup()140 void nx_pcap_cleanup()
141 {
142     pcap_close(nx_pcap_fp);
143 }
144 
145 
_nx_pcap_packet_receive_handler(u_char * param,const struct pcap_pkthdr * header,const u_char * pkt_data)146 VOID _nx_pcap_packet_receive_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
147 {
148 
149 NX_PACKET   *packet_ptr;
150 UINT        status;
151 UINT        packet_type;
152 
153 #ifndef NX_ENABLE_PCAP_LOCAL_RECEIVE
154     /* Check whether the packet is generated by local. */
155     if((*(pkt_data + 6)  == ((nx_pcap_address_msw >> 8) & 0xFF)) &&
156        (*(pkt_data + 7)  == (nx_pcap_address_msw & 0xFF)) &&
157        (*(pkt_data + 8)  == ((nx_pcap_address_lsw >> 24) & 0xFF)) &&
158        (*(pkt_data + 9)  == ((nx_pcap_address_lsw >> 16) & 0xFF)) &&
159        (*(pkt_data + 10) == ((nx_pcap_address_lsw >> 8) & 0xFF)) &&
160        (*(pkt_data + 11) == (nx_pcap_address_lsw & 0xFF)))
161     {
162         return;
163     }
164 #endif /* NX_PCAP_LOCAL_RECEIVE */
165 
166     _tx_thread_context_save();
167 
168     status = nx_packet_allocate(nx_pcap_default_ip -> nx_ip_default_packet_pool, &packet_ptr, NX_RECEIVE_PACKET, NX_NO_WAIT);
169 
170     if(status)
171     {
172         _tx_thread_context_restore();
173         return;
174     }
175 
176     /* Make sure IP header is 4-byte aligned. */
177     packet_ptr -> nx_packet_prepend_ptr += 2;
178     packet_ptr -> nx_packet_append_ptr += 2;
179 
180     status = nx_packet_data_append(packet_ptr, (VOID*)pkt_data, header -> len,
181                                    nx_pcap_default_ip -> nx_ip_default_packet_pool, NX_NO_WAIT);
182 
183     if(status)
184     {
185         nx_packet_release(packet_ptr);
186         _tx_thread_context_restore();
187         return;
188     }
189 
190     /* Pickup the packet header to determine where the packet needs to be sent.  */
191     packet_type =  (((UINT) (*(packet_ptr -> nx_packet_prepend_ptr+12))) << 8) |
192                      ((UINT) (*(packet_ptr -> nx_packet_prepend_ptr+13)));
193 
194     /* Route the incoming packet according to its ethernet type.  */
195     if((packet_type == NX_ETHERNET_IP) || (packet_type == NX_ETHERNET_IPV6))
196     {
197 
198         /* Note:  The length reported by some Ethernet hardware includes bytes after the packet
199            as well as the Ethernet header.  In some cases, the actual packet length after the
200            Ethernet header should be derived from the length in the IP header (lower 16 bits of
201            the first 32-bit word).  */
202 
203         /* Clean off the Ethernet header.  */
204         packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr + NX_ETHERNET_SIZE;
205 
206         /* Adjust the packet length.  */
207         packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length - NX_ETHERNET_SIZE;
208 
209 
210         _nx_ip_packet_deferred_receive(nx_pcap_default_ip, packet_ptr);
211     }
212     else if(packet_type == NX_ETHERNET_ARP)
213     {
214 
215         /* Clean off the Ethernet header.  */
216         packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr + NX_ETHERNET_SIZE;
217 
218         /* Adjust the packet length.  */
219         packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length - NX_ETHERNET_SIZE;
220 
221         _nx_arp_packet_deferred_receive(nx_pcap_default_ip, packet_ptr);
222 
223     }
224     else if(packet_type == NX_ETHERNET_RARP)
225     {
226 
227         /* Clean off the Ethernet header.  */
228         packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr + NX_ETHERNET_SIZE;
229 
230         /* Adjust the packet length.  */
231         packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length - NX_ETHERNET_SIZE;
232 
233         _nx_rarp_packet_deferred_receive(nx_pcap_default_ip, packet_ptr);
234     }
235 #ifdef NX_ENABLE_PPPOE
236     else if ((packet_type == NX_ETHERNET_PPPOE_DISCOVERY) ||
237              (packet_type == NX_ETHERNET_PPPOE_SESSION))
238     {
239 
240         /* Clean off the Ethernet header.  */
241         packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr + NX_ETHERNET_SIZE;
242 
243         /* Adjust the packet length.  */
244         packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length - NX_ETHERNET_SIZE;
245 
246         /* Route to the PPPoE receive function.  */
247         _nx_pppoe_packet_deferred_receive(packet_ptr);
248     }
249 #endif
250     else
251     {
252 
253         /* Invalid ethernet header... release the packet.  */
254         nx_packet_release(packet_ptr);
255     }
256     _tx_thread_context_restore();
257 }
258 
259 #ifdef WIN32
_nx_pcap_receive_thread_entry(LPVOID thread_input)260 DWORD WINAPI _nx_pcap_receive_thread_entry(LPVOID  thread_input)
261 {
262     /* Loop to capture packets. */
263     pcap_loop(nx_pcap_fp, 0, _nx_pcap_packet_receive_handler, NULL);
264     return 0;
265 }
266 #else
_nx_pcap_receive_thread_entry(void * arg)267 void *_nx_pcap_receive_thread_entry(void *arg)
268 {
269 
270     /* Loop to capture packets. */
271     pcap_loop(nx_pcap_fp, 0, _nx_pcap_packet_receive_handler, NULL);
272     return((void *)0);
273 }
274 #endif
275 
_nx_pcap_initialize(NX_IP * ip_ptr_in)276 UINT _nx_pcap_initialize(NX_IP *ip_ptr_in)
277 {
278 CHAR    errbuf[PCAP_ERRBUF_SIZE] = { 0 };
279 
280 #ifndef WIN32
281 struct sched_param sp;
282 
283     /* Define the thread's priority. */
284 #ifdef TX_LINUX_PRIORITY_ISR
285     sp.sched_priority = TX_LINUX_PRIORITY_ISR;
286 #else
287     sp.sched_priority = 2;
288 #endif
289 #endif
290 
291     /* Return if source has been opened. */
292     if(nx_pcap_fp)
293         return 1;
294 
295 #ifdef WIN32
296     if((nx_pcap_fp = pcap_open(nx_pcap_source_name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1, NULL, errbuf)) == NULL)
297     {
298         return NX_NOT_CREATED;
299     }
300 #else
301     if((nx_pcap_fp = pcap_create(nx_pcap_source_name, NULL)) == NULL)
302     {
303         return NX_NOT_CREATED;
304     }
305 
306     if (pcap_set_immediate_mode(nx_pcap_fp, 1) < 0)
307     {
308         nx_pcap_cleanup();
309         return NX_NOT_CREATED;
310     }
311 
312     if (pcap_set_promisc(nx_pcap_fp, 1) < 0)
313     {
314         nx_pcap_cleanup();
315         return NX_NOT_CREATED;
316     }
317 
318     if (pcap_set_snaplen(nx_pcap_fp, 65536) < 0)
319     {
320         nx_pcap_cleanup();
321         return NX_NOT_CREATED;
322     }
323 
324     if (pcap_activate(nx_pcap_fp) < 0)
325     {
326         nx_pcap_cleanup();
327         return NX_NOT_CREATED;
328     }
329 #endif
330 
331     nx_pcap_default_ip = ip_ptr_in;
332 
333 #ifdef WIN32
334     nx_pcap_receive_thread = CreateThread(NULL, 0, _nx_pcap_receive_thread_entry, (LPVOID)NULL, CREATE_SUSPENDED, NULL);
335     SetThreadPriority(nx_pcap_receive_thread, THREAD_PRIORITY_BELOW_NORMAL);
336     ResumeThread(nx_pcap_receive_thread);
337 #else
338 
339     /* Create a Linux thread to loop for capturing packets */
340     pthread_create(&nx_pcap_receive_thread, NULL, _nx_pcap_receive_thread_entry, NULL);
341 
342     /* Set the thread's policy and priority */
343     pthread_setschedparam(nx_pcap_receive_thread, SCHED_FIFO, &sp);
344 #endif
345 
346     return NX_SUCCESS;
347 }
348 
349 
_nx_pcap_network_driver_output(NX_PACKET * packet_ptr)350 VOID  _nx_pcap_network_driver_output(NX_PACKET *packet_ptr)
351 {
352 UINT        old_threshold = 0;
353 
354     /* Disable preemption.  */
355     tx_thread_preemption_change(tx_thread_identify(), 0, &old_threshold);
356 
357     _nx_pcap_send_packet(packet_ptr);
358 
359     /* Remove the Ethernet header.  In real hardware environments, this is typically
360        done after a transmit complete interrupt.  */
361     packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr + NX_ETHERNET_SIZE;
362 
363     /* Adjust the packet length.  */
364     packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length - NX_ETHERNET_SIZE;
365 
366     /* Now that the Ethernet frame has been removed, release the packet.  */
367     nx_packet_transmit_release(packet_ptr);
368 
369     /* Restore preemption.  */
370     tx_thread_preemption_change(tx_thread_identify(), old_threshold, &old_threshold);
371 }
372 
373 
_nx_pcap_network_driver(NX_IP_DRIVER * driver_req_ptr)374 VOID  _nx_pcap_network_driver(NX_IP_DRIVER *driver_req_ptr)
375 {
376 NX_IP           *ip_ptr;
377 NX_PACKET       *packet_ptr;
378 ULONG           *ethernet_frame_ptr;
379 NX_INTERFACE    *interface_ptr;
380 #ifdef __PRODUCT_NETXDUO__
381 UINT            interface_index;
382 #endif
383 
384     /* Setup the IP pointer from the driver request.  */
385     ip_ptr =  driver_req_ptr -> nx_ip_driver_ptr;
386 
387     /* Default to successful return.  */
388     driver_req_ptr -> nx_ip_driver_status =  NX_SUCCESS;
389 
390     /* Setup interface pointer.  */
391     interface_ptr = driver_req_ptr -> nx_ip_driver_interface;
392 
393 #ifdef __PRODUCT_NETXDUO__
394     /* Obtain the index number of the network interface. */
395     interface_index = interface_ptr -> nx_interface_index;
396 #endif
397 
398     /* Process according to the driver request type in the IP control
399        block.  */
400     switch (driver_req_ptr -> nx_ip_driver_command)
401     {
402 
403         case NX_LINK_INTERFACE_ATTACH:
404             {
405                 interface_ptr = (NX_INTERFACE*)(driver_req_ptr -> nx_ip_driver_interface);
406                 break;
407             }
408 
409         case NX_LINK_INITIALIZE:
410             {
411 
412                 /* Device driver shall initialize the Ethernet Controller here. */
413 
414                 /* Once the Ethernet controller is initialized, the driver needs to
415                    configure the NetX Interface Control block, as outlined below. */
416 
417 #ifdef __PRODUCT_NETXDUO__
418                 /* The nx_interface_ip_mtu_size should be the MTU for the IP payload.
419                    For regular Ethernet, the IP MTU is 1500. */
420                 nx_ip_interface_mtu_set(ip_ptr, interface_index, (NX_LINK_MTU - NX_ETHERNET_SIZE));
421 
422                 /* Set the physical address (MAC address) of this IP instance.  */
423                 /* For this pcap driver, the MAC address is constructed by
424                    incrementing a base lsw value, to simulate multiple nodes hanging on the
425                    ethernet.  */
426                 nx_ip_interface_physical_address_set(ip_ptr, interface_index,
427                         nx_pcap_address_msw,
428                         nx_pcap_address_lsw,
429                         NX_FALSE);
430 
431                 /* Indicate to the IP software that IP to physical mapping is required.  */
432                 nx_ip_interface_address_mapping_configure(ip_ptr, interface_index, NX_TRUE);
433 #else
434                 interface_ptr -> nx_interface_ip_mtu_size =  NX_LINK_MTU;
435                 interface_ptr -> nx_interface_physical_address_msw =  nx_pcap_address_msw;
436                 interface_ptr -> nx_interface_physical_address_lsw =  nx_pcap_address_lsw;
437                 interface_ptr -> nx_interface_address_mapping_needed =  NX_TRUE;
438 #endif
439 
440 
441                 _nx_pcap_initialize(ip_ptr);
442 
443 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
444                 nx_ip_interface_capability_set(ip_ptr, interface_index, NX_INTERFACE_CAPABILITY);
445 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
446                 break;
447             }
448 
449         case NX_LINK_ENABLE:
450             {
451 
452                 /* Process driver link enable.  An Ethernet driver shall enable the
453                    transmit and reception logic.  Once the IP stack issues the
454                    LINK_ENABLE command, the stack may start transmitting IP packets. */
455 
456 
457                 /* In the driver, just set the enabled flag.  */
458                 interface_ptr -> nx_interface_link_up =  NX_TRUE;
459 
460                 break;
461             }
462 
463         case NX_LINK_DISABLE:
464             {
465 
466                 /* Process driver link disable.  This command indicates the IP layer
467                    is not going to transmit any IP datagrams, nor does it expect any
468                    IP datagrams from the interface.  Therefore after processing this command,
469                    the device driver shall not send any incoming packets to the IP
470                    layer.  Optionally the device driver may turn off the interface. */
471 
472                 /* In the pcap driver, just clear the enabled flag.  */
473                 interface_ptr -> nx_interface_link_up =  NX_FALSE;
474 
475                 break;
476             }
477 
478         case NX_LINK_PACKET_SEND:
479         case NX_LINK_PACKET_BROADCAST:
480         case NX_LINK_ARP_SEND:
481         case NX_LINK_ARP_RESPONSE_SEND:
482         case NX_LINK_RARP_SEND:
483 #ifdef NX_ENABLE_PPPOE
484         case NX_LINK_PPPOE_DISCOVERY_SEND:
485         case NX_LINK_PPPOE_SESSION_SEND:
486 #endif
487             {
488 
489                 /*
490                    The IP stack sends down a data packet for transmission.
491                    The device driver needs to prepend a MAC header, and fill in the
492                    Ethernet frame type (assuming Ethernet protocol for network transmission)
493                    based on the type of packet being transmitted.
494 
495                    The following sequence illustrates this process.
496                    */
497 
498                 /* Place the ethernet frame at the front of the packet.  */
499                 packet_ptr =  driver_req_ptr -> nx_ip_driver_packet;
500 
501                 /* Adjust the prepend pointer.  */
502                 packet_ptr -> nx_packet_prepend_ptr =  packet_ptr -> nx_packet_prepend_ptr - NX_ETHERNET_SIZE;
503 
504                 /* Adjust the packet length.  */
505                 packet_ptr -> nx_packet_length =  packet_ptr -> nx_packet_length + NX_ETHERNET_SIZE;
506 
507                 /* Setup the ethernet frame pointer to build the ethernet frame.  Backup another 2
508                    bytes to get 32-bit word alignment.  */
509                 ethernet_frame_ptr =  (ULONG *) (packet_ptr -> nx_packet_prepend_ptr - 2);
510 
511                 /* Build the ethernet frame.  */
512                 *ethernet_frame_ptr     =  driver_req_ptr -> nx_ip_driver_physical_address_msw;
513                 *(ethernet_frame_ptr+1) =  driver_req_ptr -> nx_ip_driver_physical_address_lsw;
514                 *(ethernet_frame_ptr+2) =  (interface_ptr -> nx_interface_physical_address_msw << 16) |
515                     (interface_ptr -> nx_interface_physical_address_lsw >> 16);
516                 *(ethernet_frame_ptr+3) =  (interface_ptr -> nx_interface_physical_address_lsw << 16);
517 
518                 if(driver_req_ptr -> nx_ip_driver_command == NX_LINK_ARP_SEND)
519                     *(ethernet_frame_ptr+3) |= NX_ETHERNET_ARP;
520                 else if(driver_req_ptr -> nx_ip_driver_command == NX_LINK_ARP_RESPONSE_SEND)
521                     *(ethernet_frame_ptr+3) |= NX_ETHERNET_ARP;
522                 else if(driver_req_ptr -> nx_ip_driver_command == NX_LINK_RARP_SEND)
523                     *(ethernet_frame_ptr+3) |= NX_ETHERNET_RARP;
524 #ifdef NX_ENABLE_PPPOE
525                 else if(driver_req_ptr -> nx_ip_driver_command == NX_LINK_PPPOE_DISCOVERY_SEND)
526                 {
527                     *(ethernet_frame_ptr + 3) |= NX_ETHERNET_PPPOE_DISCOVERY;
528                 }
529                 else if(driver_req_ptr -> nx_ip_driver_command == NX_LINK_PPPOE_SESSION_SEND)
530                 {
531                     *(ethernet_frame_ptr + 3) |= NX_ETHERNET_PPPOE_SESSION;
532                 }
533 #endif
534 #ifdef __PRODUCT_NETXDUO__
535                 else if(packet_ptr -> nx_packet_ip_version == 4)
536                     *(ethernet_frame_ptr+3) |= NX_ETHERNET_IP;
537                 else
538                     *(ethernet_frame_ptr+3) |= NX_ETHERNET_IPV6;
539 #else
540                 else
541                     *(ethernet_frame_ptr+3) |= NX_ETHERNET_IP;
542 #endif
543 
544 
545 
546                 /* Endian swapping if NX_LITTLE_ENDIAN is defined.  */
547                 NX_CHANGE_ULONG_ENDIAN(*(ethernet_frame_ptr));
548                 NX_CHANGE_ULONG_ENDIAN(*(ethernet_frame_ptr+1));
549                 NX_CHANGE_ULONG_ENDIAN(*(ethernet_frame_ptr+2));
550                 NX_CHANGE_ULONG_ENDIAN(*(ethernet_frame_ptr+3));
551 
552                 /* At this point, the packet is a complete Ethernet frame, ready to be transmitted.
553                    The driver shall call the actual Ethernet transmit routine and put the packet
554                    on the wire.
555 
556                    In this example, the pcap network transmit routine is called. */
557                 _nx_pcap_network_driver_output(packet_ptr);
558                 break;
559             }
560 
561         case NX_LINK_MULTICAST_JOIN:
562             {
563 
564                 /* The IP layer issues this command to join a multicast group.  Note that
565                    multicast operation is required for IPv6.
566 
567                    On a typically Ethernet controller, the driver computes a hash value based
568                    on MAC address, and programs the hash table.
569 
570                    It is likely the driver also needs to maintain an internal MAC address table.
571                    Later if a multicast address is removed, the driver needs
572                    to reprogram the hash table based on the remaining multicast MAC addresses. */
573 
574                 break;
575             }
576 
577         case NX_LINK_MULTICAST_LEAVE:
578             {
579 
580                 /* The IP layer issues this command to remove a multicast MAC address from the
581                    receiving list.  A device driver shall properly remove the multicast address
582                    from the hash table, so the hardware does not receive such traffic.  Note that
583                    in order to reprogram the hash table, the device driver may have to keep track of
584                    current active multicast MAC addresses. */
585 
586                 /* The following procedure only applies to our pcap network driver, which manages
587                    multicast MAC addresses by a simple look up table. */
588 
589                 break;
590             }
591 
592         case NX_LINK_GET_STATUS:
593             {
594 
595                 /* Return the link status in the supplied return pointer.  */
596                 *(driver_req_ptr -> nx_ip_driver_return_ptr) =  ip_ptr-> nx_ip_interface[0].nx_interface_link_up;
597                 break;
598             }
599 
600         case NX_LINK_DEFERRED_PROCESSING:
601             {
602 
603                 /* Driver defined deferred processing. This is typically used to defer interrupt
604                    processing to the thread level.
605 
606                    A typical use case of this command is:
607                    On receiving an Ethernet frame, the RX ISR does not process the received frame,
608                    but instead records such an event in its internal data structure, and issues
609                    a notification to the IP stack (the driver sends the notification to the IP
610                    helping thread by calling "_nx_ip_driver_deferred_processing()".  When the IP stack
611                    gets a notification of a pending driver deferred process, it calls the
612                    driver with the NX_LINK_DEFERRED_PROCESSING command.  The driver shall complete
613                    the pending receive process.
614                    */
615 
616                 /* The pcap driver doesn't require a deferred process so it breaks out of
617                    the switch case. */
618 
619 
620                 break;
621             }
622 
623         default:
624             {
625 
626                 /* Invalid driver request.  */
627                 /* Return the unhandled command status.  */
628                 driver_req_ptr -> nx_ip_driver_status =  NX_UNHANDLED_COMMAND;
629             }
630 
631     }
632 }
633 
634