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