]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_UDP_IP.c
Added +TCP code to main repo.
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / FreeRTOS_UDP_IP.c
1 /*\r
2  * FreeRTOS+TCP Labs Build 160919 (C) 2016 Real Time Engineers ltd.\r
3  * Authors include Hein Tibosch and Richard Barry\r
4  *\r
5  *******************************************************************************\r
6  ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
7  ***                                                                         ***\r
8  ***                                                                         ***\r
9  ***   FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP     ***\r
10  ***   demos have a dependency on FreeRTOS+FAT, which is only in the Labs    ***\r
11  ***   download):                                                            ***\r
12  ***                                                                         ***\r
13  ***   FreeRTOS+TCP is functional and has been used in commercial products   ***\r
14  ***   for some time.  Be aware however that we are still refining its       ***\r
15  ***   design, the source code does not yet quite conform to the strict      ***\r
16  ***   coding and style standards mandated by Real Time Engineers ltd., and  ***\r
17  ***   the documentation and testing is not necessarily complete.            ***\r
18  ***                                                                         ***\r
19  ***   PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE    ***\r
20  ***   URL: http://www.FreeRTOS.org/contact  Active early adopters may, at   ***\r
21  ***   the sole discretion of Real Time Engineers Ltd., be offered versions  ***\r
22  ***   under a license other than that described below.                      ***\r
23  ***                                                                         ***\r
24  ***                                                                         ***\r
25  ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
26  *******************************************************************************\r
27  *\r
28  * FreeRTOS+TCP can be used under two different free open source licenses.  The\r
29  * license that applies is dependent on the processor on which FreeRTOS+TCP is\r
30  * executed, as follows:\r
31  *\r
32  * If FreeRTOS+TCP is executed on one of the processors listed under the Special\r
33  * License Arrangements heading of the FreeRTOS+TCP license information web\r
34  * page, then it can be used under the terms of the FreeRTOS Open Source\r
35  * License.  If FreeRTOS+TCP is used on any other processor, then it can be used\r
36  * under the terms of the GNU General Public License V2.  Links to the relevant\r
37  * licenses follow:\r
38  *\r
39  * The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license\r
40  * The FreeRTOS Open Source License: http://www.FreeRTOS.org/license\r
41  * The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt\r
42  *\r
43  * FreeRTOS+TCP is distributed in the hope that it will be useful.  You cannot\r
44  * use FreeRTOS+TCP unless you agree that you use the software 'as is'.\r
45  * FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied\r
46  * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
47  * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they\r
48  * implied, expressed, or statutory.\r
49  *\r
50  * 1 tab == 4 spaces!\r
51  *\r
52  * http://www.FreeRTOS.org\r
53  * http://www.FreeRTOS.org/plus\r
54  * http://www.FreeRTOS.org/labs\r
55  *\r
56  */\r
57 \r
58 /* Standard includes. */\r
59 #include <stdint.h>\r
60 #include <stdio.h>\r
61 \r
62 /* FreeRTOS includes. */\r
63 #include "FreeRTOS.h"\r
64 #include "task.h"\r
65 #include "queue.h"\r
66 #include "semphr.h"\r
67 \r
68 /* FreeRTOS+TCP includes. */\r
69 #include "FreeRTOS_IP.h"\r
70 #include "FreeRTOS_Sockets.h"\r
71 #include "FreeRTOS_IP_Private.h"\r
72 #include "FreeRTOS_UDP_IP.h"\r
73 #include "FreeRTOS_ARP.h"\r
74 #include "FreeRTOS_DHCP.h"\r
75 #include "NetworkInterface.h"\r
76 #include "NetworkBufferManagement.h"\r
77 \r
78 #if( ipconfigUSE_DNS == 1 )\r
79         #include "FreeRTOS_DNS.h"\r
80 #endif\r
81 \r
82 /* The expected IP version and header length coded into the IP header itself. */\r
83 #define ipIP_VERSION_AND_HEADER_LENGTH_BYTE ( ( uint8_t ) 0x45 )\r
84 \r
85 /* Part of the Ethernet and IP headers are always constant when sending an IPv4\r
86 UDP packet.  This array defines the constant parts, allowing this part of the\r
87 packet to be filled in using a simple memcpy() instead of individual writes. */\r
88 UDPPacketHeader_t xDefaultPartUDPPacketHeader =\r
89 {\r
90         /* .ucBytes : */\r
91         {\r
92                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     /* Ethernet source MAC address. */\r
93                 0x08, 0x00,                                                     /* Ethernet frame type. */\r
94                 ipIP_VERSION_AND_HEADER_LENGTH_BYTE,    /* ucVersionHeaderLength. */\r
95                 0x00,                                                                   /* ucDifferentiatedServicesCode. */\r
96                 0x00, 0x00,                                                     /* usLength. */\r
97                 0x00, 0x00,                                                     /* usIdentification. */\r
98                 0x00, 0x00,                                                     /* usFragmentOffset. */\r
99                 ipconfigUDP_TIME_TO_LIVE,                               /* ucTimeToLive */\r
100                 ipPROTOCOL_UDP,                                                 /* ucProtocol. */\r
101                 0x00, 0x00,                                                     /* usHeaderChecksum. */\r
102                 0x00, 0x00, 0x00, 0x00                                  /* Source IP address. */\r
103         }\r
104 };\r
105 /*-----------------------------------------------------------*/\r
106 \r
107 void vProcessGeneratedUDPPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer )\r
108 {\r
109 UDPPacket_t *pxUDPPacket;\r
110 IPHeader_t *pxIPHeader;\r
111 eARPLookupResult_t eReturned;\r
112 uint32_t ulIPAddress = pxNetworkBuffer->ulIPAddress;\r
113 \r
114         /* Map the UDP packet onto the start of the frame. */\r
115         pxUDPPacket = ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;\r
116 \r
117         /* Determine the ARP cache status for the requested IP address. */\r
118         eReturned = eARPGetCacheEntry( &( ulIPAddress ), &( pxUDPPacket->xEthernetHeader.xDestinationAddress ) );\r
119 \r
120         if( eReturned != eCantSendPacket )\r
121         {\r
122                 if( eReturned == eARPCacheHit )\r
123                 {\r
124                         #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )\r
125                                 uint8_t ucSocketOptions;\r
126                         #endif\r
127                         iptraceSENDING_UDP_PACKET( pxNetworkBuffer->ulIPAddress );\r
128 \r
129                         /* Create short cuts to the data within the packet. */\r
130                         pxIPHeader = &( pxUDPPacket->xIPHeader );\r
131 \r
132                 #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 )\r
133                         /* Is it possible that the packet is not actually a UDP packet\r
134                         after all, but an ICMP packet. */\r
135                         if( pxNetworkBuffer->usPort != ipPACKET_CONTAINS_ICMP_DATA )\r
136                 #endif /* ipconfigSUPPORT_OUTGOING_PINGS */\r
137                         {\r
138                         UDPHeader_t *pxUDPHeader;\r
139 \r
140                                 pxUDPHeader = &( pxUDPPacket->xUDPHeader );\r
141 \r
142                                 pxUDPHeader->usDestinationPort = pxNetworkBuffer->usPort;\r
143                                 pxUDPHeader->usSourcePort = pxNetworkBuffer->usBoundPort;\r
144                                 pxUDPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( UDPHeader_t ) );\r
145                                 pxUDPHeader->usLength = FreeRTOS_htons( pxUDPHeader->usLength );\r
146                                 pxUDPHeader->usChecksum = 0u;\r
147                         }\r
148 \r
149                         /* memcpy() the constant parts of the header information into\r
150                         the     correct location within the packet.  This fills in:\r
151                                 xEthernetHeader.xSourceAddress\r
152                                 xEthernetHeader.usFrameType\r
153                                 xIPHeader.ucVersionHeaderLength\r
154                                 xIPHeader.ucDifferentiatedServicesCode\r
155                                 xIPHeader.usLength\r
156                                 xIPHeader.usIdentification\r
157                                 xIPHeader.usFragmentOffset\r
158                                 xIPHeader.ucTimeToLive\r
159                                 xIPHeader.ucProtocol\r
160                         and\r
161                                 xIPHeader.usHeaderChecksum\r
162                         */\r
163 \r
164                         /* Save options now, as they will be overwritten by memcpy */\r
165                         #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )\r
166                         {\r
167                                 ucSocketOptions = pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ];\r
168                         }\r
169                         #endif\r
170 \r
171                         memcpy( ( void *) &( pxUDPPacket->xEthernetHeader.xSourceAddress ), ( void * ) xDefaultPartUDPPacketHeader.ucBytes, sizeof( xDefaultPartUDPPacketHeader ) );\r
172 \r
173                 #if ipconfigSUPPORT_OUTGOING_PINGS == 1\r
174                         if( pxNetworkBuffer->usPort == ipPACKET_CONTAINS_ICMP_DATA )\r
175                         {\r
176                                 pxIPHeader->ucProtocol = ipPROTOCOL_ICMP;\r
177                                 pxIPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( IPHeader_t ) );\r
178                         }\r
179                         else\r
180                 #endif /* ipconfigSUPPORT_OUTGOING_PINGS */\r
181                         {\r
182                                 pxIPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( IPHeader_t ) + sizeof( UDPHeader_t ) );\r
183                         }\r
184 \r
185                         /* The total transmit size adds on the Ethernet header. */\r
186                         pxNetworkBuffer->xDataLength = pxIPHeader->usLength + sizeof( EthernetHeader_t );\r
187                         pxIPHeader->usLength = FreeRTOS_htons( pxIPHeader->usLength );\r
188                         pxIPHeader->ulDestinationIPAddress = pxNetworkBuffer->ulIPAddress;\r
189 \r
190                         #if( ipconfigUSE_LLMNR == 1 )\r
191                         {\r
192                                 /* LLMNR messages are typically used on a LAN and they're\r
193                                  * not supposed to cross routers */\r
194                                 if( pxNetworkBuffer->ulIPAddress == ipLLMNR_IP_ADDR )\r
195                                 {\r
196                                         pxIPHeader->ucTimeToLive = 0x01;\r
197                                 }\r
198                         }\r
199                         #endif\r
200 \r
201                         #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )\r
202                         {\r
203                                 pxIPHeader->usHeaderChecksum = 0u;\r
204                                 pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0UL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );\r
205                                 pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );\r
206 \r
207                                 if( ( ucSocketOptions & ( uint8_t ) FREERTOS_SO_UDPCKSUM_OUT ) != 0u )\r
208                                 {\r
209                                         usGenerateProtocolChecksum( (uint8_t*)pxUDPPacket, pdTRUE );\r
210                                 }\r
211                                 else\r
212                                 {\r
213                                         pxUDPPacket->xUDPHeader.usChecksum = 0u;\r
214                                 }\r
215                         }\r
216                         #endif\r
217                 }\r
218                 else if( eReturned == eARPCacheMiss )\r
219                 {\r
220                         /* Add an entry to the ARP table with a null hardware address.\r
221                         This allows the ARP timer to know that an ARP reply is\r
222                         outstanding, and perform retransmissions if necessary. */\r
223                         vARPRefreshCacheEntry( NULL, ulIPAddress );\r
224 \r
225                         /* Generate an ARP for the required IP address. */\r
226                         iptracePACKET_DROPPED_TO_GENERATE_ARP( pxNetworkBuffer->ulIPAddress );\r
227                         pxNetworkBuffer->ulIPAddress = ulIPAddress;\r
228                         vARPGenerateRequestPacket( pxNetworkBuffer );\r
229                 }\r
230                 else\r
231                 {\r
232                         /* The lookup indicated that an ARP request has already been\r
233                         sent out for the queried IP address. */\r
234                         eReturned = eCantSendPacket;\r
235                 }\r
236         }\r
237 \r
238         if( eReturned != eCantSendPacket )\r
239         {\r
240                 /* The network driver is responsible for freeing the network buffer\r
241                 after the packet has been sent. */\r
242 \r
243                 #if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES )\r
244                 {\r
245                         if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES )\r
246                         {\r
247                         BaseType_t xIndex;\r
248 \r
249                                 for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ )\r
250                                 {\r
251                                         pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0u;\r
252                                 }\r
253                                 pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES;\r
254                         }\r
255                 }\r
256                 #endif\r
257 \r
258                 xNetworkInterfaceOutput( pxNetworkBuffer, pdTRUE );\r
259         }\r
260         else\r
261         {\r
262                 /* The packet can't be sent (DHCP not completed?).  Just drop the\r
263                 packet. */\r
264                 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );\r
265         }\r
266 }\r
267 /*-----------------------------------------------------------*/\r
268 \r
269 BaseType_t xProcessReceivedUDPPacket( NetworkBufferDescriptor_t *pxNetworkBuffer, uint16_t usPort )\r
270 {\r
271 BaseType_t xReturn = pdPASS;\r
272 FreeRTOS_Socket_t *pxSocket;\r
273 \r
274 UDPPacket_t *pxUDPPacket = (UDPPacket_t *) pxNetworkBuffer->pucEthernetBuffer;\r
275 \r
276         pxSocket = pxUDPSocketLookup( usPort );\r
277 \r
278         if( pxSocket )\r
279         {\r
280 \r
281                 /* When refreshing the ARP cache with received UDP packets we must be\r
282                 careful;  hundreds of broadcast messages may pass and if we're not\r
283                 handling them, no use to fill the ARP cache with those IP addresses. */\r
284                 vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress );\r
285 \r
286                 #if( ipconfigUSE_CALLBACKS == 1 )\r
287                 {\r
288                         /* Did the owner of this socket register a reception handler ? */\r
289                         if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xUDP.pxHandleReceive ) )\r
290                         {\r
291                                 struct freertos_sockaddr xSourceAddress, destinationAddress;\r
292                                 void *pcData = ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] );\r
293                                 FOnUDPReceive_t xHandler = ( FOnUDPReceive_t ) pxSocket->u.xUDP.pxHandleReceive;\r
294                                 xSourceAddress.sin_port = pxNetworkBuffer->usPort;\r
295                                 xSourceAddress.sin_addr = pxNetworkBuffer->ulIPAddress;\r
296                                 destinationAddress.sin_port = usPort;\r
297                                 destinationAddress.sin_addr = pxUDPPacket->xIPHeader.ulDestinationIPAddress;\r
298 \r
299                                 if( xHandler( ( Socket_t * ) pxSocket, ( void* ) pcData, ( size_t ) pxNetworkBuffer->xDataLength,\r
300                                         &xSourceAddress, &destinationAddress ) != pdFALSE )\r
301                                 {\r
302                                         xReturn = pdFAIL; /* xHandler has consumed the data, do not add it to .xWaitingPacketsList'. */\r
303                                 }\r
304                         }\r
305                 }\r
306                 #endif /* ipconfigUSE_CALLBACKS */\r
307 \r
308                 #if( ipconfigUDP_MAX_RX_PACKETS > 0 )\r
309                 {\r
310                         if( xReturn == pdPASS )\r
311                         {\r
312                                 if ( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) >= pxSocket->u.xUDP.uxMaxPackets )\r
313                                 {\r
314                                         FreeRTOS_debug_printf( ( "xProcessReceivedUDPPacket: buffer full %ld >= %ld port %u\n",\r
315                                                 listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ),\r
316                                                 pxSocket->u.xUDP.uxMaxPackets, pxSocket->usLocalPort ) );\r
317                                         xReturn = pdFAIL; /* we did not consume or release the buffer */\r
318                                 }\r
319                         }\r
320                 }\r
321                 #endif\r
322 \r
323                 if( xReturn == pdPASS )\r
324                 {\r
325                         vTaskSuspendAll();\r
326                         {\r
327                                 if( xReturn == pdPASS )\r
328                                 {\r
329                                         taskENTER_CRITICAL();\r
330                                         {\r
331                                                 /* Add the network packet to the list of packets to be\r
332                                                 processed by the socket. */\r
333                                                 vListInsertEnd( &( pxSocket->u.xUDP.xWaitingPacketsList ), &( pxNetworkBuffer->xBufferListItem ) );\r
334                                         }\r
335                                         taskEXIT_CRITICAL();\r
336                                 }\r
337                         }\r
338                         xTaskResumeAll();\r
339 \r
340                         /* Set the socket's receive event */\r
341                         if( pxSocket->xEventGroup != NULL )\r
342                         {\r
343                                 xEventGroupSetBits( pxSocket->xEventGroup, eSOCKET_RECEIVE );\r
344                         }\r
345 \r
346                         #if( ipconfigSUPPORT_SELECT_FUNCTION == 1 )\r
347                         {\r
348                                 if( ( pxSocket->pxSocketSet != NULL ) && ( ( pxSocket->xSelectBits & eSELECT_READ ) != 0 ) )\r
349                                 {\r
350                                         xEventGroupSetBits( pxSocket->pxSocketSet->xSelectGroup, eSELECT_READ );\r
351                                 }\r
352                         }\r
353                         #endif\r
354 \r
355                         #if( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 )\r
356                         {\r
357                                 if( pxSocket->pxUserSemaphore != NULL )\r
358                                 {\r
359                                         xSemaphoreGive( pxSocket->pxUserSemaphore );\r
360                                 }\r
361                         }\r
362                         #endif\r
363 \r
364                         #if( ipconfigUSE_DHCP == 1 )\r
365                         {\r
366                                 if( xIsDHCPSocket( pxSocket ) )\r
367                                 {\r
368                                         xSendEventToIPTask( eDHCPEvent );\r
369                                 }\r
370                         }\r
371                         #endif\r
372                 }\r
373         }\r
374         else\r
375         {\r
376                 /* There is no socket listening to the target port, but still it might\r
377                 be for this node. */\r
378 \r
379                 #if( ipconfigUSE_DNS == 1 )\r
380                         /* A DNS reply, check for the source port.  Although the DNS client\r
381                         does open a UDP socket to send a messages, this socket will be\r
382                         closed after a short timeout.  Messages that come late (after the\r
383                         socket is closed) will be treated here. */\r
384                         if( FreeRTOS_ntohs( pxUDPPacket->xUDPHeader.usSourcePort ) == ipDNS_PORT )\r
385                         {\r
386                                 vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress );\r
387                                 xReturn = ( BaseType_t )ulDNSHandlePacket( pxNetworkBuffer );\r
388                         }\r
389                         else\r
390                 #endif\r
391 \r
392                 #if( ipconfigUSE_LLMNR == 1 )\r
393                         /* A LLMNR request, check for the destination port. */\r
394                         if( ( usPort == FreeRTOS_ntohs( ipLLMNR_PORT ) ) ||\r
395                                 ( pxUDPPacket->xUDPHeader.usSourcePort == FreeRTOS_ntohs( ipLLMNR_PORT ) ) )\r
396                         {\r
397                                 vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress );\r
398                                 xReturn = ( BaseType_t )ulDNSHandlePacket( pxNetworkBuffer );\r
399                         }\r
400                         else\r
401                 #endif /* ipconfigUSE_LLMNR */\r
402 \r
403                 #if( ipconfigUSE_NBNS == 1 )\r
404                         /* a NetBIOS request, check for the destination port */\r
405                         if( ( usPort == FreeRTOS_ntohs( ipNBNS_PORT ) ) ||\r
406                                 ( pxUDPPacket->xUDPHeader.usSourcePort == FreeRTOS_ntohs( ipNBNS_PORT ) ) )\r
407                         {\r
408                                 vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress );\r
409                                 xReturn = ( BaseType_t )ulNBNSHandlePacket( pxNetworkBuffer );\r
410                         }\r
411                         else\r
412                 #endif /* ipconfigUSE_NBNS */\r
413                 {\r
414                         xReturn = pdFAIL;\r
415                 }\r
416         }\r
417 \r
418         return xReturn;\r
419 }\r
420 /*-----------------------------------------------------------*/\r