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