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