xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/linux/NetworkInterface.c (revision b23fa86ac476770d3224c07213bec32f5b1628bd)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
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 /* ======================== Standard Library includes ======================== */
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <stdint.h>
38 #include <arpa/inet.h>
39 #include <netdb.h>
40 #include <pthread.h>
41 #include <stdlib.h>
42 #include <ctype.h>
43 #include <signal.h>
44 #include <pcap.h>
45 
46 /* ========================= FreeRTOS+TCP includes ========================== */
47 #include "FreeRTOS_IP.h"
48 #include "FreeRTOS_IP_Private.h"
49 #include "NetworkBufferManagement.h"
50 #include "FreeRTOS_Stream_Buffer.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 static size_t prvStreamBufferAdd( StreamBuffer_t * pxBuffer,
99                                   const uint8_t * pucData,
100                                   size_t uxByteCount );
101 
102 /*
103  * This function will return pdTRUE if the packet is targeted at
104  * the MAC address of this device, in other words when is was bounced-
105  * back by the WinPCap interface.
106  */
107 static BaseType_t xPacketBouncedBack( const uint8_t * pucBuffer );
108 
109 /*-----------------------------------------------------------*/
110 
111 /*
112  * A pointer to the network interface is needed later when receiving packets.
113  */
114 static NetworkInterface_t * pxMyInterface;
115 
116 static BaseType_t xNetworkInterfaceInitialise( NetworkInterface_t * pxInterface );
117 static BaseType_t xNetworkInterfaceOutput( NetworkInterface_t * pxInterface,
118                                            NetworkBufferDescriptor_t * const pxNetworkBuffer,
119                                            BaseType_t bReleaseAfterSend );
120 
121 NetworkInterface_t * pxLinux_FillInterfaceDescriptor( BaseType_t xEMACIndex,
122                                                       NetworkInterface_t * pxInterface );
123 
124 /*-----------------------------------------------------------*/
125 
126 /*!
127  * @brief API call, called from reeRTOS_IP.c to initialize the capture device
128  *        to be able to send and receive packets
129  * @return pdPASS if successful else pdFAIL
130  */
xNetworkInterfaceInitialise(NetworkInterface_t * pxInterface)131 static BaseType_t xNetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
132 {
133     BaseType_t ret = pdFAIL;
134     pcap_if_t * pxAllNetworkInterfaces;
135 
136     ( void ) pxInterface;
137 
138     /* Query the computer the simulation is being executed on to find the
139      * network interfaces it has installed. */
140     pxAllNetworkInterfaces = prvGetAvailableNetworkInterfaces();
141 
142     if( pxAllNetworkInterfaces != NULL )
143     {
144         prvPrintAvailableNetworkInterfaces( pxAllNetworkInterfaces );
145         ret = prvOpenSelectedNetworkInterface( pxAllNetworkInterfaces );
146 
147         if( ret == pdPASS )
148         {
149             ret = prvCreateThreadSafeBuffers();
150 
151             if( ret == pdPASS )
152             {
153                 ret = prvCreateWorkerThreads();
154             }
155         }
156 
157         /* The device list is no longer required. */
158         pcap_freealldevs( pxAllNetworkInterfaces );
159     }
160 
161     if( ( pxOpenedInterfaceHandle != NULL ) && ( ret == pdPASS ) )
162     {
163         ret = pdPASS;
164     }
165 
166     return ret;
167 }
168 
prvStreamBufferAdd(StreamBuffer_t * pxBuffer,const uint8_t * pucData,size_t uxByteCount)169 static size_t prvStreamBufferAdd( StreamBuffer_t * pxBuffer,
170                                   const uint8_t * pucData,
171                                   size_t uxByteCount )
172 {
173     size_t uxSpace, uxNextHead, uxFirst;
174     size_t uxCount = uxByteCount;
175 
176     uxSpace = uxStreamBufferGetSpace( pxBuffer );
177 
178     /* The number of bytes that can be written is the minimum of the number of
179      * bytes requested and the number available. */
180     uxCount = FreeRTOS_min_size_t( uxSpace, uxCount );
181 
182     if( uxCount != 0U )
183     {
184         uxNextHead = pxBuffer->uxHead;
185 
186         if( pucData != NULL )
187         {
188             /* Calculate the number of bytes that can be added in the first
189             * write - which may be less than the total number of bytes that need
190             * to be added if the buffer will wrap back to the beginning. */
191             uxFirst = FreeRTOS_min_size_t( pxBuffer->LENGTH - uxNextHead, uxCount );
192 
193             /* Write as many bytes as can be written in the first write. */
194             ( void ) memcpy( &( pxBuffer->ucArray[ uxNextHead ] ), pucData, uxFirst );
195 
196             /* If the number of bytes written was less than the number that
197              * could be written in the first write... */
198             if( uxCount > uxFirst )
199             {
200                 /* ...then write the remaining bytes to the start of the
201                  * buffer. */
202                 ( void ) memcpy( pxBuffer->ucArray, &( pucData[ uxFirst ] ), uxCount - uxFirst );
203             }
204         }
205 
206         uxNextHead += uxCount;
207 
208         if( uxNextHead >= pxBuffer->LENGTH )
209         {
210             uxNextHead -= pxBuffer->LENGTH;
211         }
212 
213         pxBuffer->uxHead = uxNextHead;
214     }
215 
216     return uxCount;
217 }
218 
219 /*!
220  * @brief API call, called from reeRTOS_IP.c to send a network packet over the
221  *        selected interface
222  * @return pdTRUE if successful else pdFALSE
223  */
xNetworkInterfaceOutput(NetworkInterface_t * pxInterface,NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t bReleaseAfterSend)224 static BaseType_t xNetworkInterfaceOutput( NetworkInterface_t * pxInterface,
225                                            NetworkBufferDescriptor_t * const pxNetworkBuffer,
226                                            BaseType_t bReleaseAfterSend )
227 {
228     size_t xSpace;
229 
230     iptraceNETWORK_INTERFACE_TRANSMIT();
231     configASSERT( xIsCallingFromIPTask() == pdTRUE );
232     ( void ) pxInterface;
233 
234     /* Both the length of the data being sent and the actual data being sent
235      *  are placed in the thread safe buffer used to pass data between the FreeRTOS
236      *  tasks and the pthread that sends data via the pcap library.  Drop
237      *  the packet if there is insufficient space in the buffer to hold both. */
238     xSpace = uxStreamBufferGetSpace( xSendBuffer );
239 
240     if( ( pxNetworkBuffer->xDataLength <=
241           ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
242         ( xSpace >= ( pxNetworkBuffer->xDataLength +
243                       sizeof( pxNetworkBuffer->xDataLength ) ) ) )
244     {
245         /* First write in the length of the data, then write in the data
246          * itself. */
247         uxStreamBufferAdd( xSendBuffer,
248                            0,
249                            ( const uint8_t * ) &( pxNetworkBuffer->xDataLength ),
250                            sizeof( pxNetworkBuffer->xDataLength ) );
251         uxStreamBufferAdd( xSendBuffer,
252                            0,
253                            ( const uint8_t * ) pxNetworkBuffer->pucEthernetBuffer,
254                            pxNetworkBuffer->xDataLength );
255     }
256     else
257     {
258         FreeRTOS_printf( ( "xNetworkInterfaceOutput: send buffers full to store %lu\n",
259                            pxNetworkBuffer->xDataLength ) );
260     }
261 
262     /* Kick the Tx task in either case in case it doesn't know the buffer is
263      * full. */
264     event_signal( pvSendEvent );
265 
266     /* The buffer has been sent so can be released. */
267     if( bReleaseAfterSend != pdFALSE )
268     {
269         vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
270     }
271 
272     return pdPASS;
273 }
274 
275 /* ====================== Static Function definitions ======================= */
276 
277 /*!
278  * @brief create thread safe buffers to send/receive packets between threads
279  * @returns
280  */
prvCreateThreadSafeBuffers(void)281 static int prvCreateThreadSafeBuffers( void )
282 {
283     int ret = pdFAIL;
284 
285     /* The buffer used to pass data to be transmitted from a FreeRTOS task to
286      * the linux thread that sends via the pcap library. */
287     do
288     {
289         if( xSendBuffer == NULL )
290         {
291             xSendBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) + xSEND_BUFFER_SIZE + 1 );
292 
293             if( xSendBuffer == NULL )
294             {
295                 break;
296             }
297 
298             configASSERT( xSendBuffer );
299             memset( xSendBuffer, '\0', sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) );
300             xSendBuffer->LENGTH = xSEND_BUFFER_SIZE + 1;
301         }
302 
303         /* The buffer used to pass received data from the pthread that receives
304          * via the pcap library to the FreeRTOS task. */
305         if( xRecvBuffer == NULL )
306         {
307             xRecvBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) + xRECV_BUFFER_SIZE + 1 );
308 
309             if( xRecvBuffer == NULL )
310             {
311                 break;
312             }
313 
314             configASSERT( xRecvBuffer );
315             memset( xRecvBuffer, '\0', sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) );
316             xRecvBuffer->LENGTH = xRECV_BUFFER_SIZE + 1;
317         }
318 
319         ret = pdPASS;
320     } while( 0 );
321 
322     return ret;
323 }
324 /*-----------------------------------------------------------*/
325 
xGetPhyLinkStatus(NetworkInterface_t * pxInterface)326 BaseType_t xGetPhyLinkStatus( NetworkInterface_t * pxInterface )
327 {
328     BaseType_t xResult = pdFALSE;
329 
330     ( void ) pxInterface;
331 
332     if( pxOpenedInterfaceHandle != NULL )
333     {
334         xResult = pdTRUE;
335     }
336 
337     return xResult;
338 }
339 
340 /*-----------------------------------------------------------*/
341 
342 #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
343 
344 
345 /* Do not call the following function directly. It is there for downward compatibility.
346  * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point
347  * objects.  See the description in FreeRTOS_Routing.h. */
pxFillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)348     NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex,
349                                                     NetworkInterface_t * pxInterface )
350     {
351         return pxLinux_FillInterfaceDescriptor( xEMACIndex, pxInterface );
352     }
353 
354 #endif
355 
356 /*-----------------------------------------------------------*/
357 
pxLinux_FillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)358 NetworkInterface_t * pxLinux_FillInterfaceDescriptor( BaseType_t xEMACIndex,
359                                                       NetworkInterface_t * pxInterface )
360 {
361     static char pcName[ 17 ];
362 
363 /* This function pxFillInterfaceDescriptor() adds a network-interface.
364  * Make sure that the object pointed to by 'pxInterface'
365  * is declared static or global, and that it will remain to exist. */
366 
367     pxMyInterface = pxInterface;
368 
369     snprintf( pcName, sizeof( pcName ), "eth%ld", xEMACIndex );
370 
371     memset( pxInterface, '\0', sizeof( *pxInterface ) );
372     pxInterface->pcName = pcName;                    /* Just for logging, debugging. */
373     pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
374     pxInterface->pfInitialise = xNetworkInterfaceInitialise;
375     pxInterface->pfOutput = xNetworkInterfaceOutput;
376     pxInterface->pfGetPhyLinkStatus = xGetPhyLinkStatus;
377 
378     FreeRTOS_AddNetworkInterface( pxInterface );
379 
380     return pxInterface;
381 }
382 /*-----------------------------------------------------------*/
383 
384 /*!
385  * @brief  print network interfaces available on the system
386  * @param[in]   pxAllNetworkInterfaces interface structure list to print
387  */
prvPrintAvailableNetworkInterfaces(pcap_if_t * pxAllNetworkInterfaces)388 static void prvPrintAvailableNetworkInterfaces( pcap_if_t * pxAllNetworkInterfaces )
389 {
390     pcap_if_t * xInterface;
391     int32_t lInterfaceNumber = 1;
392     char cBuffer[ 512 ];
393 
394     if( pxAllNetworkInterfaces != NULL )
395     {
396         /* Print out the list of network interfaces.  The first in the list
397          * is interface '1', not interface '0'. */
398         for( xInterface = pxAllNetworkInterfaces;
399              xInterface != NULL; xInterface = xInterface->next )
400         {
401             /* The descriptions of the devices can be full of spaces, clean them
402              * a little.  printf() can only be used here because the network is not
403              * up yet - so no other network tasks will be running. */
404             printf( "Interface %d - %s\n",
405                     lInterfaceNumber,
406                     prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->name ) );
407             printf( "              (%s)\n",
408                     prvRemoveSpaces( cBuffer,
409                                      sizeof( cBuffer ),
410                                      xInterface->description ? xInterface->description :
411                                      "No description" ) );
412             printf( "\n" );
413             lInterfaceNumber++;
414         }
415     }
416 
417     if( lInterfaceNumber == 1 )
418     {
419         /* The interface number was never incremented, so the above for() loop
420          * did not execute meaning no interfaces were found. */
421         printf( " \nNo network interfaces were found.\n" );
422         pxAllNetworkInterfaces = NULL;
423     }
424 
425     printf( "\r\nThe interface that will be opened is set by " );
426     printf( "\"configNETWORK_INTERFACE_TO_USE\", which\r\nshould be defined in FreeRTOSConfig.h\r\n" );
427 
428     if( ( xConfigNetworkInterfaceToUse < 1L ) || ( xConfigNetworkInterfaceToUse >= lInterfaceNumber ) )
429     {
430         printf( "\r\nERROR:  configNETWORK_INTERFACE_TO_USE is set to %ld, which is an invalid value.\r\n", xConfigNetworkInterfaceToUse );
431         printf( "Please set configNETWORK_INTERFACE_TO_USE to one of the interface numbers listed above,\r\n" );
432         printf( "then re-compile and re-start the application.  Only Ethernet (as opposed to WiFi)\r\n" );
433         printf( "interfaces are supported.\r\n\r\nHALTING\r\n\r\n\r\n" );
434         xInvalidInterfaceDetected = pdTRUE;
435 
436         if( pxAllNetworkInterfaces != NULL )
437         {
438             /* Free the device list, as no devices are going to be opened. */
439             pcap_freealldevs( pxAllNetworkInterfaces );
440             pxAllNetworkInterfaces = NULL;
441         }
442     }
443     else
444     {
445         printf( "Attempting to open interface number %ld.\n", xConfigNetworkInterfaceToUse );
446     }
447 }
448 
449 /*!
450  * @brief  get network interfaces from the system
451  * @returns the structure list containing all found devices
452  */
prvGetAvailableNetworkInterfaces(void)453 static pcap_if_t * prvGetAvailableNetworkInterfaces( void )
454 {
455     pcap_if_t * pxAllNetworkInterfaces = NULL;
456 
457     if( xInvalidInterfaceDetected == pdFALSE )
458     {
459         int ret;
460         ret = pcap_findalldevs( &pxAllNetworkInterfaces, errbuf );
461 
462         if( ret == PCAP_ERROR )
463         {
464             FreeRTOS_printf( ( "Could not obtain a list of network interfaces\n%s\n",
465                                errbuf ) );
466             pxAllNetworkInterfaces = NULL;
467         }
468         else
469         {
470             printf( "\r\n\r\nThe following network interfaces are available:\r\n\r\n" );
471         }
472     }
473 
474     return pxAllNetworkInterfaces;
475 }
476 
477 /*!
478  * @brief  set device operation modes
479  * @returns pdPASS on success pdFAIL on failure
480  */
prvSetDeviceModes()481 static int prvSetDeviceModes()
482 {
483     int ret = pdFAIL;
484 
485     /*
486      * Open in promiscuous mode as the MAC and
487      * IP address is going to be "simulated", and
488      * not be the real MAC and IP address.  This allows
489      * traffic to the simulated IP address to be routed
490      * to uIP, and traffic to the real IP address to be
491      * routed to the Linux TCP/IP stack.
492      */
493     FreeRTOS_debug_printf( ( "setting device modes of operation...\n" ) );
494 
495     do
496     {
497         ret = pcap_set_promisc( pxOpenedInterfaceHandle, 1 );
498 
499         if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) )
500         {
501             FreeRTOS_printf( ( "coult not activate promisuous mode\n" ) );
502             break;
503         }
504 
505         ret = pcap_set_snaplen( pxOpenedInterfaceHandle,
506                                 ipTOTAL_ETHERNET_FRAME_SIZE );
507 
508         if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) )
509         {
510             FreeRTOS_printf( ( "coult not set snaplen\n" ) );
511             break;
512         }
513 
514         ret = pcap_set_timeout( pxOpenedInterfaceHandle, 200 );
515 
516         if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) )
517         {
518             FreeRTOS_printf( ( "coult not set timeout\n" ) );
519             break;
520         }
521 
522         ret = pcap_set_buffer_size( pxOpenedInterfaceHandle,
523                                     ipTOTAL_ETHERNET_FRAME_SIZE * 1100 );
524 
525         if( ( ret != 0 ) && ( ret != PCAP_ERROR_ACTIVATED ) )
526         {
527             FreeRTOS_printf( ( "coult not set buffer size\n" ) );
528             break;
529         }
530 
531         ret = pdPASS;
532     } while( 0 );
533 
534     return ret;
535 }
536 
537 /*!
538  * @brief  open selected interface given its name
539  * @param [in] pucName interface  name to pen
540  * @returns pdPASS on success pdFAIL on failure
541  */
prvOpenInterface(const char * pucName)542 static int prvOpenInterface( const char * pucName )
543 {
544     static char pucInterfaceName[ 256 ];
545     int ret = pdFAIL;
546 
547     if( pucName != NULL )
548     {
549         ( void ) strncpy( pucInterfaceName, pucName, sizeof( pucInterfaceName ) );
550         pucInterfaceName[ sizeof( pucInterfaceName ) - ( size_t ) 1 ] = '\0';
551 
552         FreeRTOS_debug_printf( ( "opening interface %s\n", pucInterfaceName ) );
553 
554         pxOpenedInterfaceHandle = pcap_create( pucInterfaceName, errbuf );
555 
556         if( pxOpenedInterfaceHandle != NULL )
557         {
558             ret = prvSetDeviceModes();
559 
560             if( ret == pdPASS )
561             {
562                 if( pcap_activate( pxOpenedInterfaceHandle ) == 0 )
563                 {
564                     /* Configure the capture filter to allow blocking reads, and to filter
565                      * out packets that are not of interest to this demo. */
566                     ret = prvConfigureCaptureBehaviour();
567                 }
568                 else
569                 {
570                     FreeRTOS_debug_printf( ( "pcap activate error %s\n",
571                                              pcap_geterr( pxOpenedInterfaceHandle ) ) );
572                     ret = pdFAIL;
573                 }
574             }
575         }
576         else
577         {
578             FreeRTOS_printf( ( "\n%s is not supported by pcap and cannot be opened %s\n",
579                                pucInterfaceName, errbuf ) );
580         }
581     }
582     else
583     {
584         FreeRTOS_printf( ( "could not open interface: name is null\n" ) );
585     }
586 
587     return ret;
588 }
589 
590 /*!
591  * @brief Open the network interface. The number of the interface to be opened is
592  *	       set by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h
593  *	       Calling this function will set the pxOpenedInterfaceHandle variable
594  *	       If, after calling this function, pxOpenedInterfaceHandle
595  *	       is equal to NULL, then the interface could not be opened.
596  * @param [in] pxAllNetworkInterfaces network interface list to choose from
597  * @returns pdPASS on success or pdFAIL when something goes wrong
598  */
prvOpenSelectedNetworkInterface(pcap_if_t * pxAllNetworkInterfaces)599 static int prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces )
600 {
601     pcap_if_t * pxInterface;
602     int32_t x;
603     int ret = pdFAIL;
604 
605     /* Walk the list of devices until the selected device is located. */
606     pxInterface = pxAllNetworkInterfaces;
607 
608     for( x = 0L; x < ( xConfigNetworkInterfaceToUse - 1L ); x++ )
609     {
610         pxInterface = pxInterface->next;
611     }
612 
613     /* Open the selected interface. */
614     if( prvOpenInterface( pxInterface->name ) == pdPASS )
615     {
616         FreeRTOS_debug_printf( ( "Successfully opened interface number %d.\n", x + 1 ) );
617         ret = pdPASS;
618     }
619     else
620     {
621         FreeRTOS_printf( ( "Failed to open interface number %d.\n", x + 1 ) );
622     }
623 
624     return ret;
625 }
626 
627 /*!
628  * @brief launch 2 linux threads, one for Tx and one for Rx
629  *        and one FreeRTOS thread that will simulate an interrupt
630  *        and notify the tcp/ip stack of new data
631  * @return pdPASS on success otherwise pdFAIL
632  */
prvCreateWorkerThreads(void)633 static int prvCreateWorkerThreads( void )
634 {
635     pthread_t vPcapRecvThreadHandle;
636     pthread_t vPcapSendThreadHandle;
637     int ret = pdPASS;
638 
639     if( pvSendEvent == NULL )
640     {
641         FreeRTOS_debug_printf( ( "Creating Threads ..\n" ) );
642         ret = pdFAIL;
643         /* Create event used to signal the  pcap Tx thread. */
644         pvSendEvent = event_create();
645 
646         do
647         {
648             /* Create the thread that handles pcap  Rx. */
649             ret = pthread_create( &vPcapRecvThreadHandle,
650                                   NULL,
651                                   prvLinuxPcapRecvThread,
652                                   NULL );
653 
654             if( ret != 0 )
655             {
656                 FreeRTOS_printf( ( "pthread error %d", ret ) );
657                 break;
658             }
659 
660             /* Create the thread that handles pcap  Tx. */
661             ret = pthread_create( &vPcapSendThreadHandle,
662                                   NULL,
663                                   prvLinuxPcapSendThread,
664                                   NULL );
665 
666             if( ret != 0 )
667             {
668                 FreeRTOS_printf( ( "pthread error %d", ret ) );
669                 break;
670             }
671 
672             ret = pdPASS;
673         } while( 0 );
674 
675         /* Create a task that simulates an interrupt in a real system.  This will
676          * block waiting for packets, then send a message to the IP task when data
677          * is available. */
678         if( xTaskCreate( prvInterruptSimulatorTask,
679                          "MAC_ISR",
680                          configMINIMAL_STACK_SIZE,
681                          NULL,
682                          configMAC_ISR_SIMULATOR_PRIORITY,
683                          NULL ) != pdPASS )
684         {
685             ret = pdFAIL;
686             FreeRTOS_printf( ( "xTaskCreate could not create a new task\n" ) );
687         }
688     }
689 
690     return ret;
691 }
692 
693 /*!
694  * @brief Create the buffers used to pass packets between the FreeRTOS simulator
695  *        and the pthreads that are handling pcap as well as the FreeRTOS task
696  *        responsible of simulating an interrupt.
697  * @returns pdPASS when successful and pdFAIL when there is a failure
698  */
prvConfigureCaptureBehaviour(void)699 static int prvConfigureCaptureBehaviour( void )
700 {
701     struct bpf_program xFilterCode;
702     uint32_t ulNetMask;
703     char pcap_filter[ 500 ];
704     int ret = pdFAIL;
705 
706     FreeRTOS_debug_printf( ( "Configuring Capture behaviour\n" ) );
707 
708     /* Set up a filter so only the packets of interest are passed to the IP
709      * stack.  errbuf is used for convenience to create the string.  Don't
710      * confuse this with an error message. */
711     sprintf( pcap_filter, "broadcast or multicast or ether host %x:%x:%x:%x:%x:%x",
712              ipLOCAL_MAC_ADDRESS[ 0 ],
713              ipLOCAL_MAC_ADDRESS[ 1 ],
714              ipLOCAL_MAC_ADDRESS[ 2 ],
715              ipLOCAL_MAC_ADDRESS[ 3 ],
716              ipLOCAL_MAC_ADDRESS[ 4 ],
717              ipLOCAL_MAC_ADDRESS[ 5 ] );
718     FreeRTOS_debug_printf( ( "pcap filter to compile: %s\n", pcap_filter ) );
719 
720     ulNetMask = ( configNET_MASK3 << 24UL ) | ( configNET_MASK2 << 16UL ) | ( configNET_MASK1 << 8L ) | configNET_MASK0;
721 
722     ret = pcap_compile( pxOpenedInterfaceHandle,
723                         &xFilterCode,
724                         pcap_filter,
725                         1,
726                         ulNetMask );
727 
728     if( ret < 0 )
729     {
730         ( void ) printf( "\nThe packet filter string is invalid %s\n",
731                          pcap_geterr( pxOpenedInterfaceHandle ) );
732     }
733     else
734     {
735         ret = pcap_setfilter( pxOpenedInterfaceHandle, &xFilterCode );
736 
737         if( ret < 0 )
738         {
739             ( void ) printf( "\nAn error occurred setting the packet filter. %s\n",
740                              pcap_geterr( pxOpenedInterfaceHandle ) );
741         }
742         else
743         {
744             ret = pdPASS;
745         }
746 
747         /* When pcap_compile() succeeds, it allocates memory for the memory pointed to by the bpf_program struct
748          * parameter.pcap_freecode() will free that memory. */
749         pcap_freecode( &xFilterCode );
750     }
751 
752     return ret;
753 }
754 
755 /*!
756  * @brief  callback function called from pcap_dispatch function when new
757  *         data arrives on the interface
758  * @param [in] user data sent to pcap_dispatch
759  * @param [in] pkt_header received packet header
760  * @param [in] pkt_data received packet data
761  * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls
762  */
pcap_callback(unsigned char * user,const struct pcap_pkthdr * pkt_header,const u_char * pkt_data)763 static void pcap_callback( unsigned char * user,
764                            const struct pcap_pkthdr * pkt_header,
765                            const u_char * pkt_data )
766 {
767     FreeRTOS_debug_printf( ( "Receiving < ===========  network callback user: %s len: %d caplen: %d\n",
768                              user,
769                              pkt_header->len,
770                              pkt_header->caplen ) );
771     print_hex( pkt_data, pkt_header->len );
772 
773     /* Pass data to the FreeRTOS simulator on a thread safe circular buffer. */
774     if( ( pkt_header->caplen <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
775         ( uxStreamBufferGetSpace( xRecvBuffer ) >= ( ( ( size_t ) pkt_header->caplen ) + sizeof( *pkt_header ) ) ) )
776     {
777         /* NOTE. The prvStreamBufferAdd function is used here in place of
778          * uxStreamBufferAdd since the uxStreamBufferAdd call will suspend
779          * the FreeRTOS scheduler to atomically update the head and front
780          * of the stream buffer. Since xRecvBuffer is being used as a regular
781          * circular buffer (i.e. only the head and tail are needed), this call
782          * only updates the head of the buffer, removing the need to suspend
783          * the scheduler, and allowing this function to be safely called from
784          * a Windows thread. */
785         prvStreamBufferAdd( xRecvBuffer, ( const uint8_t * ) pkt_header, sizeof( *pkt_header ) );
786         prvStreamBufferAdd( xRecvBuffer, ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen );
787     }
788 }
789 
790 /*!
791  * @brief infinite loop pthread to read from pcap
792  * @param [in] pvParam not used
793  * @returns NULL
794  * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls
795  * @remarks This function disables signal, to prevent it from being put into
796  *          sleep byt the posix port
797  */
prvLinuxPcapRecvThread(void * pvParam)798 static void * prvLinuxPcapRecvThread( void * pvParam )
799 {
800     int ret;
801 
802     /* Disable signals to this thread since this is a Linux pthread to be able to
803      * printf and other blocking operations without being interrupted and put in
804      * suspension mode by the linux port signals
805      */
806     sigset_t set;
807 
808     ( void ) pvParam;
809 
810     sigfillset( &set );
811     pthread_sigmask( SIG_SETMASK, &set, NULL );
812 
813     for( ; ; )
814     {
815         ret = pcap_dispatch( pxOpenedInterfaceHandle, 1,
816                              pcap_callback, ( u_char * ) "mydata" );
817 
818         if( ret == -1 )
819         {
820             FreeRTOS_printf( ( "pcap_dispatch error received: %s\n",
821                                pcap_geterr( pxOpenedInterfaceHandle ) ) );
822         }
823     }
824 
825     return NULL;
826 }
827 
828 /*!
829  * @brief Infinite loop thread that waits for events when there is data
830  *        available then sends the data on the interface
831  * @param [in] pvParam not used
832  * @returns NULL
833  * @warning this is called from a Linux thread, do not attempt any FreeRTOS calls
834  */
prvLinuxPcapSendThread(void * pvParam)835 static void * prvLinuxPcapSendThread( void * pvParam )
836 {
837     size_t xLength;
838     uint8_t ucBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
839     const time_t xMaxMSToWait = 1000;
840 
841     /* disable signals to avoid treating this thread as a FreeRTOS task and putting
842      * it to sleep by the scheduler */
843     sigset_t set;
844 
845     ( void ) pvParam;
846 
847     sigfillset( &set );
848     pthread_sigmask( SIG_SETMASK, &set, NULL );
849 
850     for( ; ; )
851     {
852         /* Wait until notified of something to send. */
853         event_wait_timed( pvSendEvent, xMaxMSToWait );
854 
855         /* Is there more than the length value stored in the circular buffer
856         * used to pass data from the FreeRTOS simulator into this pthread?*/
857         while( uxStreamBufferGetSize( xSendBuffer ) > sizeof( xLength ) )
858         {
859             uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) &xLength, sizeof( xLength ), pdFALSE );
860             uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) ucBuffer, xLength, pdFALSE );
861             FreeRTOS_debug_printf( ( "Sending  ========== > data pcap_sendpadcket %lu\n", xLength ) );
862             print_hex( ucBuffer, xLength );
863 
864             if( pcap_sendpacket( pxOpenedInterfaceHandle, ucBuffer, ( int ) xLength ) != 0 )
865             {
866                 FreeRTOS_printf( ( "pcap_sendpackeet: send failed %d\n", ulPCAPSendFailures ) );
867                 ulPCAPSendFailures++;
868             }
869         }
870     }
871 
872     return NULL;
873 }
874 
875 /*-----------------------------------------------------------*/
876 
xPacketBouncedBack(const uint8_t * pucBuffer)877 static BaseType_t xPacketBouncedBack( const uint8_t * pucBuffer )
878 {
879     static BaseType_t xHasWarned = pdFALSE;
880     EthernetHeader_t * pxEtherHeader;
881     NetworkEndPoint_t * pxEndPoint;
882     BaseType_t xResult = pdFALSE;
883 
884     pxEtherHeader = ( EthernetHeader_t * ) pucBuffer;
885 
886     /* Sometimes, packets are bounced back by the driver and we need not process them. Check
887      * whether this packet is one such packet. */
888     for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
889          pxEndPoint != NULL;
890          pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
891     {
892         if( memcmp( pxEndPoint->xMACAddress.ucBytes, pxEtherHeader->xSourceAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ) == 0 )
893         {
894             if( xHasWarned == pdFALSE )
895             {
896                 xHasWarned = pdTRUE;
897                 FreeRTOS_printf( ( "Bounced back by WinPCAP interface: %02x:%02x:%02x:%02x:%02x:%02x\n",
898                                    pxEndPoint->xMACAddress.ucBytes[ 0 ],
899                                    pxEndPoint->xMACAddress.ucBytes[ 1 ],
900                                    pxEndPoint->xMACAddress.ucBytes[ 2 ],
901                                    pxEndPoint->xMACAddress.ucBytes[ 3 ],
902                                    pxEndPoint->xMACAddress.ucBytes[ 4 ],
903                                    pxEndPoint->xMACAddress.ucBytes[ 5 ] ) );
904             }
905 
906             xResult = pdTRUE;
907             break;
908         }
909     }
910 
911     return xResult;
912 }
913 /*-----------------------------------------------------------*/
914 
915 /*!
916  * @brief FreeRTOS infinite loop thread that simulates a network interrupt to notify the
917  *         network stack of the presence of new data
918  * @param [in] pvParameters not used
919  */
prvInterruptSimulatorTask(void * pvParameters)920 static void prvInterruptSimulatorTask( void * pvParameters )
921 {
922     struct pcap_pkthdr xHeader;
923     static struct pcap_pkthdr * pxHeader;
924     const uint8_t * pucPacketData;
925     uint8_t ucRecvBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
926     NetworkBufferDescriptor_t * pxNetworkBuffer;
927     IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
928     eFrameProcessingResult_t eResult;
929 
930     /* Remove compiler warnings about unused parameters. */
931     ( void ) pvParameters;
932 
933     for( ; ; )
934     {
935         /* Does the circular buffer used to pass data from the pthread thread that
936          * handles pacap Rx into the FreeRTOS simulator contain another packet? */
937         if( uxStreamBufferGetSize( xRecvBuffer ) > sizeof( xHeader ) )
938         {
939             /* Get the next packet. */
940             uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) &xHeader, sizeof( xHeader ), pdFALSE );
941             uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) ucRecvBuffer, ( size_t ) xHeader.len, pdFALSE );
942             pucPacketData = ucRecvBuffer;
943             pxHeader = &xHeader;
944 
945             iptraceNETWORK_INTERFACE_RECEIVE();
946 
947             /* Check for minimal size. */
948             if( pxHeader->len >= sizeof( EthernetHeader_t ) )
949             {
950                 eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData );
951             }
952             else
953             {
954                 eResult = eReleaseBuffer;
955             }
956 
957             if( eResult == eProcessBuffer )
958             {
959                 /* Will the data fit into the frame buffer? */
960                 if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE )
961                 {
962                     /* Obtain a buffer into which the data can be placed.  This
963                      * is only	an interrupt simulator, not a real interrupt, so it
964                      * is ok to call the task level function here, but note that
965                      * some buffer implementations cannot be called from a real
966                      * interrupt. */
967                     if( xPacketBouncedBack( pucPacketData ) == pdFALSE )
968                     {
969                         pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( pxHeader->len, 0 );
970                     }
971                     else
972                     {
973                         pxNetworkBuffer = NULL;
974                     }
975 
976                     if( pxNetworkBuffer != NULL )
977                     {
978                         memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len );
979                         pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len;
980 
981                         #if ( niDISRUPT_PACKETS == 1 )
982                             {
983                                 pxNetworkBuffer = vRxFaultInjection( pxNetworkBuffer, pucPacketData );
984                             }
985                         #endif /* niDISRUPT_PACKETS */
986 
987                         if( pxNetworkBuffer != NULL )
988                         {
989                             xRxEvent.pvData = ( void * ) pxNetworkBuffer;
990 
991                             pxNetworkBuffer->pxInterface = pxMyInterface;
992                             pxNetworkBuffer->pxEndPoint = FreeRTOS_MatchingEndpoint( pxMyInterface, pxNetworkBuffer->pucEthernetBuffer );
993                             pxNetworkBuffer->pxEndPoint = pxNetworkEndPoints; /*temporary change for single end point */
994 
995                             /* Data was received and stored.  Send a message to
996                              * the IP task to let it know. */
997                             if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL )
998                             {
999                                 /* The buffer could not be sent to the stack so
1000                                  * must be released again.  This is only an
1001                                  * interrupt simulator, not a real interrupt, so it
1002                                  * is ok to use the task level function here, but
1003                                  * note no all buffer implementations will allow
1004                                  * this function to be executed from a real
1005                                  * interrupt. */
1006                                 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
1007                                 iptraceETHERNET_RX_EVENT_LOST();
1008                             }
1009                         }
1010                         else
1011                         {
1012                             /* The packet was already released or stored inside
1013                              * vRxFaultInjection().  Don't release it here. */
1014                         }
1015                     }
1016                     else
1017                     {
1018                         iptraceETHERNET_RX_EVENT_LOST();
1019                     }
1020                 }
1021                 else
1022                 {
1023                     /* Log that a packet was dropped because it would have
1024                      * overflowed the buffer, but there may be more buffers to
1025                      * process. */
1026                 }
1027             }
1028         }
1029         else
1030         {
1031             /* There is no real way of simulating an interrupt.  Make sure
1032              * other tasks can run. */
1033             vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY );
1034         }
1035     }
1036 }
1037 
1038 /*!
1039  * @brief remove spaces from pcMessage into pcBuffer
1040  * @param [out] pcBuffer buffer to fill up
1041  * @param [in] aBuflen length of pcBuffer
1042  * @param [in] pcMessage original message
1043  * @returns
1044  */
prvRemoveSpaces(char * pcBuffer,int aBuflen,const char * pcMessage)1045 static const char * prvRemoveSpaces( char * pcBuffer,
1046                                      int aBuflen,
1047                                      const char * pcMessage )
1048 {
1049     char * pcTarget = pcBuffer;
1050 
1051     /* Utility function used to format messages being printed only. */
1052     while( ( *pcMessage != 0 ) && ( pcTarget < ( &pcBuffer[ aBuflen - 1 ] ) ) )
1053     {
1054         *( pcTarget++ ) = *pcMessage;
1055 
1056         if( isspace( *pcMessage ) != pdFALSE )
1057         {
1058             while( isspace( *pcMessage ) != pdFALSE )
1059             {
1060                 pcMessage++;
1061             }
1062         }
1063         else
1064         {
1065             pcMessage++;
1066         }
1067     }
1068 
1069     *pcTarget = '\0';
1070 
1071     return pcBuffer;
1072 }
1073 
1074 /*!
1075  * @brief print binary packet in hex
1076  * @param [in] bin_daa data to print
1077  * @param [in] len length of the data
1078  */
print_hex(unsigned const char * const bin_data,size_t len)1079 static void print_hex( unsigned const char * const bin_data,
1080                        size_t len )
1081 {
1082     size_t i;
1083 
1084     for( i = 0; i < len; ++i )
1085     {
1086         FreeRTOS_debug_printf( ( "%.2X ", bin_data[ i ] ) );
1087     }
1088 
1089     FreeRTOS_debug_printf( ( "\n" ) );
1090 }
1091 
1092 
1093 #define BUFFER_SIZE               ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
1094 #define BUFFER_SIZE_ROUNDED_UP    ( ( BUFFER_SIZE + 7 ) & ~0x07UL )
1095 
1096 /*!
1097  * @brief Allocate RAM for packet buffers and set the pucEthernetBuffer field for each descriptor.
1098  *        Called when the BufferAllocation1 scheme is used.
1099  * @param [in,out] pxNetworkBuffers Pointer to an array of NetworkBufferDescriptor_t to populate.
1100  */
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])1101 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
1102 {
1103     static uint8_t * pucNetworkPacketBuffers = NULL;
1104     size_t uxIndex;
1105 
1106     if( pucNetworkPacketBuffers == NULL )
1107     {
1108         pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP );
1109     }
1110 
1111     if( pucNetworkPacketBuffers == NULL )
1112     {
1113         FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) );
1114         configASSERT( 0 );
1115     }
1116     else
1117     {
1118         for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ )
1119         {
1120             size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP;
1121             NetworkBufferDescriptor_t ** ppDescriptor;
1122 
1123             /* At the beginning of each pbuff is a pointer to the relevant descriptor */
1124             ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] );
1125 
1126             /* Set this pointer to the address of the correct descriptor */
1127             *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] );
1128 
1129             /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
1130              * beginning of the allocated buffer. */
1131             pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] );
1132         }
1133     }
1134 }
1135