1 /*
2 * FreeRTOS V202212.00
3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * https://www.FreeRTOS.org
23 * https://github.com/FreeRTOS
24 *
25 */
26
27 #include <stdlib.h>
28
29 /* WinPCap includes. */
30 #define HAVE_REMOTE
31 #include "pcap.h"
32
33 /* FreeRTOS includes. */
34 #include "FreeRTOS.h"
35 #include "task.h"
36 #include "semphr.h"
37
38 /* FreeRTOS+TCP includes. */
39 #include "FreeRTOS_IP.h"
40 #include "FreeRTOS_IP_Private.h"
41 #include "NetworkBufferManagement.h"
42 #include "FreeRTOS_Routing.h"
43
44 /* Thread-safe circular buffers are being used to pass data to and from the PCAP
45 * access functions. */
46 #include "Win32-Extensions.h"
47 #include "FreeRTOS_Stream_Buffer.h"
48
49
50 /* Sizes of the thread safe circular buffers used to pass data to and from the
51 * WinPCAP Windows threads. */
52 #define xSEND_BUFFER_SIZE 32768
53 #define xRECV_BUFFER_SIZE 32768
54
55 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
56 * driver will filter incoming packets and only pass the stack those packets it
57 * considers need processing. */
58 #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
59 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer
60 #else
61 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
62 #endif
63
64 /* Used to insert test code only. */
65 #define niDISRUPT_PACKETS 0
66
67 /*-----------------------------------------------------------*/
68
69 /*
70 * Windows threads that are outside of the control of the FreeRTOS simulator are
71 * used to interface with the WinPCAP libraries.
72 */
73 DWORD WINAPI prvWinPcapRecvThread( void * pvParam );
74 DWORD WINAPI prvWinPcapSendThread( void * pvParam );
75
76 /*
77 * A pointer to the network interface is needed later when receiving packets.
78 */
79 static NetworkInterface_t * pxMyInterface;
80
81 /*
82 * Print out a numbered list of network interfaces that are available on the
83 * host computer.
84 */
85 static pcap_if_t * prvPrintAvailableNetworkInterfaces( void );
86
87 /*
88 * Open the network interface. The number of the interface to be opened is set
89 * by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h.
90 */
91 static void prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces,
92 const NetworkInterface_t * pxInterface );
93
94 static int prvOpenInterface( const char * pucName,
95 const NetworkInterface_t * pxInterface );
96
97 /*
98 * Configure the capture filter to allow blocking reads, and to filter out
99 * packets that are not of interest to this demo.
100 */
101 static void prvConfigureCaptureBehaviour( const NetworkInterface_t * pxInterface );
102
103 /*
104 * A function that simulates Ethernet interrupts by periodically polling the
105 * WinPCap interface for new data.
106 */
107 static void prvInterruptSimulatorTask( void * pvParameters );
108
109 /*
110 * Create the buffers that are used to pass data between the FreeRTOS simulator
111 * and the Win32 threads that manage WinPCAP.
112 */
113 static void prvCreateThreadSafeBuffers( void );
114
115 /*
116 * This function is equivalent to uxStreamBufferAdd from
117 * FreeRTOS_Stream_Buffer.c in the case that the stream buffer is being used
118 * as a normal circular buffer (i.e. only the tail and head pointers are
119 * needed). Thus, this function does not take the offset argument, and does not
120 * update the front pointer of the stream buffer. This allows the removal of
121 * the calls to vTaskSuspendAll and xTaskResumeAll, as the head and front
122 * pointer no longer need to be atomically updated, allowing this function to be
123 * safely used by a Windows thread.
124 */
125 static size_t prvStreamBufferAdd( StreamBuffer_t * pxBuffer,
126 const uint8_t * pucData,
127 size_t uxByteCount );
128
129 /*
130 * Utility function used to format print messages only.
131 */
132 static const char * prvRemoveSpaces( char * pcBuffer,
133 int aBuflen,
134 const char * pcMessage );
135
136 /*
137 * This function will return pdTRUE if the packet is targeted at
138 * the MAC address of this device, in other words when is was bounced-
139 * back by the WinPCap interface.
140 */
141 static BaseType_t xPacketBouncedBack( const uint8_t * pucBuffer );
142
143 /*-----------------------------------------------------------*/
144
145 /* Required by the WinPCap library. */
146 static char cErrorBuffer[ PCAP_ERRBUF_SIZE ];
147
148 /* An event used to wake up the Win32 thread that sends data through the WinPCAP
149 * library. */
150 static void * pvSendEvent = NULL;
151
152 /* _HT_ made the PCAP interface number configurable through the program's
153 * parameters in order to test in different machines. */
154 static BaseType_t xConfigNetworkInterfaceToUse = configNETWORK_INTERFACE_TO_USE;
155
156 /* Handles to the Windows threads that handle the PCAP IO. */
157 static HANDLE vWinPcapRecvThreadHandle = NULL;
158 static HANDLE vWinPcapSendThreadHandle = NULL;
159
160 /* The interface being used by WinPCap. */
161 static pcap_t * pxOpenedInterfaceHandle = NULL;
162
163 /* Circular buffers used by the PCAP Win32 threads. */
164 static StreamBuffer_t * xSendBuffer = NULL;
165 static StreamBuffer_t * xRecvBuffer = NULL;
166
167 /* Logs the number of WinPCAP send failures, for viewing in the debugger only. */
168 static volatile uint32_t ulWinPCAPSendFailures = 0;
169
170 /*-----------------------------------------------------------*/
171
172 static BaseType_t xWinPcap_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface );
173 static BaseType_t xWinPcap_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
174 NetworkBufferDescriptor_t * const pxNetworkBuffer,
175 BaseType_t bReleaseAfterSend );
176 static BaseType_t xWinPcap_GetPhyLinkStatus( NetworkInterface_t * pxInterface );
177
178 NetworkInterface_t * pxWinPcap_FillInterfaceDescriptor( BaseType_t xEMACIndex,
179 NetworkInterface_t * pxInterface );
180
181 /*-----------------------------------------------------------*/
182
xWinPcap_NetworkInterfaceInitialise(NetworkInterface_t * pxInterface)183 static BaseType_t xWinPcap_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
184 {
185 BaseType_t xReturn = pdFALSE;
186 pcap_if_t * pxAllNetworkInterfaces;
187
188 ( void ) pxInterface;
189
190 /* Query the computer the simulation is being executed on to find the
191 * network interfaces it has installed. */
192 pxAllNetworkInterfaces = prvPrintAvailableNetworkInterfaces();
193
194 /* Open the network interface. The number of the interface to be opened is
195 * set by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h.
196 * Calling this function will set the pxOpenedInterfaceHandle variable. If,
197 * after calling this function, pxOpenedInterfaceHandle is equal to NULL, then
198 * the interface could not be opened. */
199 if( pxAllNetworkInterfaces != NULL )
200 {
201 prvOpenSelectedNetworkInterface( pxAllNetworkInterfaces, pxInterface );
202 }
203
204 if( pxOpenedInterfaceHandle != NULL )
205 {
206 xReturn = pdPASS;
207 }
208
209 return xReturn;
210 }
211 /*-----------------------------------------------------------*/
212
prvCreateThreadSafeBuffers(void)213 static void prvCreateThreadSafeBuffers( void )
214 {
215 /* The buffer used to pass data to be transmitted from a FreeRTOS task to
216 * the Win32 thread that sends via the WinPCAP library. */
217 if( xSendBuffer == NULL )
218 {
219 xSendBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) + xSEND_BUFFER_SIZE + 1 );
220 configASSERT( xSendBuffer );
221 memset( xSendBuffer, '\0', sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) );
222 xSendBuffer->LENGTH = xSEND_BUFFER_SIZE + 1;
223 }
224
225 /* The buffer used to pass received data from the Win32 thread that receives
226 * via the WinPCAP library to the FreeRTOS task. */
227 if( xRecvBuffer == NULL )
228 {
229 xRecvBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) + xRECV_BUFFER_SIZE + 1 );
230 configASSERT( xRecvBuffer );
231 memset( xRecvBuffer, '\0', sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) );
232 xRecvBuffer->LENGTH = xRECV_BUFFER_SIZE + 1;
233 }
234 }
235 /*-----------------------------------------------------------*/
236
prvStreamBufferAdd(StreamBuffer_t * pxBuffer,const uint8_t * pucData,size_t uxByteCount)237 static size_t prvStreamBufferAdd( StreamBuffer_t * pxBuffer,
238 const uint8_t * pucData,
239 size_t uxByteCount )
240 {
241 size_t uxSpace, uxNextHead, uxFirst;
242 size_t uxCount = uxByteCount;
243
244 uxSpace = uxStreamBufferGetSpace( pxBuffer );
245
246 /* The number of bytes that can be written is the minimum of the number of
247 * bytes requested and the number available. */
248 uxCount = FreeRTOS_min_size_t( uxSpace, uxCount );
249
250 if( uxCount != 0U )
251 {
252 uxNextHead = pxBuffer->uxHead;
253
254 if( pucData != NULL )
255 {
256 /* Calculate the number of bytes that can be added in the first
257 * write - which may be less than the total number of bytes that need
258 * to be added if the buffer will wrap back to the beginning. */
259 uxFirst = FreeRTOS_min_size_t( pxBuffer->LENGTH - uxNextHead, uxCount );
260
261 /* Write as many bytes as can be written in the first write. */
262 ( void ) memcpy( &( pxBuffer->ucArray[ uxNextHead ] ), pucData, uxFirst );
263
264 /* If the number of bytes written was less than the number that
265 * could be written in the first write... */
266 if( uxCount > uxFirst )
267 {
268 /* ...then write the remaining bytes to the start of the
269 * buffer. */
270 ( void ) memcpy( pxBuffer->ucArray, &( pucData[ uxFirst ] ), uxCount - uxFirst );
271 }
272 }
273
274 uxNextHead += uxCount;
275
276 if( uxNextHead >= pxBuffer->LENGTH )
277 {
278 uxNextHead -= pxBuffer->LENGTH;
279 }
280
281 pxBuffer->uxHead = uxNextHead;
282 }
283
284 return uxCount;
285 }
286
287 /*-----------------------------------------------------------*/
288
xWinPcap_NetworkInterfaceOutput(NetworkInterface_t * pxInterface,NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t bReleaseAfterSend)289 static BaseType_t xWinPcap_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
290 NetworkBufferDescriptor_t * const pxNetworkBuffer,
291 BaseType_t bReleaseAfterSend )
292 {
293 size_t xSpace;
294
295 ( void ) pxInterface;
296
297 iptraceNETWORK_INTERFACE_TRANSMIT();
298 configASSERT( xIsCallingFromIPTask() == pdTRUE );
299
300 /* Both the length of the data being sent and the actual data being sent
301 * are placed in the thread safe buffer used to pass data between the FreeRTOS
302 * tasks and the Win32 thread that sends data via the WinPCAP library. Drop
303 * the packet if there is insufficient space in the buffer to hold both. */
304 xSpace = uxStreamBufferGetSpace( xSendBuffer );
305
306 if( ( pxNetworkBuffer->xDataLength <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
307 ( xSpace >= ( pxNetworkBuffer->xDataLength + sizeof( pxNetworkBuffer->xDataLength ) ) ) )
308 {
309 /* First write in the length of the data, then write in the data
310 * itself. */
311 uxStreamBufferAdd( xSendBuffer, 0, ( const uint8_t * ) &( pxNetworkBuffer->xDataLength ), sizeof( pxNetworkBuffer->xDataLength ) );
312 uxStreamBufferAdd( xSendBuffer, 0, ( const uint8_t * ) pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );
313 }
314 else
315 {
316 FreeRTOS_debug_printf( ( "xNetworkInterfaceOutput: send buffers full to store %lu\n", pxNetworkBuffer->xDataLength ) );
317 }
318
319 /* Kick the Tx task in either case in case it doesn't know the buffer is
320 * full. */
321 SetEvent( pvSendEvent );
322
323 /* The buffer has been sent so can be released. */
324 if( bReleaseAfterSend != pdFALSE )
325 {
326 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
327 }
328
329 return pdPASS;
330 }
331 /*-----------------------------------------------------------*/
332
xWinPcap_GetPhyLinkStatus(NetworkInterface_t * pxInterface)333 static BaseType_t xWinPcap_GetPhyLinkStatus( NetworkInterface_t * pxInterface )
334 {
335 BaseType_t xResult = pdFALSE;
336
337 ( void ) pxInterface;
338
339 if( pxOpenedInterfaceHandle != NULL )
340 {
341 xResult = pdTRUE;
342 }
343
344 return xResult;
345 }
346 /*-----------------------------------------------------------*/
347
348 #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
349
350
351 /* Do not call the following function directly. It is there for downward compatibility.
352 * The function FreeRTOS_IPInit() will call it to initialise the interface and end-point
353 * objects. See the description in FreeRTOS_Routing.h. */
pxFillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)354 NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex,
355 NetworkInterface_t * pxInterface )
356 {
357 pxWinPcap_FillInterfaceDescriptor( xEMACIndex, pxInterface );
358 }
359
360 #endif
361 /*-----------------------------------------------------------*/
362
pxWinPcap_FillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)363 NetworkInterface_t * pxWinPcap_FillInterfaceDescriptor( BaseType_t xEMACIndex,
364 NetworkInterface_t * pxInterface )
365 {
366 static char pcName[ 17 ];
367
368 /* This function pxWinPcap_FillInterfaceDescriptor() adds a network-interface.
369 * Make sure that the object pointed to by 'pxInterface'
370 * is declared static or global, and that it will remain to exist. */
371
372 pxMyInterface = pxInterface;
373
374 snprintf( pcName, sizeof( pcName ), "eth%ld", xEMACIndex );
375
376 memset( pxInterface, '\0', sizeof( *pxInterface ) );
377 pxInterface->pcName = pcName; /* Just for logging, debugging. */
378 pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
379 pxInterface->pfInitialise = xWinPcap_NetworkInterfaceInitialise;
380 pxInterface->pfOutput = xWinPcap_NetworkInterfaceOutput;
381 pxInterface->pfGetPhyLinkStatus = xWinPcap_GetPhyLinkStatus;
382
383 FreeRTOS_AddNetworkInterface( pxInterface );
384
385 return pxInterface;
386 }
387 /*-----------------------------------------------------------*/
388
389 #ifdef configNETWORK_INTERFACE_TYPE_TO_USE
390
391 /* In earlier versions of this network interface,
392 * `configNETWORK_INTERFACE_TO_USE` indicated the interface
393 * sequence number to be used. On some laptops with dynamic
394 * adapters, the numbering of interfaces changes all the time.
395 * The new macro 'configNETWORK_INTERFACE_TYPE_TO_USE' can be
396 * used to define the name of the interface to use, e.g. "Realtek"
397 * Note that a sort of strcasestr() is used to find a match between
398 * an interface name and e.g. "Realtek".
399 */
xDesiredAdapter(const char * pcDescription)400 static BaseType_t xDesiredAdapter( const char * pcDescription )
401 {
402 size_t uxIndex;
403 size_t uxLength;
404 size_t uxKeyLength = strlen( configNETWORK_INTERFACE_TYPE_TO_USE );
405 BaseType_t xMatchFound = pdFALSE;
406
407 if( ( pcDescription != NULL ) && ( pcDescription[ 0 ] != 0 ) )
408 {
409 uxLength = strlen( pcDescription );
410
411 if( uxKeyLength <= uxLength )
412 {
413 for( uxIndex = 0U; ( uxIndex <= uxLength - uxKeyLength ) && ( xMatchFound == 0 ); uxIndex++ )
414 {
415 if( strncasecmp( configNETWORK_INTERFACE_TYPE_TO_USE, &( pcDescription[ uxIndex ] ), uxKeyLength ) == 0 )
416 {
417 xMatchFound = pdTRUE;
418 break;
419 }
420 }
421 }
422 }
423
424 return xMatchFound;
425 }
426 #endif /* ifdef configNETWORK_INTERFACE_TYPE_TO_USE */
427 /*-----------------------------------------------------------*/
428
prvPrintAvailableNetworkInterfaces(void)429 static pcap_if_t * prvPrintAvailableNetworkInterfaces( void )
430 {
431 pcap_if_t * pxAllNetworkInterfaces = NULL, * xInterface;
432 int32_t lInterfaceNumber = 1;
433 char cBuffer[ 512 ];
434 static BaseType_t xInvalidInterfaceDetected = pdFALSE;
435
436 if( xInvalidInterfaceDetected == pdFALSE )
437 {
438 if( pcap_findalldevs_ex( PCAP_SRC_IF_STRING, NULL, &pxAllNetworkInterfaces, cErrorBuffer ) == -1 )
439 {
440 printf( "Could not obtain a list of network interfaces\n%s\n", cErrorBuffer );
441 pxAllNetworkInterfaces = NULL;
442 }
443 else
444 {
445 printf( "\r\n\r\nThe following network interfaces are available:\r\n\r\n" );
446 }
447
448 if( pxAllNetworkInterfaces != NULL )
449 {
450 /* Print out the list of network interfaces. The first in the list
451 * is interface '1', not interface '0'. */
452 for( xInterface = pxAllNetworkInterfaces; xInterface != NULL; xInterface = xInterface->next )
453 {
454 /* The descriptions of the devices can be full of spaces, clean them
455 * a little. printf() can only be used here because the network is not
456 * up yet - so no other network tasks will be running. */
457 printf( "Interface %d - %s\n", lInterfaceNumber, prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->name ) );
458 printf( " (%s)\n", prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->description ? xInterface->description : "No description" ) );
459 printf( "\n" );
460 #ifdef configNETWORK_INTERFACE_TYPE_TO_USE
461 {
462 if( xInterface->description != NULL )
463 {
464 if( xDesiredAdapter( xInterface->description ) )
465 {
466 printf( "The description of adapter %d matches with '%s'\n", lInterfaceNumber, configNETWORK_INTERFACE_TYPE_TO_USE );
467 xConfigNetworkInterfaceToUse = lInterfaceNumber;
468 }
469 }
470 }
471 #endif /* ifdef configNETWORK_INTERFACE_TYPE_TO_USE */
472 lInterfaceNumber++;
473 }
474 }
475
476 if( lInterfaceNumber == 1 )
477 {
478 /* The interface number was never incremented, so the above for() loop
479 * did not execute meaning no interfaces were found. */
480 printf( " \nNo network interfaces were found.\n" );
481 pxAllNetworkInterfaces = NULL;
482 }
483
484 printf( "\r\nThe interface that will be opened is set by " );
485 printf( "\"configNETWORK_INTERFACE_TO_USE\", which\r\nshould be defined in FreeRTOSConfig.h\r\n" );
486
487 if( ( xConfigNetworkInterfaceToUse < 1L ) || ( xConfigNetworkInterfaceToUse >= lInterfaceNumber ) )
488 {
489 printf( "\r\nERROR: configNETWORK_INTERFACE_TO_USE is set to %d, which is an invalid value.\r\n", xConfigNetworkInterfaceToUse );
490 printf( "Please set configNETWORK_INTERFACE_TO_USE to one of the interface numbers listed above,\r\n" );
491 printf( "then re-compile and re-start the application. Only Ethernet (as opposed to WiFi)\r\n" );
492 printf( "interfaces are supported.\r\n\r\nHALTING\r\n\r\n\r\n" );
493 xInvalidInterfaceDetected = pdTRUE;
494
495 if( pxAllNetworkInterfaces != NULL )
496 {
497 /* Free the device list, as no devices are going to be opened. */
498 pcap_freealldevs( pxAllNetworkInterfaces );
499 pxAllNetworkInterfaces = NULL;
500 }
501 }
502 else
503 {
504 printf( "Attempting to open interface number %d.\n", xConfigNetworkInterfaceToUse );
505 }
506 }
507
508 return pxAllNetworkInterfaces;
509 }
510 /*-----------------------------------------------------------*/
511
prvOpenInterface(const char * pucName,const NetworkInterface_t * pxInterface)512 static int prvOpenInterface( const char * pucName,
513 const NetworkInterface_t * pxInterface )
514 {
515 static char pucInterfaceName[ 256 ];
516
517 if( pucName != NULL )
518 {
519 strncpy( pucInterfaceName, pucName, sizeof( pucInterfaceName ) );
520 }
521
522 pxOpenedInterfaceHandle = pcap_open( pucInterfaceName, /* The name of the selected interface. */
523 ipTOTAL_ETHERNET_FRAME_SIZE, /* The size of the packet to capture. */
524 PCAP_OPENFLAG_PROMISCUOUS, /* Open in promiscuous mode as the MAC and
525 * IP address is going to be "simulated", and
526 * not be the real MAC and IP address. This allows
527 * traffic to the simulated IP address to be routed
528 * to uIP, and traffic to the real IP address to be
529 * routed to the Windows TCP/IP stack. */
530 100,
531 NULL, /* No authentication is required as this is
532 * not a remote capture session. */
533 cErrorBuffer
534 );
535
536 if( pxOpenedInterfaceHandle == NULL )
537 {
538 printf( "\n%s is not supported by WinPcap and cannot be opened\n", pucInterfaceName );
539 return 1;
540 }
541 else
542 {
543 /* Configure the capture filter to allow blocking reads, and to filter
544 * out packets that are not of interest to this demo. */
545 prvConfigureCaptureBehaviour( pxInterface );
546 }
547
548 return 0;
549 }
550 /*-----------------------------------------------------------*/
551
prvOpenSelectedNetworkInterface(pcap_if_t * pxAllNetworkInterfaces,const NetworkInterface_t * pxInterface)552 static void prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces,
553 const NetworkInterface_t * pxInterface )
554 {
555 pcap_if_t * pxPCAPInterface;
556 int32_t x;
557
558 /* Walk the list of devices until the selected device is located. */
559 pxPCAPInterface = pxAllNetworkInterfaces;
560
561 for( x = 0L; x < ( xConfigNetworkInterfaceToUse - 1L ); x++ )
562 {
563 pxPCAPInterface = pxPCAPInterface->next;
564 }
565
566 /* Open the selected interface. */
567 if( prvOpenInterface( pxPCAPInterface->name, pxInterface ) == 0 )
568 {
569 printf( "Successfully opened interface number %d.\n", x + 1 );
570 }
571 else
572 {
573 printf( "Failed to open interface number %d.\n", x + 1 );
574 }
575
576 /* The device list is no longer required. */
577 pcap_freealldevs( pxAllNetworkInterfaces );
578 }
579 /*-----------------------------------------------------------*/
580
prvConfigureCaptureBehaviour(const NetworkInterface_t * pxInterface)581 static void prvConfigureCaptureBehaviour( const NetworkInterface_t * pxInterface )
582 {
583 struct bpf_program xFilterCode;
584 uint32_t ulNetMask;
585 const uint8_t * pucMAC;
586 NetworkEndPoint_t * pxEndPoint;
587
588 /* Find the MAC address of the very first end-point. */
589 pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface );
590
591 /* Make sure that at least one end-point is defined. */
592 configASSERT( pxEndPoint != NULL );
593
594 pucMAC = pxEndPoint->xMACAddress.ucBytes;
595
596 /* Set up a filter so only the packets of interest are passed to the IP
597 * stack. cErrorBuffer is used for convenience to create the string. Don't
598 * confuse this with an error message. */
599 snprintf( cErrorBuffer, sizeof( cErrorBuffer ), "broadcast or multicast or ether host %x:%x:%x:%x:%x:%x",
600 pucMAC[ 0 ], pucMAC[ 1 ], pucMAC[ 2 ], pucMAC[ 3 ], pucMAC[ 4 ], pucMAC[ 5 ] );
601
602 ulNetMask = ( configNET_MASK3 << 24UL ) | ( configNET_MASK2 << 16UL ) | ( configNET_MASK1 << 8L ) | configNET_MASK0;
603
604 if( pcap_compile( pxOpenedInterfaceHandle, &xFilterCode, cErrorBuffer, 1, ulNetMask ) < 0 )
605 {
606 printf( "\nThe packet filter string is invalid\n" );
607 }
608 else
609 {
610 if( pcap_setfilter( pxOpenedInterfaceHandle, &xFilterCode ) < 0 )
611 {
612 printf( "\nAn error occurred setting the packet filter.\n" );
613 }
614
615 /* When pcap_compile() succeeds, it allocates memory for the memory pointed to by the bpf_program struct
616 * parameter.pcap_freecode() will free that memory. */
617 pcap_freecode( &xFilterCode );
618 }
619
620 /* Create the buffers used to pass packets between the FreeRTOS simulator
621 * and the Win32 threads that are handling WinPCAP. */
622 prvCreateThreadSafeBuffers();
623
624 if( pvSendEvent == NULL )
625 {
626 /* Create event used to signal the Win32 WinPCAP Tx thread. */
627 pvSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
628
629 /* Create the Win32 thread that handles WinPCAP Rx. */
630 vWinPcapRecvThreadHandle = CreateThread(
631 NULL, /* Pointer to thread security attributes. */
632 0, /* Initial thread stack size, in bytes. */
633 prvWinPcapRecvThread, /* Pointer to thread function. */
634 NULL, /* Argument for new thread. */
635 0, /* Creation flags. */
636 NULL );
637
638 /* Use the cores that are not used by the FreeRTOS tasks. */
639 SetThreadAffinityMask( vWinPcapRecvThreadHandle, ~0x01u );
640
641 /* Create the Win32 thread that handlers WinPCAP Tx. */
642 vWinPcapSendThreadHandle = CreateThread(
643 NULL, /* Pointer to thread security attributes. */
644 0, /* initial thread stack size, in bytes. */
645 prvWinPcapSendThread, /* Pointer to thread function. */
646 NULL, /* Argument for new thread. */
647 0, /* Creation flags. */
648 NULL );
649
650 /* Use the cores that are not used by the FreeRTOS tasks. */
651 SetThreadAffinityMask( vWinPcapSendThreadHandle, ~0x01u );
652
653 /* Create a task that simulates an interrupt in a real system. This will
654 * block waiting for packets, then send a message to the IP task when data
655 * is available. */
656 xTaskCreate( prvInterruptSimulatorTask, "MAC_ISR", configMINIMAL_STACK_SIZE, NULL, configMAC_ISR_SIMULATOR_PRIORITY, NULL );
657 }
658 }
659 /*-----------------------------------------------------------*/
660
661 /* WinPCAP function. */
pcap_callback(u_char * user,const struct pcap_pkthdr * pkt_header,const u_char * pkt_data)662 void pcap_callback( u_char * user,
663 const struct pcap_pkthdr * pkt_header,
664 const u_char * pkt_data )
665 {
666 ( void ) user;
667
668 /* THIS IS CALLED FROM A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS
669 * OR TO PRINT OUT MESSAGES HERE. */
670
671 /* Pass data to the FreeRTOS simulator on a thread safe circular buffer. */
672 if( ( pkt_header->caplen <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
673 ( uxStreamBufferGetSpace( xRecvBuffer ) >= ( ( ( size_t ) pkt_header->caplen ) + sizeof( *pkt_header ) ) ) )
674 {
675 /* The received packets will be written to a C source file,
676 * only if 'ipconfigUSE_DUMP_PACKETS' is defined.
677 * Otherwise, there is no action. */
678 iptraceDUMP_PACKET( ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen, pdTRUE );
679
680 /* NOTE. The prvStreamBufferAdd function is used here in place of
681 * uxStreamBufferAdd since the uxStreamBufferAdd call will suspend
682 * the FreeRTOS scheduler to atomically update the head and front
683 * of the stream buffer. Since xRecvBuffer is being used as a regular
684 * circular buffer (i.e. only the head and tail are needed), this call
685 * only updates the head of the buffer, removing the need to suspend
686 * the scheduler, and allowing this function to be safely called from
687 * a Windows thread. */
688 prvStreamBufferAdd( xRecvBuffer, ( const uint8_t * ) pkt_header, sizeof( *pkt_header ) );
689 prvStreamBufferAdd( xRecvBuffer, ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen );
690 }
691 }
692 /*-----------------------------------------------------------*/
693
prvWinPcapRecvThread(void * pvParam)694 DWORD WINAPI prvWinPcapRecvThread( void * pvParam )
695 {
696 ( void ) pvParam;
697
698 /* THIS IS A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS OR TO PRINT
699 * OUT MESSAGES HERE. */
700
701 for( ; ; )
702 {
703 pcap_dispatch( pxOpenedInterfaceHandle, 1, pcap_callback, ( u_char * ) "mydata" );
704 }
705 }
706 /*-----------------------------------------------------------*/
707
prvWinPcapSendThread(void * pvParam)708 DWORD WINAPI prvWinPcapSendThread( void * pvParam )
709 {
710 size_t xLength;
711 uint8_t ucBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
712 static char cErrorMessage[ 1024 ];
713 const DWORD xMaxMSToWait = 1000;
714
715 /* THIS IS A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS OR TO PRINT
716 * OUT MESSAGES HERE. */
717
718 /* Remove compiler warnings about unused parameters. */
719 ( void ) pvParam;
720
721 for( ; ; )
722 {
723 /* Wait until notified of something to send. */
724 WaitForSingleObject( pvSendEvent, xMaxMSToWait );
725
726 /* Is there more than the length value stored in the circular buffer
727 * used to pass data from the FreeRTOS simulator into this Win32 thread? */
728 while( uxStreamBufferGetSize( xSendBuffer ) > sizeof( xLength ) )
729 {
730 uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) &xLength, sizeof( xLength ), pdFALSE );
731 uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) ucBuffer, xLength, pdFALSE );
732
733 /* The packets sent will be written to a C source file,
734 * only if 'ipconfigUSE_DUMP_PACKETS' is defined.
735 * Otherwise, there is no action. */
736 iptraceDUMP_PACKET( ucBuffer, xLength, pdFALSE );
737
738 if( pcap_sendpacket( pxOpenedInterfaceHandle, ucBuffer, xLength ) != 0 )
739 {
740 ulWinPCAPSendFailures++;
741 }
742 }
743 }
744 }
745 /*-----------------------------------------------------------*/
746
xPacketBouncedBack(const uint8_t * pucBuffer)747 static BaseType_t xPacketBouncedBack( const uint8_t * pucBuffer )
748 {
749 static BaseType_t xHasWarned = pdFALSE;
750 EthernetHeader_t * pxEtherHeader;
751 NetworkEndPoint_t * pxEndPoint;
752 BaseType_t xResult = pdFALSE;
753
754 pxEtherHeader = ( EthernetHeader_t * ) pucBuffer;
755
756 /* Sometimes, packets are bounced back by the driver and we need not process them. Check
757 * whether this packet is one such packet. */
758 for( pxEndPoint = FreeRTOS_FirstEndPoint( NULL );
759 pxEndPoint != NULL;
760 pxEndPoint = FreeRTOS_NextEndPoint( NULL, pxEndPoint ) )
761 {
762 if( memcmp( pxEndPoint->xMACAddress.ucBytes, pxEtherHeader->xSourceAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ) == 0 )
763 {
764 if( xHasWarned == pdFALSE )
765 {
766 xHasWarned = pdTRUE;
767 FreeRTOS_printf( ( "Bounced back by WinPCAP interface: %02x:%02x:%02x:%02x:%02x:%02x\n",
768 pxEndPoint->xMACAddress.ucBytes[ 0 ],
769 pxEndPoint->xMACAddress.ucBytes[ 1 ],
770 pxEndPoint->xMACAddress.ucBytes[ 2 ],
771 pxEndPoint->xMACAddress.ucBytes[ 3 ],
772 pxEndPoint->xMACAddress.ucBytes[ 4 ],
773 pxEndPoint->xMACAddress.ucBytes[ 5 ] ) );
774 }
775
776 xResult = pdTRUE;
777 break;
778 }
779 }
780
781 return xResult;
782 }
783 /*-----------------------------------------------------------*/
784
prvInterruptSimulatorTask(void * pvParameters)785 static void prvInterruptSimulatorTask( void * pvParameters )
786 {
787 struct pcap_pkthdr xHeader;
788 static struct pcap_pkthdr * pxHeader;
789 const uint8_t * pucPacketData;
790 uint8_t ucRecvBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
791 NetworkBufferDescriptor_t * pxNetworkBuffer;
792 IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
793 eFrameProcessingResult_t eResult;
794
795 /* Remove compiler warnings about unused parameters. */
796 ( void ) pvParameters;
797
798 for( ; ; )
799 {
800 /* Does the circular buffer used to pass data from the Win32 thread that
801 * handles WinPCAP Rx into the FreeRTOS simulator contain another packet? */
802 if( uxStreamBufferGetSize( xRecvBuffer ) > sizeof( xHeader ) )
803 {
804 /* Get the next packet. */
805 uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) &xHeader, sizeof( xHeader ), pdFALSE );
806 uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) ucRecvBuffer, ( size_t ) xHeader.len, pdFALSE );
807 pucPacketData = ucRecvBuffer;
808 pxHeader = &xHeader;
809
810 iptraceNETWORK_INTERFACE_RECEIVE();
811
812 /* Check for minimal size. */
813 if( pxHeader->len >= sizeof( EthernetHeader_t ) )
814 {
815 eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData );
816 }
817 else
818 {
819 eResult = eReleaseBuffer;
820 }
821
822 if( eResult == eProcessBuffer )
823 {
824 /* Will the data fit into the frame buffer? */
825 if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE )
826 {
827 BaseType_t xBounced = xPacketBouncedBack( pucPacketData );
828
829 /* Obtain a buffer into which the data can be placed. This
830 * is only an interrupt simulator, not a real interrupt, so it
831 * is ok to call the task level function here, but note that
832 * some buffer implementations cannot be called from a real
833 * interrupt. */
834 if( xBounced == pdFALSE )
835 {
836 pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( pxHeader->len, 0 );
837 }
838 else
839 {
840 pxNetworkBuffer = NULL;
841 }
842
843 if( pxNetworkBuffer != NULL )
844 {
845 memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len );
846 pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len;
847
848 #if ( niDISRUPT_PACKETS == 1 )
849 {
850 pxNetworkBuffer = vRxFaultInjection( pxNetworkBuffer, pucPacketData );
851 }
852 #endif /* niDISRUPT_PACKETS */
853
854 if( pxNetworkBuffer != NULL )
855 {
856 xRxEvent.pvData = ( void * ) pxNetworkBuffer;
857
858 pxNetworkBuffer->pxInterface = pxMyInterface;
859 pxNetworkBuffer->pxEndPoint = FreeRTOS_MatchingEndpoint( pxMyInterface, pxNetworkBuffer->pucEthernetBuffer );
860
861 /* Just for now, make sure that there is a valid end-point. */
862 if( pxNetworkBuffer->pxEndPoint == NULL )
863 {
864 FreeRTOS_printf( ( "Network interface: dropped packet\n" ) );
865 vReleaseNetworkBufferAndDescriptor( BUFFER_FROM_WHERE_CALL( 153 ) pxNetworkBuffer );
866 }
867 else
868 {
869 /* Data was received and stored. Send a message to
870 * the IP task to let it know. */
871 if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL )
872 {
873 /* The buffer could not be sent to the stack so
874 * must be released again. This is only an
875 * interrupt simulator, not a real interrupt, so it
876 * is ok to use the task level function here, but
877 * note no all buffer implementations will allow
878 * this function to be executed from a real
879 * interrupt. */
880 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
881 iptraceETHERNET_RX_EVENT_LOST();
882 }
883 }
884 }
885 else
886 {
887 /* The packet was already released or stored inside
888 * vRxFaultInjection(). Don't release it here. */
889 }
890 }
891 else
892 {
893 iptraceETHERNET_RX_EVENT_LOST();
894 }
895 }
896 else
897 {
898 /* Log that a packet was dropped because it would have
899 * overflowed the buffer, but there may be more buffers to
900 * process. */
901 }
902 }
903 }
904 else
905 {
906 /* There is no real way of simulating an interrupt. Make sure
907 * other tasks can run. */
908 vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY );
909 }
910 }
911 }
912 /*-----------------------------------------------------------*/
913
prvRemoveSpaces(char * pcBuffer,int aBuflen,const char * pcMessage)914 static const char * prvRemoveSpaces( char * pcBuffer,
915 int aBuflen,
916 const char * pcMessage )
917 {
918 char * pcTarget = pcBuffer;
919
920 /* Utility function used to format messages being printed only. */
921 while( ( *pcMessage != 0 ) && ( pcTarget < ( pcBuffer + aBuflen - 1 ) ) )
922 {
923 *( pcTarget++ ) = *pcMessage;
924
925 if( isspace( *pcMessage ) != pdFALSE )
926 {
927 while( isspace( *pcMessage ) != pdFALSE )
928 {
929 pcMessage++;
930 }
931 }
932 else
933 {
934 pcMessage++;
935 }
936 }
937
938 *pcTarget = '\0';
939
940 return pcBuffer;
941 }
942 /*-----------------------------------------------------------*/
943
944 #define BUFFER_SIZE ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
945 #define BUFFER_SIZE_ROUNDED_UP ( ( BUFFER_SIZE + 7 ) & ~0x07UL )
946
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])947 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
948 {
949 static uint8_t * pucNetworkPacketBuffers = NULL;
950 size_t uxIndex;
951
952 if( pucNetworkPacketBuffers == NULL )
953 {
954 pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP );
955 }
956
957 if( pucNetworkPacketBuffers == NULL )
958 {
959 FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) );
960 configASSERT( 0 );
961 }
962 else
963 {
964 for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ )
965 {
966 size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP;
967 NetworkBufferDescriptor_t ** ppDescriptor;
968
969 /* At the beginning of each pbuff is a pointer to the relevant descriptor */
970 ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] );
971
972 /* Set this pointer to the address of the correct descriptor */
973 *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] );
974
975 /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
976 * beginning of the allocated buffer. */
977 pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] );
978 }
979 }
980 }
981