1 /*
2 * FreeRTOS+TCP V3.1.0
3 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * http://aws.amazon.com/freertos
25 * http://www.FreeRTOS.org
26 */
27
28 /**
29 * @file FreeRTOS_UDP_IP.c
30 * @brief This file has the source code for the UDP-IP functionality of the FreeRTOS+TCP
31 * network stack.
32 */
33
34 /* Standard includes. */
35 #include <stdint.h>
36 #include <stdio.h>
37
38 /* FreeRTOS includes. */
39 #include "FreeRTOS.h"
40 #include "task.h"
41 #include "queue.h"
42 #include "semphr.h"
43 #include "event_groups.h"
44 #include "list.h"
45
46 /* FreeRTOS+TCP includes. */
47 #include "FreeRTOS_IP.h"
48 #include "FreeRTOS_Sockets.h"
49 #include "FreeRTOS_IP_Private.h"
50 #include "FreeRTOS_UDP_IP.h"
51 #include "FreeRTOS_ARP.h"
52 #include "FreeRTOS_DNS.h"
53 #include "FreeRTOS_DHCP.h"
54 #include "FreeRTOS_IP_Utils.h"
55 #include "NetworkInterface.h"
56 #include "NetworkBufferManagement.h"
57
58 #if ( ipconfigUSE_DNS == 1 )
59 #include "FreeRTOS_DNS.h"
60 #endif
61
62 /** @brief The expected IP version and header length coded into the IP header itself. */
63 #define ipIP_VERSION_AND_HEADER_LENGTH_BYTE ( ( uint8_t ) 0x45 )
64
65 /** @brief Part of the Ethernet and IP headers are always constant when sending an IPv4
66 * UDP packet. This array defines the constant parts, allowing this part of the
67 * packet to be filled in using a simple memcpy() instead of individual writes. */
68 /*lint -e708 (Info -- union initialization). */
69 UDPPacketHeader_t xDefaultPartUDPPacketHeader =
70 {
71 /* .ucBytes : */
72 {
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Ethernet source MAC address. */
74 0x08, 0x00, /* Ethernet frame type. */
75 ipIP_VERSION_AND_HEADER_LENGTH_BYTE, /* ucVersionHeaderLength. */
76 0x00, /* ucDifferentiatedServicesCode. */
77 0x00, 0x00, /* usLength. */
78 0x00, 0x00, /* usIdentification. */
79 0x00, 0x00, /* usFragmentOffset. */
80 ipconfigUDP_TIME_TO_LIVE, /* ucTimeToLive */
81 ipPROTOCOL_UDP, /* ucProtocol. */
82 0x00, 0x00, /* usHeaderChecksum. */
83 0x00, 0x00, 0x00, 0x00 /* Source IP address. */
84 }
85 };
86 /*-----------------------------------------------------------*/
87
88 /**
89 * @brief Process the generated UDP packet and do other checks before sending the
90 * packet such as ARP cache check and address resolution.
91 *
92 * @param[in] pxNetworkBuffer: The network buffer carrying the packet.
93 */
vProcessGeneratedUDPPacket(NetworkBufferDescriptor_t * const pxNetworkBuffer)94 void vProcessGeneratedUDPPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer )
95 {
96 UDPPacket_t * pxUDPPacket;
97 IPHeader_t * pxIPHeader;
98 eARPLookupResult_t eReturned;
99 uint32_t ulIPAddress = pxNetworkBuffer->ulIPAddress;
100 size_t uxPayloadSize;
101 /* memcpy() helper variables for MISRA Rule 21.15 compliance*/
102 const void * pvCopySource;
103 void * pvCopyDest;
104
105 /* Map the UDP packet onto the start of the frame. */
106
107 /* MISRA Ref 11.3.1 [Misaligned access] */
108 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
109 /* coverity[misra_c_2012_rule_11_3_violation] */
110 pxUDPPacket = ( ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer );
111
112 #if ipconfigSUPPORT_OUTGOING_PINGS == 1
113 if( pxNetworkBuffer->usPort == ( uint16_t ) ipPACKET_CONTAINS_ICMP_DATA )
114 {
115 uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( ICMPPacket_t );
116 }
117 else
118 #endif
119 {
120 uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t );
121 }
122
123 /* Determine the ARP cache status for the requested IP address. */
124 eReturned = eARPGetCacheEntry( &( ulIPAddress ), &( pxUDPPacket->xEthernetHeader.xDestinationAddress ) );
125
126 if( eReturned != eCantSendPacket )
127 {
128 if( eReturned == eARPCacheHit )
129 {
130 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
131 uint8_t ucSocketOptions;
132 #endif
133 iptraceSENDING_UDP_PACKET( pxNetworkBuffer->ulIPAddress );
134
135 /* Create short cuts to the data within the packet. */
136 pxIPHeader = &( pxUDPPacket->xIPHeader );
137
138 #if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 )
139
140 /* Is it possible that the packet is not actually a UDP packet
141 * after all, but an ICMP packet. */
142 if( pxNetworkBuffer->usPort != ( uint16_t ) ipPACKET_CONTAINS_ICMP_DATA )
143 #endif /* ipconfigSUPPORT_OUTGOING_PINGS */
144 {
145 UDPHeader_t * pxUDPHeader;
146
147 pxUDPHeader = &( pxUDPPacket->xUDPHeader );
148
149 pxUDPHeader->usDestinationPort = pxNetworkBuffer->usPort;
150 pxUDPHeader->usSourcePort = pxNetworkBuffer->usBoundPort;
151 pxUDPHeader->usLength = ( uint16_t ) ( uxPayloadSize + sizeof( UDPHeader_t ) );
152 pxUDPHeader->usLength = FreeRTOS_htons( pxUDPHeader->usLength );
153 pxUDPHeader->usChecksum = 0U;
154 }
155
156 /* memcpy() the constant parts of the header information into
157 * the correct location within the packet. This fills in:
158 * xEthernetHeader.xSourceAddress
159 * xEthernetHeader.usFrameType
160 * xIPHeader.ucVersionHeaderLength
161 * xIPHeader.ucDifferentiatedServicesCode
162 * xIPHeader.usLength
163 * xIPHeader.usIdentification
164 * xIPHeader.usFragmentOffset
165 * xIPHeader.ucTimeToLive
166 * xIPHeader.ucProtocol
167 * and
168 * xIPHeader.usHeaderChecksum
169 */
170
171 /* Save options now, as they will be overwritten by memcpy */
172 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
173 {
174 ucSocketOptions = pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ];
175 }
176 #endif
177
178 /*
179 * Offset the memcpy by the size of a MAC address to start at the packet's
180 * Ethernet header 'source' MAC address; the preceding 'destination' should not be altered.
181 */
182
183 /*
184 * Use helper variables for memcpy() to remain
185 * compliant with MISRA Rule 21.15. These should be
186 * optimized away.
187 */
188 pvCopySource = xDefaultPartUDPPacketHeader.ucBytes;
189 /* The Ethernet source address is at offset 6. */
190 pvCopyDest = &pxNetworkBuffer->pucEthernetBuffer[ sizeof( MACAddress_t ) ];
191 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( xDefaultPartUDPPacketHeader ) );
192
193 #if ipconfigSUPPORT_OUTGOING_PINGS == 1
194 if( pxNetworkBuffer->usPort == ( uint16_t ) ipPACKET_CONTAINS_ICMP_DATA )
195 {
196 pxIPHeader->ucProtocol = ipPROTOCOL_ICMP;
197 pxIPHeader->usLength = ( uint16_t ) ( uxPayloadSize + sizeof( IPHeader_t ) + sizeof( ICMPHeader_t ) );
198 }
199 else
200 #endif /* ipconfigSUPPORT_OUTGOING_PINGS */
201 {
202 pxIPHeader->usLength = ( uint16_t ) ( uxPayloadSize + sizeof( IPHeader_t ) + sizeof( UDPHeader_t ) );
203 }
204
205 pxIPHeader->usLength = FreeRTOS_htons( pxIPHeader->usLength );
206 pxIPHeader->ulDestinationIPAddress = pxNetworkBuffer->ulIPAddress;
207
208 /* The stack doesn't support fragments, so the fragment offset field must always be zero.
209 * The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go.
210 */
211 #if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 )
212 pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT;
213 #else
214 pxIPHeader->usFragmentOffset = 0U;
215 #endif
216
217 #if ( ipconfigUSE_LLMNR == 1 )
218 {
219 /* LLMNR messages are typically used on a LAN and they're
220 * not supposed to cross routers */
221 if( pxNetworkBuffer->ulIPAddress == ipLLMNR_IP_ADDR )
222 {
223 pxIPHeader->ucTimeToLive = 0x01;
224 }
225 }
226 #endif
227
228 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
229 {
230 pxIPHeader->usHeaderChecksum = 0U;
231 pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
232 pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
233
234 if( ( ucSocketOptions & ( uint8_t ) FREERTOS_SO_UDPCKSUM_OUT ) != 0U )
235 {
236 ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxUDPPacket, pxNetworkBuffer->xDataLength, pdTRUE );
237 }
238 else
239 {
240 pxUDPPacket->xUDPHeader.usChecksum = 0U;
241 }
242 }
243 #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */
244 }
245 else if( eReturned == eARPCacheMiss )
246 {
247 /* Add an entry to the ARP table with a null hardware address.
248 * This allows the ARP timer to know that an ARP reply is
249 * outstanding, and perform retransmissions if necessary. */
250 vARPRefreshCacheEntry( NULL, ulIPAddress );
251
252 /* Generate an ARP for the required IP address. */
253 iptracePACKET_DROPPED_TO_GENERATE_ARP( pxNetworkBuffer->ulIPAddress );
254 pxNetworkBuffer->ulIPAddress = ulIPAddress;
255 vARPGenerateRequestPacket( pxNetworkBuffer );
256 }
257 else
258 {
259 /* The lookup indicated that an ARP request has already been
260 * sent out for the queried IP address. */
261 eReturned = eCantSendPacket;
262 }
263 }
264
265 if( eReturned != eCantSendPacket )
266 {
267 /* The network driver is responsible for freeing the network buffer
268 * after the packet has been sent. */
269
270 #if ( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 )
271 {
272 if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES )
273 {
274 BaseType_t xIndex;
275
276 for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ )
277 {
278 pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0U;
279 }
280
281 pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES;
282 }
283 }
284 #endif /* if( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */
285 iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer );
286 ( void ) xNetworkInterfaceOutput( pxNetworkBuffer, pdTRUE );
287 }
288 else
289 {
290 /* The packet can't be sent (DHCP not completed?). Just drop the
291 * packet. */
292 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
293 }
294 }
295 /*-----------------------------------------------------------*/
296
297 /**
298 * @brief Process the received UDP packet.
299 *
300 * @param[in] pxNetworkBuffer: The network buffer carrying the UDP packet.
301 * @param[in] usPort: The port number on which this packet was received.
302 * @param[out] pxIsWaitingForARPResolution: If the packet is awaiting ARP resolution,
303 * this pointer will be set to pdTRUE. pdFALSE otherwise.
304 *
305 * @return pdPASS in case the UDP packet could be processed. Else pdFAIL is returned.
306 */
xProcessReceivedUDPPacket(NetworkBufferDescriptor_t * pxNetworkBuffer,uint16_t usPort,BaseType_t * pxIsWaitingForARPResolution)307 BaseType_t xProcessReceivedUDPPacket( NetworkBufferDescriptor_t * pxNetworkBuffer,
308 uint16_t usPort,
309 BaseType_t * pxIsWaitingForARPResolution )
310 {
311 BaseType_t xReturn = pdPASS;
312 FreeRTOS_Socket_t * pxSocket;
313
314 configASSERT( pxNetworkBuffer != NULL );
315 configASSERT( pxNetworkBuffer->pucEthernetBuffer != NULL );
316
317 /* Map the ethernet buffer to the UDPPacket_t struct for easy access to the fields. */
318
319 /* MISRA Ref 11.3.1 [Misaligned access] */
320 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
321 /* coverity[misra_c_2012_rule_11_3_violation] */
322 const UDPPacket_t * pxUDPPacket = ( ( const UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer );
323
324 /* Caller must check for minimum packet size. */
325 pxSocket = pxUDPSocketLookup( usPort );
326
327 *pxIsWaitingForARPResolution = pdFALSE;
328
329 do
330 {
331 if( pxSocket != NULL )
332 {
333 if( *ipLOCAL_IP_ADDRESS_POINTER != 0U )
334 {
335 if( xCheckRequiresARPResolution( pxNetworkBuffer ) == pdTRUE )
336 {
337 /* Mark this packet as waiting for ARP resolution. */
338 *pxIsWaitingForARPResolution = pdTRUE;
339
340 /* Return a fail to show that the frame will not be processed right now. */
341 xReturn = pdFAIL;
342 break;
343 }
344 else
345 {
346 /* IP address is not on the same subnet, ARP table can be updated.
347 * When refreshing the ARP cache with received UDP packets we must be
348 * careful; hundreds of broadcast messages may pass and if we're not
349 * handling them, no use to fill the ARP cache with those IP addresses.
350 */
351 vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress );
352 }
353 }
354 else
355 {
356 /* During DHCP, IP address is not assigned and therefore ARP verification
357 * is not possible. */
358 }
359
360 #if ( ipconfigUSE_CALLBACKS == 1 )
361 {
362 /* Did the owner of this socket register a reception handler ? */
363 if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xUDP.pxHandleReceive ) )
364 {
365 struct freertos_sockaddr xSourceAddress, destinationAddress;
366 void * pcData = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] );
367 FOnUDPReceive_t xHandler = ( FOnUDPReceive_t ) pxSocket->u.xUDP.pxHandleReceive;
368 xSourceAddress.sin_port = pxNetworkBuffer->usPort;
369 xSourceAddress.sin_addr = pxNetworkBuffer->ulIPAddress;
370 destinationAddress.sin_port = usPort;
371 destinationAddress.sin_addr = pxUDPPacket->xIPHeader.ulDestinationIPAddress;
372
373 /* The value of 'xDataLength' was proven to be at least the size of a UDP packet in prvProcessIPPacket(). */
374 if( xHandler( ( Socket_t ) pxSocket,
375 ( void * ) pcData,
376 ( size_t ) ( pxNetworkBuffer->xDataLength - ipUDP_PAYLOAD_OFFSET_IPv4 ),
377 &( xSourceAddress ),
378 &( destinationAddress ) ) != 0 )
379 {
380 xReturn = pdFAIL; /* xHandler has consumed the data, do not add it to .xWaitingPacketsList'. */
381 }
382 }
383 }
384 #endif /* ipconfigUSE_CALLBACKS */
385
386 #if ( ipconfigUDP_MAX_RX_PACKETS > 0U )
387 {
388 if( xReturn == pdPASS )
389 {
390 if( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) >= pxSocket->u.xUDP.uxMaxPackets )
391 {
392 FreeRTOS_debug_printf( ( "xProcessReceivedUDPPacket: buffer full %ld >= %ld port %u\n",
393 listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) ),
394 pxSocket->u.xUDP.uxMaxPackets, pxSocket->usLocalPort ) );
395 xReturn = pdFAIL; /* we did not consume or release the buffer */
396 }
397 }
398 }
399 #endif /* if ( ipconfigUDP_MAX_RX_PACKETS > 0U ) */
400
401 #if ( ipconfigUSE_CALLBACKS == 1 ) || ( ipconfigUDP_MAX_RX_PACKETS > 0U )
402 if( xReturn == pdPASS ) /*lint !e774: Boolean within 'if' always evaluates to True, depending on configuration. [MISRA 2012 Rule 14.3, required. */
403 #else
404 /* xReturn is still pdPASS. */
405 #endif
406 {
407 vTaskSuspendAll();
408 {
409 taskENTER_CRITICAL();
410 {
411 /* Add the network packet to the list of packets to be
412 * processed by the socket. */
413 vListInsertEnd( &( pxSocket->u.xUDP.xWaitingPacketsList ), &( pxNetworkBuffer->xBufferListItem ) );
414 }
415 taskEXIT_CRITICAL();
416 }
417 ( void ) xTaskResumeAll();
418
419 /* Set the socket's receive event */
420 if( pxSocket->xEventGroup != NULL )
421 {
422 ( void ) xEventGroupSetBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_RECEIVE );
423 }
424
425 #if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
426 {
427 if( ( pxSocket->pxSocketSet != NULL ) && ( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_READ ) ) != 0U ) )
428 {
429 ( void ) xEventGroupSetBits( pxSocket->pxSocketSet->xSelectGroup, ( EventBits_t ) eSELECT_READ );
430 }
431 }
432 #endif
433
434 #if ( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 )
435 {
436 if( pxSocket->pxUserSemaphore != NULL )
437 {
438 ( void ) xSemaphoreGive( pxSocket->pxUserSemaphore );
439 }
440 }
441 #endif
442
443 #if ( ipconfigUSE_DHCP == 1 )
444 {
445 if( xIsDHCPSocket( pxSocket ) != 0 )
446 {
447 ( void ) xSendDHCPEvent();
448 }
449 }
450 #endif
451 }
452 }
453 else
454 {
455 /* There is no socket listening to the target port, but still it might
456 * be for this node. */
457
458 #if ( ipconfigUSE_DNS == 1 ) && ( ipconfigDNS_USE_CALLBACKS == 1 )
459
460 /* A DNS reply, check for the source port. Although the DNS client
461 * does open a UDP socket to send a messages, this socket will be
462 * closed after a short timeout. Messages that come late (after the
463 * socket is closed) will be treated here. */
464 if( FreeRTOS_ntohs( pxUDPPacket->xUDPHeader.usSourcePort ) == ( uint16_t ) ipDNS_PORT )
465 {
466 vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress );
467 xReturn = ( BaseType_t ) ulDNSHandlePacket( pxNetworkBuffer );
468 }
469 else
470 #endif
471
472 #if ( ipconfigUSE_LLMNR == 1 )
473 /* A LLMNR request, check for the destination port. */
474 if( ( usPort == FreeRTOS_ntohs( ipLLMNR_PORT ) ) ||
475 ( pxUDPPacket->xUDPHeader.usSourcePort == FreeRTOS_ntohs( ipLLMNR_PORT ) ) )
476 {
477 vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress );
478 xReturn = ( BaseType_t ) ulDNSHandlePacket( pxNetworkBuffer );
479 }
480 else
481 #endif /* ipconfigUSE_LLMNR */
482
483 #if ( ipconfigUSE_NBNS == 1 )
484 /* a NetBIOS request, check for the destination port */
485 if( ( usPort == FreeRTOS_ntohs( ipNBNS_PORT ) ) ||
486 ( pxUDPPacket->xUDPHeader.usSourcePort == FreeRTOS_ntohs( ipNBNS_PORT ) ) )
487 {
488 vARPRefreshCacheEntry( &( pxUDPPacket->xEthernetHeader.xSourceAddress ), pxUDPPacket->xIPHeader.ulSourceIPAddress );
489 xReturn = ( BaseType_t ) ulNBNSHandlePacket( pxNetworkBuffer );
490 }
491 else
492 #endif /* ipconfigUSE_NBNS */
493 {
494 xReturn = pdFAIL;
495 }
496 }
497 } while( ipFALSE_BOOL );
498
499 return xReturn;
500 }
501 /*-----------------------------------------------------------*/
502