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