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