xref: /FreeRTOS-Plus-TCP-v3.1.0/source/portable/NetworkInterface/linux/NetworkInterface.c (revision 37bdfe577f3b728058de714e2e747d3c78803f26)
1 /*
2  * FreeRTOS+TCP V3.1.0
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 /* ========================= FreeRTOS includes ============================== */
29 #include "FreeRTOS.h"
30 #include "event_groups.h"
31 #include "task.h"
32 #include "semphr.h"
33 
34 /* ========================= FreeRTOS+TCP includes ========================== */
35 #include "FreeRTOS_IP.h"
36 #include "FreeRTOS_IP_Private.h"
37 #include "NetworkBufferManagement.h"
38 #include "FreeRTOS_Stream_Buffer.h"
39 
40 /* ======================== Standard Library includes ======================== */
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <stdint.h>
44 #include <arpa/inet.h>
45 #include <netdb.h>
46 #include <pthread.h>
47 #include <stdlib.h>
48 #include <ctype.h>
49 #include <signal.h>
50 #include <pcap.h>
51 
52 /* ========================== Local includes =================================*/
53 #include <utils/wait_for_event.h>
54 
55 /* ======================== Macro Definitions =============================== */
56 #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
57     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eProcessBuffer
58 #else
59     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) \
60     eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
61 #endif
62 
63 /* ============================== Definitions =============================== */
64 #define xSEND_BUFFER_SIZE    32768
65 #define xRECV_BUFFER_SIZE    32768
66 #define MAX_CAPTURE_LEN      65535
67 #define IP_SIZE              100
68 
69 /* ================== Static Function Prototypes ============================ */
70 static int prvConfigureCaptureBehaviour( void );
71 static int prvCreateThreadSafeBuffers( void );
72 static void * prvLinuxPcapSendThread( void * pvParam );
73 static void * prvLinuxPcapRecvThread( void * pvParam );
74 static void prvInterruptSimulatorTask( void * pvParameters );
75 static void prvPrintAvailableNetworkInterfaces( pcap_if_t * pxAllNetworkInterfaces );
76 static pcap_if_t * prvGetAvailableNetworkInterfaces( void );
77 static const char * prvRemoveSpaces( char * pcBuffer,
78                                      int aBuflen,
79                                      const char * pcMessage );
80 static int prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces );
81 static int prvCreateWorkerThreads( void );
82 static int prvSetDeviceModes( void );
83 static void print_hex( unsigned const char * const bin_data,
84                        size_t len );
85 
86 /* ======================== Static Global Variables ========================= */
87 static StreamBuffer_t * xSendBuffer = NULL;
88 static StreamBuffer_t * xRecvBuffer = NULL;
89 static char errbuf[ PCAP_ERRBUF_SIZE ];
90 static pcap_t * pxOpenedInterfaceHandle = NULL;
91 static struct event * pvSendEvent = NULL;
92 static uint32_t ulPCAPSendFailures = 0;
93 static BaseType_t xConfigNetworkInterfaceToUse = configNETWORK_INTERFACE_TO_USE;
94 static BaseType_t xInvalidInterfaceDetected = pdFALSE;
95 
96 /* ======================= API Function definitions ========================= */
97 
98 /*!
99  * @brief API call, called from reeRTOS_IP.c to initialize the capture device
100  *        to be able to send and receive packets
101  * @return pdPASS if successful else pdFAIL
102  */
xNetworkInterfaceInitialise(void)103 BaseType_t xNetworkInterfaceInitialise( void )
104 {
105     BaseType_t ret = pdFAIL;
106     pcap_if_t * pxAllNetworkInterfaces;
107 
108     /* Query the computer the simulation is being executed on to find the
109      * network interfaces it has installed. */
110     pxAllNetworkInterfaces = prvGetAvailableNetworkInterfaces();
111 
112     if( pxAllNetworkInterfaces != NULL )
113     {
114         prvPrintAvailableNetworkInterfaces( pxAllNetworkInterfaces );
115         ret = prvOpenSelectedNetworkInterface( pxAllNetworkInterfaces );
116 
117         if( ret == pdPASS )
118         {
119             ret = prvCreateThreadSafeBuffers();
120 
121             if( ret == pdPASS )
122             {
123                 ret = prvCreateWorkerThreads();
124             }
125         }
126 
127         /* The device list is no longer required. */
128         pcap_freealldevs( pxAllNetworkInterfaces );
129     }
130 
131     if( ( pxOpenedInterfaceHandle != NULL ) && ( ret == pdPASS ) )
132     {
133         ret = pdPASS;
134     }
135 
136     return ret;
137 }
138 
139 /*!
140  * @brief API call, called from reeRTOS_IP.c to send a network packet over the
141  *        selected interface
142  * @return pdTRUE if successful else pdFALSE
143  */
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t bReleaseAfterSend)144 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer,
145                                     BaseType_t bReleaseAfterSend )
146 {
147     size_t xSpace;
148 
149     iptraceNETWORK_INTERFACE_TRANSMIT();
150     configASSERT( xIsCallingFromIPTask() == pdTRUE );
151 
152     /* Both the length of the data being sent and the actual data being sent
153      *  are placed in the thread safe buffer used to pass data between the FreeRTOS
154      *  tasks and the pthread that sends data via the pcap library.  Drop
155      *  the packet if there is insufficient space in the buffer to hold both. */
156     xSpace = uxStreamBufferGetSpace( xSendBuffer );
157 
158     if( ( pxNetworkBuffer->xDataLength <=
159           ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
160         ( xSpace >= ( pxNetworkBuffer->xDataLength +
161                       sizeof( pxNetworkBuffer->xDataLength ) ) ) )
162     {
163         /* First write in the length of the data, then write in the data
164          * itself. */
165         uxStreamBufferAdd( xSendBuffer,
166                            0,
167                            ( const uint8_t * ) &( pxNetworkBuffer->xDataLength ),
168                            sizeof( pxNetworkBuffer->xDataLength ) );
169         uxStreamBufferAdd( xSendBuffer,
170                            0,
171                            ( const uint8_t * ) pxNetworkBuffer->pucEthernetBuffer,
172                            pxNetworkBuffer->xDataLength );
173     }
174     else
175     {
176         FreeRTOS_printf( ( "xNetworkInterfaceOutput: send buffers full to store %lu\n",
177                            pxNetworkBuffer->xDataLength ) );
178     }
179 
180     /* Kick the Tx task in either case in case it doesn't know the buffer is
181      * full. */
182     event_signal( pvSendEvent );
183 
184     /* The buffer has been sent so can be released. */
185     if( bReleaseAfterSend != pdFALSE )
186     {
187         vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
188     }
189 
190     return pdPASS;
191 }
192 
193 /* ====================== Static Function definitions ======================= */
194 
195 /*!
196  * @brief create thread safe buffers to send/receive packets between threads
197  * @returns
198  */
prvCreateThreadSafeBuffers(void)199 static int prvCreateThreadSafeBuffers( void )
200 {
201     int ret = pdFAIL;
202 
203     /* The buffer used to pass data to be transmitted from a FreeRTOS task to
204      * the linux thread that sends via the pcap library. */
205     do
206     {
207         if( xSendBuffer == NULL )
208         {
209             xSendBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) + xSEND_BUFFER_SIZE + 1 );
210 
211             if( xSendBuffer == NULL )
212             {
213                 break;
214             }
215 
216             configASSERT( xSendBuffer );
217             memset( xSendBuffer, '\0', sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) );
218             xSendBuffer->LENGTH = xSEND_BUFFER_SIZE + 1;
219         }
220 
221         /* The buffer used to pass received data from the pthread that receives
222          * via the pcap library to the FreeRTOS task. */
223         if( xRecvBuffer == NULL )
224         {
225             xRecvBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) + xRECV_BUFFER_SIZE + 1 );
226 
227             if( xRecvBuffer == NULL )
228             {
229                 break;
230             }
231 
232             configASSERT( xRecvBuffer );
233             memset( xRecvBuffer, '\0', sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) );
234             xRecvBuffer->LENGTH = xRECV_BUFFER_SIZE + 1;
235         }
236 
237         ret = pdPASS;
238     } while( 0 );
239 
240     return ret;
241 }
242 
243 /*!
244  * @brief  print network interfaces available on the system
245  * @param[in]   pxAllNetworkInterfaces interface structure list to print
246  */
prvPrintAvailableNetworkInterfaces(pcap_if_t * pxAllNetworkInterfaces)247 static void prvPrintAvailableNetworkInterfaces( pcap_if_t * pxAllNetworkInterfaces )
248 {
249     pcap_if_t * xInterface;
250     int32_t lInterfaceNumber = 1;
251     char cBuffer[ 512 ];
252 
253     if( pxAllNetworkInterfaces != NULL )
254     {
255         /* Print out the list of network interfaces.  The first in the list
256          * is interface '1', not interface '0'. */
257         for( xInterface = pxAllNetworkInterfaces;
258              xInterface != NULL; xInterface = xInterface->next )
259         {
260             /* The descriptions of the devices can be full of spaces, clean them
261              * a little.  printf() can only be used here because the network is not
262              * up yet - so no other network tasks will be running. */
263             printf( "Interface %d - %s\n",
264                     lInterfaceNumber,
265                     prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->name ) );
266             printf( "              (%s)\n",
267                     prvRemoveSpaces( cBuffer,
268                                      sizeof( cBuffer ),
269                                      xInterface->description ? xInterface->description :
270                                      "No description" ) );
271             printf( "\n" );
272             lInterfaceNumber++;
273         }
274     }
275 
276     if( lInterfaceNumber == 1 )
277     {
278         /* The interface number was never incremented, so the above for() loop
279          * did not execute meaning no interfaces were found. */
280         printf( " \nNo network interfaces were found.\n" );
281         pxAllNetworkInterfaces = NULL;
282     }
283 
284     printf( "\r\nThe interface that will be opened is set by " );
285     printf( "\"configNETWORK_INTERFACE_TO_USE\", which\r\nshould be defined in FreeRTOSConfig.h\r\n" );
286 
287     if( ( xConfigNetworkInterfaceToUse < 1L ) || ( xConfigNetworkInterfaceToUse >= lInterfaceNumber ) )
288     {
289         printf( "\r\nERROR:  configNETWORK_INTERFACE_TO_USE is set to %ld, which is an invalid value.\r\n", xConfigNetworkInterfaceToUse );
290         printf( "Please set configNETWORK_INTERFACE_TO_USE to one of the interface numbers listed above,\r\n" );
291         printf( "then re-compile and re-start the application.  Only Ethernet (as opposed to WiFi)\r\n" );
292         printf( "interfaces are supported.\r\n\r\nHALTING\r\n\r\n\r\n" );
293         xInvalidInterfaceDetected = pdTRUE;
294 
295         if( pxAllNetworkInterfaces != NULL )
296         {
297             /* Free the device list, as no devices are going to be opened. */
298             pcap_freealldevs( pxAllNetworkInterfaces );
299             pxAllNetworkInterfaces = NULL;
300         }
301     }
302     else
303     {
304         printf( "Attempting to open interface number %ld.\n", xConfigNetworkInterfaceToUse );
305     }
306 }
307 
308 /*!
309  * @brief  get network interfaces from the system
310  * @returns the structure list containing all found devices
311  */
prvGetAvailableNetworkInterfaces(void)312 static pcap_if_t * prvGetAvailableNetworkInterfaces( void )
313 {
314     pcap_if_t * pxAllNetworkInterfaces = NULL;
315 
316     if( xInvalidInterfaceDetected == pdFALSE )
317     {
318         int ret;
319         ret = pcap_findalldevs( &pxAllNetworkInterfaces, errbuf );
320 
321         if( ret == PCAP_ERROR )
322         {
323             FreeRTOS_printf( ( "Could not obtain a list of network interfaces\n%s\n",
324                                errbuf ) );
325             pxAllNetworkInterfaces = NULL;
326         }
327         else
328         {
329             printf( "\r\n\r\nThe following network interfaces are available:\r\n\r\n" );
330         }
331     }
332 
333     return pxAllNetworkInterfaces;
334 }
335 
336 /*!
337  * @brief  set device operation modes
338  * @returns pdPASS on success pdFAIL on failure
339  */
prvSetDeviceModes()340 static int prvSetDeviceModes()
341 {
342     int ret = pdFAIL;
343 
344     /*
345      * Open in promiscuous mode as the MAC and
346      * IP address is going to be "simulated", and
347      * not be the real MAC and IP address.  This allows
348      * traffic to the simulated IP address to be routed
349      * to uIP, and traffic to the real IP address to be
350      * routed to the Linux TCP/IP stack.
351      */
352     FreeRTOS_debug_printf( ( "setting device modes of operation...\n" ) );
353 
354     do
355     {
356         ret = pcap_set_promisc( pxOpenedInterfaceHandle, 1 );
357 
358         if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) )
359         {
360             FreeRTOS_printf( ( "coult not activate promisuous mode\n" ) );
361             break;
362         }
363 
364         ret = pcap_set_snaplen( pxOpenedInterfaceHandle,
365                                 ipTOTAL_ETHERNET_FRAME_SIZE );
366 
367         if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) )
368         {
369             FreeRTOS_printf( ( "coult not set snaplen\n" ) );
370             break;
371         }
372 
373         ret = pcap_set_timeout( pxOpenedInterfaceHandle, 200 );
374 
375         if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) )
376         {
377             FreeRTOS_printf( ( "coult not set timeout\n" ) );
378             break;
379         }
380 
381         ret = pcap_set_buffer_size( pxOpenedInterfaceHandle,
382                                     ipTOTAL_ETHERNET_FRAME_SIZE * 1100 );
383 
384         if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) )
385         {
386             FreeRTOS_printf( ( "coult not set buffer size\n" ) );
387             break;
388         }
389 
390         ret = pdPASS;
391     } while( 0 );
392 
393     return ret;
394 }
395 
396 /*!
397  * @brief  open selected interface given its name
398  * @param [in] pucName interface  name to pen
399  * @returns pdPASS on success pdFAIL on failure
400  */
prvOpenInterface(const char * pucName)401 static int prvOpenInterface( const char * pucName )
402 {
403     static char pucInterfaceName[ 256 ];
404     int ret = pdFAIL;
405 
406     if( pucName != NULL )
407     {
408         ( void ) strncpy( pucInterfaceName, pucName, sizeof( pucInterfaceName ) );
409         pucInterfaceName[ sizeof( pucInterfaceName ) - ( size_t ) 1 ] = '\0';
410 
411         FreeRTOS_debug_printf( ( "opening interface %s\n", pucInterfaceName ) );
412 
413         pxOpenedInterfaceHandle = pcap_create( pucInterfaceName, errbuf );
414 
415         if( pxOpenedInterfaceHandle != NULL )
416         {
417             ret = prvSetDeviceModes();
418 
419             if( ret == pdPASS )
420             {
421                 if( pcap_activate( pxOpenedInterfaceHandle ) == 0 )
422                 {
423                     /* Configure the capture filter to allow blocking reads, and to filter
424                      * out packets that are not of interest to this demo. */
425                     ret = prvConfigureCaptureBehaviour();
426                 }
427                 else
428                 {
429                     FreeRTOS_debug_printf( ( "pcap activate error %s\n",
430                                              pcap_geterr( pxOpenedInterfaceHandle ) ) );
431                     ret = pdFAIL;
432                 }
433             }
434         }
435         else
436         {
437             FreeRTOS_printf( ( "\n%s is not supported by pcap and cannot be opened %s\n",
438                                pucInterfaceName, errbuf ) );
439         }
440     }
441     else
442     {
443         FreeRTOS_printf( ( "could not open interface: name is null\n" ) );
444     }
445 
446     return ret;
447 }
448 
449 /*!
450  * @brief Open the network interface. The number of the interface to be opened is
451  *	       set by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h
452  *	       Calling this function will set the pxOpenedInterfaceHandle variable
453  *	       If, after calling this function, pxOpenedInterfaceHandle
454  *	       is equal to NULL, then the interface could not be opened.
455  * @param [in] pxAllNetworkInterfaces network interface list to choose from
456  * @returns pdPASS on success or pdFAIL when something goes wrong
457  */
prvOpenSelectedNetworkInterface(pcap_if_t * pxAllNetworkInterfaces)458 static int prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces )
459 {
460     pcap_if_t * pxInterface;
461     int32_t x;
462     int ret = pdFAIL;
463 
464     /* Walk the list of devices until the selected device is located. */
465     pxInterface = pxAllNetworkInterfaces;
466 
467     for( x = 0L; x < ( xConfigNetworkInterfaceToUse - 1L ); x++ )
468     {
469         pxInterface = pxInterface->next;
470     }
471 
472     /* Open the selected interface. */
473     if( prvOpenInterface( pxInterface->name ) == pdPASS )
474     {
475         FreeRTOS_debug_printf( ( "Successfully opened interface number %d.\n", x + 1 ) );
476         ret = pdPASS;
477     }
478     else
479     {
480         FreeRTOS_printf( ( "Failed to open interface number %d.\n", x + 1 ) );
481     }
482 
483     return ret;
484 }
485 
486 /*!
487  * @brief launch 2 linux threads, one for Tx and one for Rx
488  *        and one FreeRTOS thread that will simulate an interrupt
489  *        and notify the tcp/ip stack of new data
490  * @return pdPASS on success otherwise pdFAIL
491  */
prvCreateWorkerThreads(void)492 static int prvCreateWorkerThreads( void )
493 {
494     pthread_t vPcapRecvThreadHandle;
495     pthread_t vPcapSendThreadHandle;
496     int ret = pdPASS;
497 
498     if( pvSendEvent == NULL )
499     {
500         FreeRTOS_debug_printf( ( "Creating Threads ..\n" ) );
501         ret = pdFAIL;
502         /* Create event used to signal the  pcap Tx thread. */
503         pvSendEvent = event_create();
504 
505         do
506         {
507             /* Create the thread that handles pcap  Rx. */
508             ret = pthread_create( &vPcapRecvThreadHandle,
509                                   NULL,
510                                   prvLinuxPcapRecvThread,
511                                   NULL );
512 
513             if( ret != 0 )
514             {
515                 FreeRTOS_printf( ( "pthread error %d", ret ) );
516                 break;
517             }
518 
519             /* Create the thread that handles pcap  Tx. */
520             ret = pthread_create( &vPcapSendThreadHandle,
521                                   NULL,
522                                   prvLinuxPcapSendThread,
523                                   NULL );
524 
525             if( ret != 0 )
526             {
527                 FreeRTOS_printf( ( "pthread error %d", ret ) );
528                 break;
529             }
530 
531             ret = pdPASS;
532         } while( 0 );
533 
534         /* Create a task that simulates an interrupt in a real system.  This will
535          * block waiting for packets, then send a message to the IP task when data
536          * is available. */
537         if( xTaskCreate( prvInterruptSimulatorTask,
538                          "MAC_ISR",
539                          configMINIMAL_STACK_SIZE,
540                          NULL,
541                          configMAC_ISR_SIMULATOR_PRIORITY,
542                          NULL ) != pdPASS )
543         {
544             ret = pdFAIL;
545             FreeRTOS_printf( ( "xTaskCreate could not create a new task\n" ) );
546         }
547     }
548 
549     return ret;
550 }
551 
552 /*!
553  * @brief Create the buffers used to pass packets between the FreeRTOS simulator
554  *        and the pthreads that are handling pcap as well as the FreeRTOS task
555  *        responsible of simulating an interrupt.
556  * @returns pdPASS when successful and pdFAIL when there is a failure
557  */
prvConfigureCaptureBehaviour(void)558 static int prvConfigureCaptureBehaviour( void )
559 {
560     struct bpf_program xFilterCode;
561     uint32_t ulNetMask;
562     char pcap_filter[ 500 ];
563     int ret = pdFAIL;
564 
565     FreeRTOS_debug_printf( ( "Configuring Capture behaviour\n" ) );
566 
567     /* Set up a filter so only the packets of interest are passed to the IP
568      * stack.  errbuf is used for convenience to create the string.  Don't
569      * confuse this with an error message. */
570     sprintf( pcap_filter, "broadcast or multicast or ether host %x:%x:%x:%x:%x:%x",
571              ipLOCAL_MAC_ADDRESS[ 0 ],
572              ipLOCAL_MAC_ADDRESS[ 1 ],
573              ipLOCAL_MAC_ADDRESS[ 2 ],
574              ipLOCAL_MAC_ADDRESS[ 3 ],
575              ipLOCAL_MAC_ADDRESS[ 4 ],
576              ipLOCAL_MAC_ADDRESS[ 5 ] );
577     FreeRTOS_debug_printf( ( "pcap filter to compile: %s\n", pcap_filter ) );
578 
579     ulNetMask = ( configNET_MASK3 << 24UL ) | ( configNET_MASK2 << 16UL ) | ( configNET_MASK1 << 8L ) | configNET_MASK0;
580 
581     ret = pcap_compile( pxOpenedInterfaceHandle,
582                         &xFilterCode,
583                         pcap_filter,
584                         1,
585                         ulNetMask );
586 
587     if( ret < 0 )
588     {
589         ( void ) printf( "\nThe packet filter string is invalid %s\n",
590                          pcap_geterr( pxOpenedInterfaceHandle ) );
591     }
592     else
593     {
594         ret = pcap_setfilter( pxOpenedInterfaceHandle, &xFilterCode );
595 
596         if( ret < 0 )
597         {
598             ( void ) printf( "\nAn error occurred setting the packet filter. %s\n",
599                              pcap_geterr( pxOpenedInterfaceHandle ) );
600         }
601         else
602         {
603             ret = pdPASS;
604         }
605 
606         /* When pcap_compile() succeeds, it allocates memory for the memory pointed to by the bpf_program struct
607          * parameter.pcap_freecode() will free that memory. */
608         pcap_freecode( &xFilterCode );
609     }
610 
611     return ret;
612 }
613 
614 /*!
615  * @brief  callback function called from pcap_dispatch function when new
616  *         data arrives on the interface
617  * @param [in] user data sent to pcap_dispatch
618  * @param [in] pkt_header received packet header
619  * @param [in] pkt_data received packet data
620  * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls
621  */
pcap_callback(unsigned char * user,const struct pcap_pkthdr * pkt_header,const u_char * pkt_data)622 static void pcap_callback( unsigned char * user,
623                            const struct pcap_pkthdr * pkt_header,
624                            const u_char * pkt_data )
625 {
626     FreeRTOS_debug_printf( ( "Receiving < ===========  network callback user: %s len: %d caplen: %d\n",
627                              user,
628                              pkt_header->len,
629                              pkt_header->caplen ) );
630     print_hex( pkt_data, pkt_header->len );
631 
632     /* Pass data to the FreeRTOS simulator on a thread safe circular buffer. */
633     if( ( pkt_header->caplen <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
634         ( uxStreamBufferGetSpace( xRecvBuffer ) >= ( ( ( size_t ) pkt_header->caplen ) + sizeof( *pkt_header ) ) ) )
635     {
636         uxStreamBufferAdd( xRecvBuffer, 0, ( const uint8_t * ) pkt_header, sizeof( *pkt_header ) );
637         uxStreamBufferAdd( xRecvBuffer, 0, ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen );
638     }
639 }
640 
641 /*!
642  * @brief infinite loop pthread to read from pcap
643  * @param [in] pvParam not used
644  * @returns NULL
645  * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls
646  * @remarks This function disables signal, to prevent it from being put into
647  *          sleep byt the posix port
648  */
prvLinuxPcapRecvThread(void * pvParam)649 static void * prvLinuxPcapRecvThread( void * pvParam )
650 {
651     int ret;
652 
653     ( void ) pvParam;
654 
655     /* Disable signals to this thread since this is a Linux pthread to be able to
656      * printf and other blocking operations without being interrupted and put in
657      * suspension mode by the linux port signals
658      */
659     sigset_t set;
660 
661     sigfillset( &set );
662     pthread_sigmask( SIG_SETMASK, &set, NULL );
663 
664     for( ; ; )
665     {
666         ret = pcap_dispatch( pxOpenedInterfaceHandle, 1,
667                              pcap_callback, ( u_char * ) "mydata" );
668 
669         if( ret == -1 )
670         {
671             FreeRTOS_printf( ( "pcap_dispatch error received: %s\n",
672                                pcap_geterr( pxOpenedInterfaceHandle ) ) );
673         }
674     }
675 
676     return NULL;
677 }
678 
679 /*!
680  * @brief Infinite loop thread that waits for events when there is data
681  *        available then sends the data on the interface
682  * @param [in] pvParam not used
683  * @returns NULL
684  * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls
685  */
prvLinuxPcapSendThread(void * pvParam)686 static void * prvLinuxPcapSendThread( void * pvParam )
687 {
688     size_t xLength;
689     uint8_t ucBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
690     const time_t xMaxMSToWait = 1000;
691 
692     ( void ) pvParam;
693 
694     /* disable signals to avoid treating this thread as a FreeRTOS task and putting
695      * it to sleep by the scheduler */
696     sigset_t set;
697 
698     sigfillset( &set );
699     pthread_sigmask( SIG_SETMASK, &set, NULL );
700 
701     for( ; ; )
702     {
703         /* Wait until notified of something to send. */
704         event_wait_timed( pvSendEvent, xMaxMSToWait );
705 
706         /* Is there more than the length value stored in the circular buffer
707         * used to pass data from the FreeRTOS simulator into this pthread?*/
708         while( uxStreamBufferGetSize( xSendBuffer ) > sizeof( xLength ) )
709         {
710             uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) &xLength, sizeof( xLength ), pdFALSE );
711             uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) ucBuffer, xLength, pdFALSE );
712             FreeRTOS_debug_printf( ( "Sending  ========== > data pcap_sendpadcket %lu\n", xLength ) );
713             print_hex( ucBuffer, xLength );
714 
715             if( pcap_sendpacket( pxOpenedInterfaceHandle, ucBuffer, xLength ) != 0 )
716             {
717                 FreeRTOS_printf( ( "pcap_sendpackeet: send failed %d\n", ulPCAPSendFailures ) );
718                 ulPCAPSendFailures++;
719             }
720         }
721     }
722 
723     return NULL;
724 }
725 
726 /*!
727  * @brief FreeRTOS infinite loop thread that simulates a network interrupt to notify the
728  *         network stack of the presence of new data
729  * @param [in] pvParameters not used
730  */
prvInterruptSimulatorTask(void * pvParameters)731 static void prvInterruptSimulatorTask( void * pvParameters )
732 {
733     struct pcap_pkthdr xHeader;
734     static struct pcap_pkthdr * pxHeader;
735     const uint8_t * pucPacketData;
736     uint8_t ucRecvBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
737     NetworkBufferDescriptor_t * pxNetworkBuffer;
738     IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
739     eFrameProcessingResult_t eResult;
740 
741     /* Remove compiler warnings about unused parameters. */
742     ( void ) pvParameters;
743 
744     for( ; ; )
745     {
746         /* Does the circular buffer used to pass data from the pthread thread that
747          * handles pacap Rx into the FreeRTOS simulator contain another packet? */
748         if( uxStreamBufferGetSize( xRecvBuffer ) > sizeof( xHeader ) )
749         {
750             /* Get the next packet. */
751             uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) &xHeader, sizeof( xHeader ), pdFALSE );
752             uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) ucRecvBuffer, ( size_t ) xHeader.len, pdFALSE );
753             pucPacketData = ucRecvBuffer;
754             pxHeader = &xHeader;
755 
756             iptraceNETWORK_INTERFACE_RECEIVE();
757 
758             /* Check for minimal size. */
759             if( pxHeader->len >= sizeof( EthernetHeader_t ) )
760             {
761                 eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData );
762             }
763             else
764             {
765                 eResult = eReleaseBuffer;
766             }
767 
768             if( eResult == eProcessBuffer )
769             {
770                 /* Will the data fit into the frame buffer? */
771                 if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE )
772                 {
773                     /* Obtain a buffer into which the data can be placed.  This
774                      * is only	an interrupt simulator, not a real interrupt, so it
775                      * is ok to call the task level function here, but note that
776                      * some buffer implementations cannot be called from a real
777                      * interrupt. */
778                     pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( pxHeader->len, 0 );
779 
780                     if( pxNetworkBuffer != NULL )
781                     {
782                         memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len );
783                         pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len;
784 
785                         #if ( niDISRUPT_PACKETS == 1 )
786                             {
787                                 pxNetworkBuffer = vRxFaultInjection( pxNetworkBuffer, pucPacketData );
788                             }
789                         #endif /* niDISRUPT_PACKETS */
790 
791                         if( pxNetworkBuffer != NULL )
792                         {
793                             xRxEvent.pvData = ( void * ) pxNetworkBuffer;
794 
795                             /* Data was received and stored.  Send a message to
796                              * the IP task to let it know. */
797                             if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL )
798                             {
799                                 /* The buffer could not be sent to the stack so
800                                  * must be released again.  This is only an
801                                  * interrupt simulator, not a real interrupt, so it
802                                  * is ok to use the task level function here, but
803                                  * note no all buffer implementations will allow
804                                  * this function to be executed from a real
805                                  * interrupt. */
806                                 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
807                                 iptraceETHERNET_RX_EVENT_LOST();
808                             }
809                         }
810                         else
811                         {
812                             /* The packet was already released or stored inside
813                              * vRxFaultInjection().  Don't release it here. */
814                         }
815                     }
816                     else
817                     {
818                         iptraceETHERNET_RX_EVENT_LOST();
819                     }
820                 }
821                 else
822                 {
823                     /* Log that a packet was dropped because it would have
824                      * overflowed the buffer, but there may be more buffers to
825                      * process. */
826                 }
827             }
828         }
829         else
830         {
831             /* There is no real way of simulating an interrupt.  Make sure
832              * other tasks can run. */
833             vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY );
834         }
835     }
836 }
837 
838 /*!
839  * @brief remove spaces from pcMessage into pcBuffer
840  * @param [out] pcBuffer buffer to fill up
841  * @param [in] aBuflen length of pcBuffer
842  * @param [in] pcMessage original message
843  * @returns
844  */
prvRemoveSpaces(char * pcBuffer,int aBuflen,const char * pcMessage)845 static const char * prvRemoveSpaces( char * pcBuffer,
846                                      int aBuflen,
847                                      const char * pcMessage )
848 {
849     char * pcTarget = pcBuffer;
850 
851     /* Utility function used to format messages being printed only. */
852     while( ( *pcMessage != 0 ) && ( pcTarget < ( &pcBuffer[ aBuflen - 1 ] ) ) )
853     {
854         *( pcTarget++ ) = *pcMessage;
855 
856         if( isspace( *pcMessage ) != pdFALSE )
857         {
858             while( isspace( *pcMessage ) != pdFALSE )
859             {
860                 pcMessage++;
861             }
862         }
863         else
864         {
865             pcMessage++;
866         }
867     }
868 
869     *pcTarget = '\0';
870 
871     return pcBuffer;
872 }
873 
874 /*!
875  * @brief print binary packet in hex
876  * @param [in] bin_daa data to print
877  * @param [in] len length of the data
878  */
print_hex(unsigned const char * const bin_data,size_t len)879 static void print_hex( unsigned const char * const bin_data,
880                        size_t len )
881 {
882     size_t i;
883 
884     for( i = 0; i < len; ++i )
885     {
886         FreeRTOS_debug_printf( ( "%.2X ", bin_data[ i ] ) );
887     }
888 
889     FreeRTOS_debug_printf( ( "\n" ) );
890 }
891 
892 
893 #define BUFFER_SIZE               ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
894 #define BUFFER_SIZE_ROUNDED_UP    ( ( BUFFER_SIZE + 7 ) & ~0x07UL )
895 
896 /*!
897  * @brief Allocate RAM for packet buffers and set the pucEthernetBuffer field for each descriptor.
898  *        Called when the BufferAllocation1 scheme is used.
899  * @param [in,out] pxNetworkBuffers Pointer to an array of NetworkBufferDescriptor_t to populate.
900  */
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])901 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
902 {
903     static uint8_t * pucNetworkPacketBuffers = NULL;
904     size_t uxIndex;
905 
906     if( pucNetworkPacketBuffers == NULL )
907     {
908         pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP );
909     }
910 
911     if( pucNetworkPacketBuffers == NULL )
912     {
913         FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) );
914         configASSERT( 0 );
915     }
916     else
917     {
918         for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ )
919         {
920             size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP;
921             NetworkBufferDescriptor_t ** ppDescriptor;
922 
923             /* At the beginning of each pbuff is a pointer to the relevant descriptor */
924             ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] );
925 
926             /* Set this pointer to the address of the correct descriptor */
927             *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] );
928 
929             /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
930              * beginning of the allocated buffer. */
931             pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] );
932         }
933     }
934 }
935