1 /*
2 * FreeRTOS+TCP <DEVELOPMENT BRANCH>
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_IPv4.c
30 * @brief Implements the basic functionality for the FreeRTOS+TCP network stack.
31 */
32
33 /* Standard includes. */
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 /* FreeRTOS includes. */
39 #include "FreeRTOS.h"
40
41 /* FreeRTOS+TCP includes. */
42 #include "FreeRTOS_IP.h"
43 #include "FreeRTOS_IPv4.h"
44
45 /* IPv4 multi-cast addresses range from 224.0.0.0.0 to 240.0.0.0. */
46 #define ipFIRST_MULTI_CAST_IPv4 0xE0000000U /**< Lower bound of the IPv4 multicast address. */
47 #define ipLAST_MULTI_CAST_IPv4 0xF0000000U /**< Higher bound of the IPv4 multicast address. */
48
49 /* Just make sure the contents doesn't get compiled if IPv4 is not enabled. */
50 /* *INDENT-OFF* */
51 #if( ipconfigUSE_IPv4 != 0 )
52 /* *INDENT-ON* */
53
54 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 )
55 /* Check IPv4 packet length. */
56 static BaseType_t xCheckIPv4SizeFields( const void * const pvEthernetBuffer,
57 size_t uxBufferLength );
58 #endif /* ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) */
59
60
61 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 )
62
63 /**
64 * @brief Check IPv4 packet length.
65 *
66 * @param[in] pvEthernetBuffer The Ethernet packet received.
67 * @param[in] uxBufferLength The total number of bytes received.
68 *
69 * @return pdPASS when the length fields in the packet OK, pdFAIL when the packet
70 * should be dropped.
71 */
xCheckIPv4SizeFields(const void * const pvEthernetBuffer,size_t uxBufferLength)72 static BaseType_t xCheckIPv4SizeFields( const void * const pvEthernetBuffer,
73 size_t uxBufferLength )
74 {
75 size_t uxLength;
76 UBaseType_t uxIPHeaderLength;
77 uint8_t ucProtocol;
78 uint16_t usLength;
79 uint16_t ucVersionHeaderLength;
80 size_t uxMinimumLength;
81 BaseType_t xResult = pdFAIL;
82
83 /* Map the buffer onto a IP-Packet struct to easily access the
84 * fields of the IP packet. */
85 const IPPacket_t * const pxIPPacket = ( ( const IPPacket_t * const ) pvEthernetBuffer );
86
87 DEBUG_DECLARE_TRACE_VARIABLE( BaseType_t, xLocation, 0 );
88
89 do
90 {
91 /* Check for minimum packet size: Ethernet header and an IP-header, 34 bytes */
92 if( uxBufferLength < sizeof( IPPacket_t ) )
93 {
94 DEBUG_SET_TRACE_VARIABLE( xLocation, 1 );
95 break;
96 }
97
98 ucVersionHeaderLength = pxIPPacket->xIPHeader.ucVersionHeaderLength;
99
100 /* Test if the length of the IP-header is between 20 and 60 bytes,
101 * and if the IP-version is 4. */
102 if( ( ucVersionHeaderLength < ipIPV4_VERSION_HEADER_LENGTH_MIN ) ||
103 ( ucVersionHeaderLength > ipIPV4_VERSION_HEADER_LENGTH_MAX ) )
104 {
105 DEBUG_SET_TRACE_VARIABLE( xLocation, 2 );
106 break;
107 }
108
109 ucVersionHeaderLength = ( uint16_t ) ( ( ucVersionHeaderLength & ( uint8_t ) 0x0FU ) << 2U );
110 uxIPHeaderLength = ( UBaseType_t ) ucVersionHeaderLength;
111
112 /* Check if the complete IP-header is transferred. */
113 if( uxBufferLength < ( ipSIZE_OF_ETH_HEADER + uxIPHeaderLength ) )
114 {
115 DEBUG_SET_TRACE_VARIABLE( xLocation, 3 );
116 break;
117 }
118
119 /* Check if the complete IP-header plus protocol data have been transferred: */
120 usLength = pxIPPacket->xIPHeader.usLength;
121 usLength = FreeRTOS_ntohs( usLength );
122
123 if( uxBufferLength < ( size_t ) ( ipSIZE_OF_ETH_HEADER + ( size_t ) usLength ) )
124 {
125 DEBUG_SET_TRACE_VARIABLE( xLocation, 4 );
126 break;
127 }
128
129 /* Identify the next protocol. */
130 ucProtocol = pxIPPacket->xIPHeader.ucProtocol;
131
132 /* Switch on the Layer 3/4 protocol. */
133 if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
134 {
135 /* Expect at least a complete UDP header. */
136 uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_UDP_HEADER;
137 }
138 else if( ucProtocol == ( uint8_t ) ipPROTOCOL_TCP )
139 {
140 uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_TCP_HEADER;
141 }
142 else if( ( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) ||
143 ( ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
144 {
145 uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_ICMPv4_HEADER;
146 }
147 else
148 {
149 /* Unhandled protocol, other than ICMP, IGMP, UDP, or TCP. */
150 DEBUG_SET_TRACE_VARIABLE( xLocation, 5 );
151 break;
152 }
153
154 if( uxBufferLength < uxMinimumLength )
155 {
156 DEBUG_SET_TRACE_VARIABLE( xLocation, 6 );
157 break;
158 }
159
160 uxLength = ( size_t ) usLength;
161 uxLength -= ( ( uint16_t ) uxIPHeaderLength ); /* normally, minus 20. */
162
163 if( ( uxLength < ( ( size_t ) sizeof( UDPHeader_t ) ) ) ||
164 ( uxLength > ( ( size_t ) ipconfigNETWORK_MTU - ( size_t ) uxIPHeaderLength ) ) )
165 {
166 /* For incoming packets, the length is out of bound: either
167 * too short or too long. For outgoing packets, there is a
168 * serious problem with the format/length. */
169 DEBUG_SET_TRACE_VARIABLE( xLocation, 7 );
170 break;
171 }
172
173 xResult = pdPASS;
174 } while( ipFALSE_BOOL );
175
176 if( xResult != pdPASS )
177 {
178 /* NOP if ipconfigHAS_PRINTF != 1 */
179 FreeRTOS_printf( ( "xCheckIPv4SizeFields: location %ld\n", xLocation ) );
180 }
181
182 return xResult;
183 }
184
185 #endif /* ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) */
186 /*-----------------------------------------------------------*/
187
188 /**
189 * @brief Is the IP address an IPv4 multicast address.
190 *
191 * @param[in] ulIPAddress The IP address being checked.
192 *
193 * @return pdTRUE if the IP address is a multicast address or else, pdFALSE.
194 */
xIsIPv4Multicast(uint32_t ulIPAddress)195 BaseType_t xIsIPv4Multicast( uint32_t ulIPAddress )
196 {
197 BaseType_t xReturn;
198 uint32_t ulIP = FreeRTOS_ntohl( ulIPAddress );
199
200 if( ( ulIP >= ipFIRST_MULTI_CAST_IPv4 ) && ( ulIP < ipLAST_MULTI_CAST_IPv4 ) )
201 {
202 xReturn = pdTRUE;
203 }
204 else
205 {
206 xReturn = pdFALSE;
207 }
208
209 return xReturn;
210 }
211 /*-----------------------------------------------------------*/
212
213 /**
214 * @brief Check whether this IPv4 packet is to be allowed or to be dropped.
215 *
216 * @param[in] pxIPPacket The IP packet under consideration.
217 * @param[in] pxNetworkBuffer The whole network buffer.
218 * @param[in] uxHeaderLength The length of the header.
219 *
220 * @return Whether the packet should be processed or dropped.
221 */
prvAllowIPPacketIPv4(const struct xIP_PACKET * const pxIPPacket,const struct xNETWORK_BUFFER * const pxNetworkBuffer,UBaseType_t uxHeaderLength)222 enum eFrameProcessingResult prvAllowIPPacketIPv4( const struct xIP_PACKET * const pxIPPacket,
223 const struct xNETWORK_BUFFER * const pxNetworkBuffer,
224 UBaseType_t uxHeaderLength )
225 {
226 eFrameProcessingResult_t eReturn = eProcessBuffer;
227
228 #if ( ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 0 ) || ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) )
229 const IPHeader_t * pxIPHeader = &( pxIPPacket->xIPHeader );
230 #else
231
232 /* or else, the parameter won't be used and the function will be optimised
233 * away */
234 ( void ) pxIPPacket;
235 #endif
236
237 #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 0 )
238 {
239 /* In systems with a very small amount of RAM, it might be advantageous
240 * to have incoming messages checked earlier, by the network card driver.
241 * This method may decrease the usage of sparse network buffers. */
242 uint32_t ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress;
243 uint32_t ulSourceIPAddress = pxIPHeader->ulSourceIPAddress;
244
245 /* Ensure that the incoming packet is not fragmented because the stack
246 * doesn't not support IP fragmentation. All but the last fragment coming in will have their
247 * "more fragments" flag set and the last fragment will have a non-zero offset.
248 * We need to drop the packet in either of those cases. */
249 if( ( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_OFFSET_BIT_MASK ) != 0U ) || ( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_FLAGS_MORE_FRAGMENTS ) != 0U ) )
250 {
251 /* Can not handle, fragmented packet. */
252 eReturn = eReleaseBuffer;
253 }
254
255 /* Test if the length of the IP-header is between 20 and 60 bytes,
256 * and if the IP-version is 4. */
257 else if( ( pxIPHeader->ucVersionHeaderLength < ipIPV4_VERSION_HEADER_LENGTH_MIN ) ||
258 ( pxIPHeader->ucVersionHeaderLength > ipIPV4_VERSION_HEADER_LENGTH_MAX ) )
259 {
260 /* Can not handle, unknown or invalid header version. */
261 eReturn = eReleaseBuffer;
262 }
263 else if(
264 ( FreeRTOS_FindEndPointOnIP_IPv4( ulDestinationIPAddress, 4 ) == NULL ) &&
265 ( pxNetworkBuffer->pxEndPoint == NULL ) &&
266 /* Is it an IPv4 broadcast address x.x.x.255 ? */
267 ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xffU ) != 0xffU ) &&
268 ( xIsIPv4Multicast( ulDestinationIPAddress ) == pdFALSE ) &&
269 /* Or (during DHCP negotiation) we have no IP-address yet? */
270 ( FreeRTOS_IsNetworkUp() != pdFALSE ) )
271 {
272 /* Packet is not for this node, release it */
273 eReturn = eReleaseBuffer;
274 }
275 /* Is the source address correct? */
276 else if( ( FreeRTOS_ntohl( ulSourceIPAddress ) & 0xffU ) == 0xffU )
277 {
278 /* The source address cannot be broadcast address. Replying to this
279 * packet may cause network storms. Drop the packet. */
280 eReturn = eReleaseBuffer;
281 }
282 else if( ( memcmp( xBroadcastMACAddress.ucBytes,
283 pxIPPacket->xEthernetHeader.xDestinationAddress.ucBytes,
284 sizeof( MACAddress_t ) ) == 0 ) &&
285 ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xffU ) != 0xffU ) )
286 {
287 /* Ethernet address is a broadcast address, but the IP address is not a
288 * broadcast address. */
289 eReturn = eReleaseBuffer;
290 }
291 else if( memcmp( xBroadcastMACAddress.ucBytes,
292 pxIPPacket->xEthernetHeader.xSourceAddress.ucBytes,
293 sizeof( MACAddress_t ) ) == 0 )
294 {
295 /* Ethernet source is a broadcast address. Drop the packet. */
296 eReturn = eReleaseBuffer;
297 }
298 else if( xIsIPv4Multicast( ulSourceIPAddress ) == pdTRUE )
299 {
300 /* Source is a multicast IP address. Drop the packet in conformity with RFC 1112 section 7.2. */
301 eReturn = eReleaseBuffer;
302 }
303 else
304 {
305 /* Packet is not fragmented, destination is this device, source IP and MAC
306 * addresses are correct. */
307 }
308 }
309 #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */
310
311 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 )
312 {
313 /* Some drivers of NIC's with checksum-offloading will enable the above
314 * define, so that the checksum won't be checked again here */
315 if( eReturn == eProcessBuffer )
316 {
317 const NetworkEndPoint_t * pxEndPoint = FreeRTOS_FindEndPointOnMAC( &( pxIPPacket->xEthernetHeader.xSourceAddress ), NULL );
318
319 /* Do not check the checksum of loop-back messages. */
320 if( pxEndPoint == NULL )
321 {
322 /* Is the IP header checksum correct?
323 *
324 * NOTE: When the checksum of IP header is calculated while not omitting
325 * the checksum field, the resulting value of the checksum always is 0xffff
326 * which is denoted by ipCORRECT_CRC. See this wiki for more information:
327 * https://en.wikipedia.org/wiki/IPv4_header_checksum#Verifying_the_IPv4_header_checksum
328 * and this RFC: https://tools.ietf.org/html/rfc1624#page-4
329 */
330 if( usGenerateChecksum( 0U, ( const uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ( size_t ) uxHeaderLength ) != ipCORRECT_CRC )
331 {
332 /* Check sum in IP-header not correct. */
333 eReturn = eReleaseBuffer;
334 }
335 /* Is the upper-layer checksum (TCP/UDP/ICMP) correct? */
336 else if( usGenerateProtocolChecksum( ( uint8_t * ) ( pxNetworkBuffer->pucEthernetBuffer ), pxNetworkBuffer->xDataLength, pdFALSE ) != ipCORRECT_CRC )
337 {
338 /* Protocol checksum not accepted. */
339 eReturn = eReleaseBuffer;
340 }
341 else
342 {
343 /* The checksum of the received packet is OK. */
344 }
345 }
346 }
347 }
348 #else /* if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) */
349 {
350 if( eReturn == eProcessBuffer )
351 {
352 if( xCheckIPv4SizeFields( pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ) != pdPASS )
353 {
354 /* Some of the length checks were not successful. */
355 eReturn = eReleaseBuffer;
356 }
357 }
358
359 #if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 )
360 {
361 /* Check if this is a UDP packet without a checksum. */
362 if( eReturn == eProcessBuffer )
363 {
364 uint8_t ucProtocol;
365 const ProtocolHeaders_t * pxProtocolHeaders;
366
367 /* ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS is defined as 0,
368 * and so UDP packets carrying a protocol checksum of 0, will
369 * be dropped. */
370 ucProtocol = pxIPPacket->xIPHeader.ucProtocol;
371 /* MISRA Ref 11.3.1 [Misaligned access] */
372 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
373 /* coverity[misra_c_2012_rule_11_3_violation] */
374 pxProtocolHeaders = ( ( ProtocolHeaders_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + ( size_t ) ipSIZE_OF_IPv4_HEADER ] ) );
375
376 /* Identify the next protocol. */
377 if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
378 {
379 if( pxProtocolHeaders->xUDPHeader.usChecksum == ( uint16_t ) 0U )
380 {
381 #if ( ipconfigHAS_PRINTF != 0 )
382 {
383 static BaseType_t xCount = 0;
384
385 /* Exclude this from branch coverage as this is only used for debugging. */
386 if( xCount < 5 ) /* LCOV_EXCL_BR_LINE */
387 {
388 FreeRTOS_printf( ( "prvAllowIPPacket: UDP packet from %xip without CRC dropped\n",
389 FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ) ) );
390 xCount++;
391 }
392 }
393 #endif /* ( ipconfigHAS_PRINTF != 0 ) */
394
395 /* Protocol checksum not accepted. */
396 eReturn = eReleaseBuffer;
397 }
398 }
399 }
400 }
401 #endif /* ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */
402
403 /* to avoid warning unused parameters */
404 ( void ) pxNetworkBuffer;
405 ( void ) uxHeaderLength;
406 }
407 #endif /* ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 */
408
409 return eReturn;
410 }
411
412 /*-----------------------------------------------------------*/
413
414
415 /** @brief Check if the IP-header is carrying options.
416 * @param[in] pxNetworkBuffer the network buffer that contains the packet.
417 *
418 * @return Either 'eProcessBuffer' or 'eReleaseBuffer'
419 */
prvCheckIP4HeaderOptions(struct xNETWORK_BUFFER * const pxNetworkBuffer)420 enum eFrameProcessingResult prvCheckIP4HeaderOptions( struct xNETWORK_BUFFER * const pxNetworkBuffer )
421 {
422 eFrameProcessingResult_t eReturn = eProcessBuffer;
423
424 /* This function is only called for IPv4 packets, with an IP-header
425 * which is larger than 20 bytes. The extra space is used for IP-options.
426 * The options will either be removed, or the packet shall be dropped,
427 * depending on a user define. */
428
429 #if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 )
430 {
431 /* MISRA Ref 11.3.1 [Misaligned access] */
432 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
433 /* coverity[misra_c_2012_rule_11_3_violation] */
434 IPHeader_t * pxIPHeader = ( ( IPHeader_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER ] ) );
435
436 /* All structs of headers expect a IP header size of 20 bytes
437 * IP header options were included, we'll ignore them and cut them out. */
438 size_t uxLength = ( size_t ) pxIPHeader->ucVersionHeaderLength;
439
440 /* Check if the IP headers are acceptable and if it has our destination.
441 * The lowest four bits of 'ucVersionHeaderLength' indicate the IP-header
442 * length in multiples of 4. */
443 size_t uxHeaderLength = ( size_t ) ( ( uxLength & 0x0FU ) << 2 );
444
445 /* Number of bytes contained in IPv4 header options. */
446 const size_t optlen = ( ( size_t ) uxHeaderLength ) - ipSIZE_OF_IPv4_HEADER;
447 /* From: the previous start of UDP/ICMP/TCP data. */
448 const uint8_t * pucSource = ( const uint8_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( EthernetHeader_t ) + uxHeaderLength ] );
449 /* To: the usual start of UDP/ICMP/TCP data at offset 20 (decimal ) from IP header. */
450 uint8_t * pucTarget = ( uint8_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( EthernetHeader_t ) + ipSIZE_OF_IPv4_HEADER ] );
451 /* How many: total length minus the options and the lower headers. */
452 const size_t xMoveLen = pxNetworkBuffer->xDataLength - ( optlen + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_ETH_HEADER );
453
454 ( void ) memmove( pucTarget, pucSource, xMoveLen );
455 pxNetworkBuffer->xDataLength -= optlen;
456 /* Update the total length of the IP packet after removing options. */
457 pxIPHeader->usLength = FreeRTOS_htons( FreeRTOS_ntohs( pxIPHeader->usLength ) - optlen );
458
459 /* Rewrite the Version/IHL byte to indicate that this packet has no IP options. */
460 pxIPHeader->ucVersionHeaderLength = ( uint8_t ) ( ( pxIPHeader->ucVersionHeaderLength & 0xF0U ) | /* High nibble is the version. */
461 ( ( ipSIZE_OF_IPv4_HEADER >> 2 ) & 0x0FU ) );
462 }
463 #else /* if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 ) */
464 {
465 /* 'ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS' is not set, so packets carrying
466 * IP-options will be dropped. */
467 eReturn = eReleaseBuffer;
468 }
469 #endif /* if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 ) */
470
471 return eReturn;
472 }
473 /*-----------------------------------------------------------*/
474
475 /* *INDENT-OFF* */
476 #endif /* ipconfigUSE_IPv4 != 0 ) */
477 /* *INDENT-ON* */
478