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