]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-UDP/portable/NetworkInterface/WinPCap/NetworkInterface.c
Update to MIT licensed FreeRTOS V10.0.0 - see https://www.freertos.org/History.txt
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-UDP / portable / NetworkInterface / WinPCap / NetworkInterface.c
1 /*\r
2  * FreeRTOS+UDP V1.0.4\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software. If you wish to use our Amazon\r
14  * FreeRTOS name, please do so in a fair use way that does not cause confusion.\r
15  *\r
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22  *\r
23  * http://www.FreeRTOS.org\r
24  * http://aws.amazon.com/freertos\r
25  *\r
26  * 1 tab == 4 spaces!\r
27  */\r
28 \r
29 /* WinPCap includes. */\r
30 #define HAVE_REMOTE\r
31 #include "pcap.h"\r
32 \r
33 /* Standard includes. */\r
34 #include <stdint.h>\r
35 \r
36 /* FreeRTOS includes. */\r
37 #include "FreeRTOS.h"\r
38 #include "task.h"\r
39 #include "queue.h"\r
40 #include "semphr.h"\r
41 \r
42 /* FreeRTOS+UDP includes. */\r
43 #include "FreeRTOS_UDP_IP.h"\r
44 #include "FreeRTOS_IP_Private.h"\r
45 #include "FreeRTOS_Sockets.h"\r
46 #include "NetworkBufferManagement.h"\r
47 \r
48 /* Demo includes. */\r
49 #include "NetworkInterface.h"\r
50 \r
51 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet\r
52 driver will filter incoming packets and only pass the stack those packets it\r
53 considers need processing.  In this case ipCONSIDER_FRAME_FOR_PROCESSING() can\r
54 be #defined away.  If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 0\r
55 then the Ethernet driver will pass all received packets to the stack, and the\r
56 stack must do the filtering itself.  In this case ipCONSIDER_FRAME_FOR_PROCESSING\r
57 needs to call eConsiderFrameForProcessing. */\r
58 #if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES != 1\r
59         #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer\r
60 #else\r
61         #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) )\r
62 #endif\r
63 \r
64 /*-----------------------------------------------------------*/\r
65 \r
66 /*\r
67  * Print out a numbered list of network interfaces that are available on the\r
68  * host computer.\r
69  */\r
70 static pcap_if_t * prvPrintAvailableNetworkInterfaces( void );\r
71 \r
72 /*\r
73  * Open the network interface.  The number of the interface to be opened is set\r
74  * by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h.\r
75  */\r
76 static void prvOpenSelectedNetworkInterface( pcap_if_t *pxAllNetworkInterfaces );\r
77 \r
78 /*\r
79  * Configure the capture filter to allow blocking reads, and to filter out\r
80  * packets that are not of interest to this demo.\r
81  */\r
82 static void prvConfigureCaptureBehaviour( void );\r
83 \r
84 /*\r
85  * A function that simulates Ethernet interrupts by periodically polling the\r
86  * WinPCap interface for new data.\r
87  */\r
88 static void prvInterruptSimulatorTask( void *pvParameters );\r
89 \r
90 /* The interface being used by WinPCap. */\r
91 static pcap_t *pxOpenedInterfaceHandle = NULL;\r
92 \r
93 /*-----------------------------------------------------------*/\r
94 \r
95 /* Required by the WinPCap library. */\r
96 static char cErrorBuffer[ PCAP_ERRBUF_SIZE ];\r
97 \r
98 /* When statically allocated network buffers are used (as opposed to having\r
99 the buffer payloads allocated and freed as required) the actual buffer storage\r
100 areas must be defined in the portable layer.  This is because different\r
101 microcontrollers have different location, size and alignment requirements.  In\r
102 this case the network buffers are declared in NetworkInterface.c because, as\r
103 this file is only used on Windows machines, wasting a few bytes in buffers that\r
104 never get used does not matter (the buffers will not get used if the dynamic\r
105 payload allocation file is included in the project). */\r
106 static uint8_t ucBuffers[ ipconfigNUM_NETWORK_BUFFERS ][ ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING ];\r
107 \r
108 /* The queue used to communicate Ethernet events with the IP task. */\r
109 extern xQueueHandle xNetworkEventQueue;\r
110 \r
111 /* Protect the PCAP interface as it is accessed from two tasks (an interrupt\r
112 simulator is used as real interrupts cannot be obtained from the Ethernet as\r
113 would normally be the case). */\r
114 xSemaphoreHandle xPCAPMutex = NULL;\r
115 \r
116 /*-----------------------------------------------------------*/\r
117 \r
118 BaseType_t xNetworkInterfaceInitialise( void )\r
119 {\r
120 BaseType_t xReturn = pdFALSE;\r
121 pcap_if_t *pxAllNetworkInterfaces;\r
122 \r
123         if( xPCAPMutex == NULL )\r
124         {\r
125                 xPCAPMutex = xSemaphoreCreateMutex();\r
126                 configASSERT( xPCAPMutex );\r
127         }\r
128 \r
129         /* Query the computer the simulation is being executed on to find the\r
130         network interfaces it has installed. */\r
131         pxAllNetworkInterfaces = prvPrintAvailableNetworkInterfaces();\r
132 \r
133         /* Open the network interface.  The number of the interface to be opened is\r
134         set by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h.\r
135         Calling this function will set the pxOpenedInterfaceHandle variable.  If,\r
136         after calling this function, pxOpenedInterfaceHandle is equal to NULL, then\r
137         the interface could not be opened. */\r
138         if( pxAllNetworkInterfaces != NULL )\r
139         {\r
140                 prvOpenSelectedNetworkInterface( pxAllNetworkInterfaces );\r
141         }\r
142 \r
143         if( pxOpenedInterfaceHandle != NULL )\r
144         {\r
145                 xReturn = pdPASS;\r
146         }\r
147 \r
148         return xReturn;\r
149 }\r
150 /*-----------------------------------------------------------*/\r
151 \r
152 #if updconfigLOOPBACK_ETHERNET_PACKETS == 1\r
153 \r
154         BaseType_t xNetworkInterfaceOutput( xNetworkBufferDescriptor_t * const pxNetworkBuffer )\r
155         {\r
156         xEthernetHeader_t *pxEthernetHeader;\r
157         xIPStackEvent_t xRxEvent = { eEthernetRxEvent, NULL };\r
158         extern uint8_t xDefaultPartUDPPacketHeader[];\r
159         static const xMACAddress_t xBroadcastMACAddress = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };\r
160         BaseType_t xCanLoopback;\r
161 \r
162                 pxEthernetHeader = ( xEthernetHeader_t * ) pxNetworkBuffer->pucEthernetBuffer;\r
163 \r
164                 if( memcmp( ( void * ) &( pxEthernetHeader->xDestinationAddress ), ( void * ) &xBroadcastMACAddress, sizeof( xMACAddress_t ) ) == 0 )\r
165                 {\r
166                         /* This is a broadcast. */\r
167                         xCanLoopback = pdTRUE;\r
168                 }\r
169                 else if( memcmp( ( void * ) &( pxEthernetHeader->xDestinationAddress ), ( void * ) xDefaultPartUDPPacketHeader, sizeof( xMACAddress_t ) ) == 0 )\r
170                 {\r
171                         /* This is being sent to itself. */\r
172                         xCanLoopback = pdTRUE;\r
173                 }\r
174                 else\r
175                 {\r
176                         /* This is being sent externally. */\r
177                         xCanLoopback = pdFALSE;\r
178                 }\r
179 \r
180                 iptraceNETWORK_INTERFACE_TRANSMIT();\r
181 \r
182                 if( xCanLoopback == pdTRUE )\r
183                 {\r
184                         /* Just loop the frame back to the input queue.  Here the loopback\r
185                         is sending a message to itself, so a block time cannot be used for\r
186                         fear of deadlocking. */\r
187                         xRxEvent.pvData = ( void * ) pxNetworkBuffer;\r
188                         if( xQueueSendToBack( xNetworkEventQueue, &xRxEvent, ( TickType_t ) 0 ) == pdFALSE )\r
189                         {\r
190                                 vNetworkBufferRelease( pxNetworkBuffer );\r
191                                 iptraceETHERNET_RX_EVENT_LOST();\r
192                         }\r
193                         else\r
194                         {\r
195                                 iptraceNETWORK_INTERFACE_RECEIVE();\r
196                         }\r
197                 }\r
198                 else\r
199                 {\r
200                         /* Send the packet. */\r
201                         xSemaphoreTake( xPCAPMutex, portMAX_DELAY );\r
202                         {\r
203                                 pcap_sendpacket( pxOpenedInterfaceHandle, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );\r
204                         }\r
205                         xSemaphoreGive( xPCAPMutex );\r
206 \r
207                         /* The buffer has been transmitted so can be released. */\r
208                         vNetworkBufferRelease( pxNetworkBuffer );\r
209                 }\r
210 \r
211                 return pdPASS;\r
212         }\r
213 \r
214 #else /* updconfigLOOPBACK_ETHERNET_PACKETS == 1 */\r
215 \r
216         BaseType_t xNetworkInterfaceOutput( xNetworkBufferDescriptor_t * const pxNetworkBuffer )\r
217         {\r
218                 xSemaphoreTake( xPCAPMutex, portMAX_DELAY );\r
219                 {\r
220                         iptraceNETWORK_INTERFACE_TRANSMIT();\r
221                         pcap_sendpacket( pxOpenedInterfaceHandle, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );\r
222                 }\r
223                 xSemaphoreGive( xPCAPMutex );\r
224 \r
225                 /* The buffer has been transmitted so can be released. */\r
226                 vNetworkBufferRelease( pxNetworkBuffer );\r
227 \r
228                 return pdPASS;\r
229         }\r
230 \r
231 #endif /* updconfigLOOPBACK_ETHERNET_PACKETS == 1 */\r
232 /*-----------------------------------------------------------*/\r
233 \r
234 static pcap_if_t * prvPrintAvailableNetworkInterfaces( void )\r
235 {\r
236 pcap_if_t * pxAllNetworkInterfaces = NULL, *xInterface;\r
237 long lInterfaceNumber = 1;\r
238 \r
239     if( pcap_findalldevs_ex( PCAP_SRC_IF_STRING, NULL, &pxAllNetworkInterfaces, cErrorBuffer ) == -1 )\r
240     {\r
241         printf( "\r\nCould not obtain a list of network interfaces\r\n%s\r\n", cErrorBuffer );\r
242         pxAllNetworkInterfaces = NULL;\r
243     }\r
244 \r
245         if( pxAllNetworkInterfaces != NULL )\r
246         {\r
247                 /* Print out the list of network interfaces.  The first in the list\r
248                 is interface '1', not interface '0'. */\r
249                 for( xInterface = pxAllNetworkInterfaces; xInterface != NULL; xInterface = xInterface->next )\r
250                 {\r
251                         printf( "%d. %s", lInterfaceNumber, xInterface->name );\r
252 \r
253                         if( xInterface->description != NULL )\r
254                         {\r
255                                 printf( " (%s)\r\n", xInterface->description );\r
256                         }\r
257                         else\r
258                         {\r
259                                 printf( " (No description available)\r\n") ;\r
260                         }\r
261 \r
262                         lInterfaceNumber++;\r
263                 }\r
264         }\r
265 \r
266     if( lInterfaceNumber == 1 )\r
267     {\r
268                 /* The interface number was never incremented, so the above for() loop\r
269                 did not execute meaning no interfaces were found. */\r
270         printf( " \r\nNo network interfaces were found.\r\n" );\r
271         pxAllNetworkInterfaces = NULL;\r
272     }\r
273 \r
274         printf( "\r\nThe interface that will be opened is set by configNETWORK_INTERFACE_TO_USE which should be defined in FreeRTOSConfig.h\r\n" );\r
275         printf( "Attempting to open interface number %d.\r\n", configNETWORK_INTERFACE_TO_USE );\r
276 \r
277     if( ( configNETWORK_INTERFACE_TO_USE < 1L ) || ( configNETWORK_INTERFACE_TO_USE > lInterfaceNumber ) )\r
278     {\r
279         printf("\r\nconfigNETWORK_INTERFACE_TO_USE is not in the valid range.\r\n" );\r
280 \r
281                 if( pxAllNetworkInterfaces != NULL )\r
282                 {\r
283                         /* Free the device list, as no devices are going to be opened. */\r
284                         pcap_freealldevs( pxAllNetworkInterfaces );\r
285                         pxAllNetworkInterfaces = NULL;\r
286                 }\r
287     }\r
288 \r
289         return pxAllNetworkInterfaces;\r
290 }\r
291 /*-----------------------------------------------------------*/\r
292 \r
293 static void prvOpenSelectedNetworkInterface( pcap_if_t *pxAllNetworkInterfaces )\r
294 {\r
295 pcap_if_t *xInterface;\r
296 long x;\r
297 \r
298     /* Walk the list of devices until the selected device is located. */\r
299         xInterface = pxAllNetworkInterfaces;\r
300     for( x = 0L; x < ( configNETWORK_INTERFACE_TO_USE - 1L ); x++ )\r
301         {\r
302                 xInterface = xInterface->next;\r
303         }\r
304 \r
305     /* Open the selected interface. */\r
306         pxOpenedInterfaceHandle = pcap_open(    xInterface->name,               /* The name of the selected interface. */\r
307                                                                                         ipTOTAL_ETHERNET_FRAME_SIZE,    /* The size of the packet to capture. */\r
308                                                                                         PCAP_OPENFLAG_PROMISCUOUS,      /* Open in promiscious mode as the MAC and\r
309                                                                                                                                                 IP address is going to be "simulated", and\r
310                                                                                                                                                 not be the real MAC and IP address.  This allows\r
311                                                                                                                                                 trafic to the simulated IP address to be routed\r
312                                                                                                                                                 to uIP, and trafic to the real IP address to be\r
313                                                                                                                                                 routed to the Windows TCP/IP stack. */\r
314                                                                                         0x00L,                                  /* The read time out. */\r
315                                                                                         NULL,                                   /* No authentication is required as this is\r
316                                                                                                                                                 not a remote capture session. */\r
317                                                                                         cErrorBuffer\r
318                                                                            );\r
319 \r
320     if ( pxOpenedInterfaceHandle == NULL )\r
321     {\r
322         printf( "\r\n%s is not supported by WinPcap and cannot be opened\r\n", xInterface->name );\r
323     }\r
324         else\r
325         {\r
326                 /* Configure the capture filter to allow blocking reads, and to filter\r
327                 out packets that are not of interest to this demo. */\r
328                 prvConfigureCaptureBehaviour();\r
329         }\r
330 \r
331         /* The device list is no longer required. */\r
332         pcap_freealldevs( pxAllNetworkInterfaces );\r
333 }\r
334 /*-----------------------------------------------------------*/\r
335 \r
336 static void prvConfigureCaptureBehaviour( void )\r
337 {\r
338 struct bpf_program xFilterCode;\r
339 const long lMinBytesToCopy = 10L, lBlocking = 1L;\r
340 unsigned long ulNetMask;\r
341 \r
342         /* Unblock a read as soon as anything is received. */\r
343         pcap_setmintocopy( pxOpenedInterfaceHandle, lMinBytesToCopy );\r
344 \r
345         /* Allow blocking. */\r
346         pcap_setnonblock( pxOpenedInterfaceHandle, lBlocking, cErrorBuffer );\r
347 \r
348         /* Set up a filter so only the packets of interest are passed to the IP\r
349         stack.  cErrorBuffer is used for convenience to create the string.  Don't\r
350         confuse this with an error message. *//*_RB_ This should not use the #defined constants. *//*_RB_ Constants should not be used, but passed through a generic network API. */\r
351         sprintf( cErrorBuffer, "broadcast or multicast or ether host %x:%x:%x:%x:%x:%x", configMAC_ADDR0, configMAC_ADDR1, configMAC_ADDR2, configMAC_ADDR3, configMAC_ADDR4, configMAC_ADDR5 );\r
352 \r
353         /*_RB_ Constants should not be used, but passed through a generic network API. */\r
354         ulNetMask = ( configNET_MASK3 << 24UL ) | ( configNET_MASK2 << 16UL ) | ( configNET_MASK1 << 8L ) | configNET_MASK0;\r
355 \r
356         if( pcap_compile(pxOpenedInterfaceHandle, &xFilterCode, cErrorBuffer, 1, ulNetMask ) < 0 )\r
357     {\r
358         printf("\r\nThe packet filter string is invalid\r\n" );\r
359     }\r
360         else\r
361         {\r
362                 if( pcap_setfilter( pxOpenedInterfaceHandle, &xFilterCode ) < 0 )\r
363                 {\r
364                         printf( "\r\nAn error occurred setting the packet filter.\r\n" );\r
365                 }\r
366         }\r
367 \r
368         /* Create a task that simulates an interrupt in a real system.  This will\r
369         block waiting for packets, then send a message to the uIP task when data\r
370         is available. */\r
371         xTaskCreate( prvInterruptSimulatorTask, "MAC_ISR", configMINIMAL_STACK_SIZE, NULL, configMAC_ISR_SIMULATOR_PRIORITY, NULL );\r
372 }\r
373 /*-----------------------------------------------------------*/\r
374 \r
375 static void prvInterruptSimulatorTask( void *pvParameters )\r
376 {\r
377 static struct pcap_pkthdr *pxHeader;\r
378 const uint8_t *pucPacketData;\r
379 long lResult;\r
380 xNetworkBufferDescriptor_t *pxNetworkBuffer;\r
381 xIPStackEvent_t xRxEvent = { eEthernetRxEvent, NULL };\r
382 eFrameProcessingResult_t eResult;\r
383 \r
384         /* Just to kill the compiler warning. */\r
385         ( void ) pvParameters;\r
386 \r
387         for( ;; )\r
388         {\r
389                 /* Get the next packet. */\r
390                 xSemaphoreTake( xPCAPMutex, portMAX_DELAY );\r
391                 {\r
392                         lResult = pcap_next_ex( pxOpenedInterfaceHandle, &pxHeader, &pucPacketData );\r
393                 }\r
394                 xSemaphoreGive( xPCAPMutex );\r
395 \r
396                 if( lResult == 1 )\r
397                 {\r
398                         eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData );\r
399                         if( eResult == eProcessBuffer )\r
400                         {\r
401                                 /* Will the data fit into the frame buffer? */\r
402                                 if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE )\r
403                                 {\r
404                                         /* Obtain a buffer into which the data can be placed.  This\r
405                                         is only an interrupt simulator, not a real interrupt, so it\r
406                                         is ok to call the task level function here.  */\r
407                                         xSemaphoreTake( xPCAPMutex, portMAX_DELAY );\r
408                                         {\r
409                                                 pxNetworkBuffer = pxNetworkBufferGet( pxHeader->len, 0 );\r
410                                         }\r
411                                         xSemaphoreGive( xPCAPMutex );\r
412 \r
413                                         if( pxNetworkBuffer != NULL )\r
414                                         {\r
415                                                 memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len );\r
416                                                 pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len;\r
417                                                 xRxEvent.pvData = ( void * ) pxNetworkBuffer;\r
418 \r
419                                                 /* Data was received and stored.  Send a message to the IP\r
420                                                 task to let it know. */\r
421                                                 if( xQueueSendToBack( xNetworkEventQueue, &xRxEvent, ( TickType_t ) 0 ) == pdFALSE )\r
422                                                 {\r
423                                                         /* The buffer could not be sent to the stack so\r
424                                                         must be released again.  This is only an interrupt\r
425                                                         simulator, not a real interrupt, so it is ok to use\r
426                                                         the task level function here. */\r
427                                                         vNetworkBufferRelease( pxNetworkBuffer );\r
428                                                         iptraceETHERNET_RX_EVENT_LOST();\r
429                                                 }\r
430                                                 else\r
431                                                 {\r
432                                                         iptraceNETWORK_INTERFACE_RECEIVE();\r
433                                                 }\r
434                                         }\r
435                                         else\r
436                                         {\r
437                                                 iptraceETHERNET_RX_EVENT_LOST();\r
438                                         }\r
439                                 }\r
440                                 else\r
441                                 {\r
442                                         /* Log that a packet was dropped because it would have\r
443                                         overflowed the buffer. */\r
444                                 }\r
445                         }\r
446                 }\r
447                 else\r
448                 {\r
449                         /* There is no real way of simulating an interrupt.  Make sure\r
450                         other tasks can run. */\r
451                         vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY );\r
452                 }\r
453         }\r
454 }\r
455 /*-----------------------------------------------------------*/\r
456 \r
457 #if configUSE_STATIC_BUFFERS == 1\r
458         void vNetworkInterfaceAllocateRAMToBuffers( xNetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFERS ] )\r
459         {\r
460         BaseType_t x;\r
461         xNetworkBufferDescriptor_t **ppxStartOfBuffer;\r
462 \r
463                 for( x = 0; x < ipconfigNUM_NETWORK_BUFFERS; x++ )\r
464                 {\r
465                         /* Place a pointer to the network buffer structure at the beginning\r
466                         of the buffer that will be allocated to the structure. */\r
467                         ppxStartOfBuffer = ( xNetworkBufferDescriptor_t ** ) &( ucBuffers[ x ][ 0 ] );\r
468                         *ppxStartOfBuffer = &( pxNetworkBuffers[ x ] );\r
469                         \r
470                         /* Allocate the buffer to the network buffer structure, jumping over\r
471                         the bytes where the pointer to the network buffer is now stored. */\r
472                         pxNetworkBuffers[ x ].pucEthernetBuffer = &( ucBuffers[ x ][ ipBUFFER_PADDING ] );\r
473                 }\r
474         }\r
475 #endif\r
476 /*-----------------------------------------------------------*/\r