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_IP_Utils.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 #include "task.h"
41 #include "queue.h"
42 #include "semphr.h"
43
44 /* FreeRTOS+TCP includes. */
45 #include "FreeRTOS_IP.h"
46 #include "FreeRTOS_IP_Utils.h"
47 #include "FreeRTOS_IP_Timers.h"
48 #include "FreeRTOS_Sockets.h"
49 #include "FreeRTOS_IP_Private.h"
50 #include "FreeRTOS_ARP.h"
51 #include "FreeRTOS_UDP_IP.h"
52 #include "FreeRTOS_DHCP.h"
53 #include "NetworkInterface.h"
54 #include "NetworkBufferManagement.h"
55 #include "FreeRTOS_DNS.h"
56
57 /* Used to ensure the structure packing is having the desired effect. The
58 * 'volatile' is used to prevent compiler warnings about comparing a constant with
59 * a constant. */
60 #ifndef _lint
61 #define ipEXPECTED_EthernetHeader_t_SIZE ( ( size_t ) 14 ) /**< Ethernet Header size in bytes. */
62 #define ipEXPECTED_ARPHeader_t_SIZE ( ( size_t ) 28 ) /**< ARP header size in bytes. */
63 #define ipEXPECTED_IPHeader_t_SIZE ( ( size_t ) 20 ) /**< IP header size in bytes. */
64 #define ipEXPECTED_IGMPHeader_t_SIZE ( ( size_t ) 8 ) /**< IGMP header size in bytes. */
65 #define ipEXPECTED_ICMPHeader_t_SIZE ( ( size_t ) 8 ) /**< ICMP header size in bytes. */
66 #define ipEXPECTED_UDPHeader_t_SIZE ( ( size_t ) 8 ) /**< UDP header size in bytes. */
67 #define ipEXPECTED_TCPHeader_t_SIZE ( ( size_t ) 20 ) /**< TCP header size in bytes. */
68 #endif
69
70 /** @brief Time delay between repeated attempts to initialise the network hardware. */
71 #ifndef ipINITIALISATION_RETRY_DELAY
72 #define ipINITIALISATION_RETRY_DELAY ( pdMS_TO_TICKS( 3000U ) )
73 #endif
74
75 #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
76 /* used for unit testing */
77
78 /* MISRA Ref 8.9.1 [File scoped variables] */
79 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
80 /* coverity[misra_c_2012_rule_8_9_violation] */
81 /* coverity[single_use] */
82 static BaseType_t xCallEventHook = pdFALSE;
83 #endif
84
85 #if ( ipconfigHAS_PRINTF != 0 )
86 /** @brief Last value of minimum buffer count. */
87 static UBaseType_t uxLastMinBufferCount = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS;
88
89 /** @brief Last value of minimum size. Used in printing resource stats. */
90 static size_t uxMinLastSize = 0u;
91 #endif
92
93 #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) && ( ipconfigHAS_PRINTF != 0 )
94 static UBaseType_t uxLastMinQueueSpace = 0;
95 #endif
96
97 /**
98 * Used in checksum calculation.
99 */
100 typedef union _xUnion32
101 {
102 uint32_t u32; /**< The 32-bit member of the union. */
103 uint16_t u16[ 2 ]; /**< The array of 2 16-bit members of the union. */
104 uint8_t u8[ 4 ]; /**< The array of 4 8-bit members of the union. */
105 } xUnion32;
106
107 /**
108 * Used in checksum calculation.
109 */
110 typedef union _xUnionPtr
111 {
112 const uint32_t * u32ptr; /**< The pointer member to a 32-bit variable. */
113 const uint16_t * u16ptr; /**< The pointer member to a 16-bit variable. */
114 const uint8_t * u8ptr; /**< The pointer member to an 8-bit variable. */
115 } xUnionPtr;
116
117 /*
118 * Returns the network buffer descriptor that owns a given packet buffer.
119 */
120 static NetworkBufferDescriptor_t * prvPacketBuffer_to_NetworkBuffer( const void * pvBuffer,
121 size_t uxOffset );
122
123 #if ( ipconfigUSE_DHCP != 0 )
124
125 /**
126 * @brief Create a DHCP event.
127 *
128 * @return pdPASS or pdFAIL, depending on whether xSendEventStructToIPTask()
129 * succeeded.
130 */
xSendDHCPEvent(void)131 BaseType_t xSendDHCPEvent( void )
132 {
133 IPStackEvent_t xEventMessage;
134 const TickType_t uxDontBlock = 0U;
135 uintptr_t uxOption = ( uintptr_t ) eGetDHCPState();
136
137 xEventMessage.eEventType = eDHCPEvent;
138
139 /* MISRA Ref 11.6.1 [DHCP events and conversion to void] */
140 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */
141 /* coverity[misra_c_2012_rule_11_6_violation] */
142 xEventMessage.pvData = ( void * ) uxOption;
143
144 return xSendEventStructToIPTask( &xEventMessage, uxDontBlock );
145 }
146 /*-----------------------------------------------------------*/
147 #endif /* ( ipconfigUSE_DHCP != 0 ) */
148
149 /**
150 * @brief Set multicast MAC address.
151 *
152 * @param[in] ulIPAddress: IP address.
153 * @param[out] pxMACAddress: Pointer to MAC address.
154 */
vSetMultiCastIPv4MacAddress(uint32_t ulIPAddress,MACAddress_t * pxMACAddress)155 void vSetMultiCastIPv4MacAddress( uint32_t ulIPAddress,
156 MACAddress_t * pxMACAddress )
157 {
158 uint32_t ulIP = FreeRTOS_ntohl( ulIPAddress );
159
160 pxMACAddress->ucBytes[ 0 ] = ( uint8_t ) 0x01U;
161 pxMACAddress->ucBytes[ 1 ] = ( uint8_t ) 0x00U;
162 pxMACAddress->ucBytes[ 2 ] = ( uint8_t ) 0x5EU;
163 pxMACAddress->ucBytes[ 3 ] = ( uint8_t ) ( ( ulIP >> 16 ) & 0x7fU ); /* Use 7 bits. */
164 pxMACAddress->ucBytes[ 4 ] = ( uint8_t ) ( ( ulIP >> 8 ) & 0xffU ); /* Use 8 bits. */
165 pxMACAddress->ucBytes[ 5 ] = ( uint8_t ) ( ( ulIP ) & 0xffU ); /* Use 8 bits. */
166 }
167 /*-----------------------------------------------------------*/
168
169
170 /**
171 * @brief Duplicate the given network buffer descriptor with a modified length.
172 *
173 * @param[in] pxNetworkBuffer: The network buffer to be duplicated.
174 * @param[in] uxNewLength: The length for the new buffer.
175 *
176 * @return If properly duplicated, then the duplicate network buffer or else, NULL.
177 */
pxDuplicateNetworkBufferWithDescriptor(const NetworkBufferDescriptor_t * const pxNetworkBuffer,size_t uxNewLength)178 NetworkBufferDescriptor_t * pxDuplicateNetworkBufferWithDescriptor( const NetworkBufferDescriptor_t * const pxNetworkBuffer,
179 size_t uxNewLength )
180 {
181 NetworkBufferDescriptor_t * pxNewBuffer;
182 size_t uxLengthToCopy = uxNewLength;
183
184 /* This function is only used when 'ipconfigZERO_COPY_TX_DRIVER' is set to 1.
185 * The transmit routine wants to have ownership of the network buffer
186 * descriptor, because it will pass the buffer straight to DMA. */
187 pxNewBuffer = pxGetNetworkBufferWithDescriptor( uxNewLength, ( TickType_t ) 0 );
188
189 if( pxNewBuffer != NULL )
190 {
191 /* Get the minimum of both values to copy the data. */
192 if( uxLengthToCopy > pxNetworkBuffer->xDataLength )
193 {
194 uxLengthToCopy = pxNetworkBuffer->xDataLength;
195 }
196
197 /* Set the actual packet size in case a bigger buffer than requested
198 * was returned. */
199 pxNewBuffer->xDataLength = uxNewLength;
200
201 /* Copy the original packet information. */
202 pxNewBuffer->ulIPAddress = pxNetworkBuffer->ulIPAddress;
203 pxNewBuffer->usPort = pxNetworkBuffer->usPort;
204 pxNewBuffer->usBoundPort = pxNetworkBuffer->usBoundPort;
205 ( void ) memcpy( pxNewBuffer->pucEthernetBuffer, pxNetworkBuffer->pucEthernetBuffer, uxLengthToCopy );
206 }
207
208 return pxNewBuffer;
209 }
210 /*-----------------------------------------------------------*/
211
212 /**
213 * @brief Get the network buffer descriptor from the packet buffer.
214 *
215 * @param[in] pvBuffer: The pointer to packet buffer.
216 * @param[in] uxOffset: Additional offset (such as the packet length of UDP packet etc.).
217 *
218 * @return The network buffer descriptor if the alignment is correct. Else a NULL is returned.
219 */
prvPacketBuffer_to_NetworkBuffer(const void * pvBuffer,size_t uxOffset)220 static NetworkBufferDescriptor_t * prvPacketBuffer_to_NetworkBuffer( const void * pvBuffer,
221 size_t uxOffset )
222 {
223 uintptr_t uxBuffer;
224 NetworkBufferDescriptor_t * pxResult;
225
226 if( pvBuffer == NULL )
227 {
228 pxResult = NULL;
229 }
230 else
231 {
232 /* Obtain the network buffer from the zero copy pointer. */
233
234 /* MISRA Ref 11.6.2 [Pointer arithmetic and hidden pointer] */
235 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */
236 /* coverity[misra_c_2012_rule_11_6_violation] */
237 uxBuffer = ( uintptr_t ) pvBuffer;
238
239 /* The input here is a pointer to a packet buffer plus some offset. Subtract
240 * this offset, and also the size of the header in the network buffer, usually
241 * 8 + 2 bytes. */
242 uxBuffer -= ( uxOffset + ipBUFFER_PADDING );
243
244 /* Here a pointer was placed to the network descriptor. As a
245 * pointer is dereferenced, make sure it is well aligned. */
246 if( ( uxBuffer & ( ( ( uintptr_t ) sizeof( uxBuffer ) ) - 1U ) ) == ( uintptr_t ) 0U )
247 {
248 /* MISRA Ref 11.4.2 [Validation of pointer alignment] */
249 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
250 /* coverity[misra_c_2012_rule_11_4_violation] */
251 pxResult = *( ( NetworkBufferDescriptor_t ** ) uxBuffer );
252 }
253 else
254 {
255 pxResult = NULL;
256 }
257 }
258
259 return pxResult;
260 }
261 /*-----------------------------------------------------------*/
262
263 #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 )
264
265 /**
266 * @brief Get the network buffer from the packet buffer.
267 *
268 * @param[in] pvBuffer: Pointer to the packet buffer.
269 *
270 * @return The network buffer if the alignment is correct. Else a NULL is returned.
271 */
pxPacketBuffer_to_NetworkBuffer(const void * pvBuffer)272 NetworkBufferDescriptor_t * pxPacketBuffer_to_NetworkBuffer( const void * pvBuffer )
273 {
274 return prvPacketBuffer_to_NetworkBuffer( pvBuffer, 0U );
275 }
276
277 #endif /* ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */
278 /*-----------------------------------------------------------*/
279
280 /**
281 * @brief Get the network buffer from the UDP Payload buffer.
282 *
283 * @param[in] pvBuffer: Pointer to the UDP payload buffer.
284 *
285 * @return The network buffer if the alignment is correct. Else a NULL is returned.
286 */
pxUDPPayloadBuffer_to_NetworkBuffer(const void * pvBuffer)287 NetworkBufferDescriptor_t * pxUDPPayloadBuffer_to_NetworkBuffer( const void * pvBuffer )
288 {
289 return prvPacketBuffer_to_NetworkBuffer( pvBuffer, sizeof( UDPPacket_t ) );
290 }
291 /*-----------------------------------------------------------*/
292
293 /**
294 * @brief Function to check whether the current context belongs to
295 * the IP-task.
296 *
297 * @return If the current context belongs to the IP-task, then pdTRUE is
298 * returned. Else pdFALSE is returned.
299 *
300 * @note Very important: the IP-task is not allowed to call its own API's,
301 * because it would easily get into a dead-lock.
302 */
xIsCallingFromIPTask(void)303 BaseType_t xIsCallingFromIPTask( void )
304 {
305 BaseType_t xReturn;
306 const struct tskTaskControlBlock * const xCurrentHandle = xTaskGetCurrentTaskHandle();
307 const struct tskTaskControlBlock * const xCurrentIPTaskHandle = FreeRTOS_GetIPTaskHandle();
308
309 if( xCurrentHandle == xCurrentIPTaskHandle )
310 {
311 xReturn = pdTRUE;
312 }
313 else
314 {
315 xReturn = pdFALSE;
316 }
317
318 return xReturn;
319 }
320 /*-----------------------------------------------------------*/
321
322 /**
323 * @brief Process a 'Network down' event and complete required processing.
324 */
325 /* MISRA Ref 8.9.1 [File scoped variables] */
326 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
327 /* coverity[misra_c_2012_rule_8_9_violation] */
328 /* coverity[single_use] */
prvProcessNetworkDownEvent(void)329 void prvProcessNetworkDownEvent( void )
330 {
331 /* Stop the ARP timer while there is no network. */
332 vIPSetARPTimerEnableState( pdFALSE );
333
334 #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
335 {
336 /* The first network down event is generated by the IP stack itself to
337 * initialise the network hardware, so do not call the network down event
338 * the first time through. */
339 if( xCallEventHook == pdTRUE )
340 {
341 vApplicationIPNetworkEventHook( eNetworkDown );
342 }
343
344 xCallEventHook = pdTRUE;
345 }
346 #endif /* if ipconfigUSE_NETWORK_EVENT_HOOK == 1 */
347
348 /* Per the ARP Cache Validation section of https://tools.ietf.org/html/rfc1122,
349 * treat network down as a "delivery problem" and flush the ARP cache for this
350 * interface. */
351 FreeRTOS_ClearARP();
352
353 /* The network has been disconnected (or is being initialised for the first
354 * time). Perform whatever hardware processing is necessary to bring it up
355 * again, or wait for it to be available again. This is hardware dependent. */
356 if( xNetworkInterfaceInitialise() != pdPASS )
357 {
358 /* Ideally the network interface initialisation function will only
359 * return when the network is available. In case this is not the case,
360 * wait a while before retrying the initialisation. */
361 vTaskDelay( ipINITIALISATION_RETRY_DELAY );
362 FreeRTOS_NetworkDown();
363 }
364 else
365 {
366 /* Set remaining time to 0 so it will become active immediately. */
367 #if ipconfigUSE_DHCP == 1
368 {
369 /* The network is not up until DHCP has completed. */
370 vDHCPProcess( pdTRUE, eInitialWait );
371 }
372 #else
373 {
374 /* Perform any necessary 'network up' processing. */
375 vIPNetworkUpCalls();
376 }
377 #endif
378 }
379 }
380 /*-----------------------------------------------------------*/
381
382 /**
383 * @brief Check the values of configuration options and assert on it. Also verify that the IP-task
384 * has not already been initialized.
385 */
vPreCheckConfigs(void)386 void vPreCheckConfigs( void )
387 {
388 /* This function should only be called once. */
389 configASSERT( xIPIsNetworkTaskReady() == pdFALSE );
390 configASSERT( xNetworkEventQueue == NULL );
391 configASSERT( FreeRTOS_GetIPTaskHandle() == NULL );
392
393 #if ( configASSERT_DEFINED == 1 )
394 {
395 volatile size_t uxSize = sizeof( uintptr_t );
396
397 if( uxSize == 8U )
398 {
399 /* This is a 64-bit platform, make sure there is enough space in
400 * pucEthernetBuffer to store a pointer. */
401 configASSERT( ipconfigBUFFER_PADDING >= 14 );
402 /* But it must have this strange alignment: */
403 configASSERT( ( ( ( ipconfigBUFFER_PADDING ) + 2 ) % 4 ) == 0 );
404 }
405
406 /* LCOV_EXCL_BR_START */
407 uxSize = ipconfigNETWORK_MTU;
408 /* Check if MTU is big enough. */
409 configASSERT( uxSize >= ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + ipconfigTCP_MSS ) );
410
411 uxSize = sizeof( EthernetHeader_t );
412 /* Check structure packing is correct. */
413 configASSERT( uxSize == ipEXPECTED_EthernetHeader_t_SIZE );
414
415 uxSize = sizeof( ARPHeader_t );
416 configASSERT( uxSize == ipEXPECTED_ARPHeader_t_SIZE );
417
418 uxSize = sizeof( IPHeader_t );
419 configASSERT( uxSize == ipEXPECTED_IPHeader_t_SIZE );
420
421 uxSize = sizeof( ICMPHeader_t );
422 configASSERT( uxSize == ipEXPECTED_ICMPHeader_t_SIZE );
423
424 uxSize = sizeof( UDPHeader_t );
425 configASSERT( uxSize == ipEXPECTED_UDPHeader_t_SIZE );
426 /* LCOV_EXCL_BR_STOP */
427 }
428 #endif /* if ( configASSERT_DEFINED == 1 ) */
429 }
430
431 /**
432 * @brief Generate or check the protocol checksum of the data sent in the first parameter.
433 * At the same time, the length of the packet and the length of the different layers
434 * will be checked.
435 *
436 * @param[in] pucEthernetBuffer: The Ethernet buffer for which the checksum is to be calculated
437 * or checked.
438 * @param[in] uxBufferLength: the total number of bytes received, or the number of bytes written
439 * in the packet buffer.
440 * @param[in] xOutgoingPacket: Whether this is an outgoing packet or not.
441 *
442 * @return When xOutgoingPacket is false: the error code can be either: ipINVALID_LENGTH,
443 * ipUNHANDLED_PROTOCOL, ipWRONG_CRC, or ipCORRECT_CRC.
444 * When xOutgoingPacket is true: either ipINVALID_LENGTH, ipUNHANDLED_PROTOCOL,
445 * or ipCORRECT_CRC.
446 */
usGenerateProtocolChecksum(uint8_t * pucEthernetBuffer,size_t uxBufferLength,BaseType_t xOutgoingPacket)447 uint16_t usGenerateProtocolChecksum( uint8_t * pucEthernetBuffer,
448 size_t uxBufferLength,
449 BaseType_t xOutgoingPacket )
450 {
451 uint32_t ulLength;
452 uint16_t usChecksum; /* The checksum as calculated. */
453 uint16_t usChecksumFound = 0U; /* The checksum as found in the incoming packet. */
454 const IPPacket_t * pxIPPacket;
455 UBaseType_t uxIPHeaderLength;
456 ProtocolPacket_t * pxProtPack;
457 uint8_t ucProtocol;
458
459 #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
460 const char * pcType;
461 #endif
462 uint16_t usLength;
463 uint16_t ucVersionHeaderLength;
464 DEBUG_DECLARE_TRACE_VARIABLE( BaseType_t, xLocation, 0 );
465
466 /* Introduce a do-while loop to allow use of break statements.
467 * Note: MISRA prohibits use of 'goto', thus replaced with breaks. */
468 do
469 {
470 /* Check for minimum packet size. */
471 if( uxBufferLength < sizeof( IPPacket_t ) )
472 {
473 usChecksum = ipINVALID_LENGTH;
474 DEBUG_SET_TRACE_VARIABLE( xLocation, 1 );
475 break;
476 }
477
478 /* Parse the packet length. */
479
480 /* MISRA Ref 11.3.1 [Misaligned access] */
481 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
482 /* coverity[misra_c_2012_rule_11_3_violation] */
483 pxIPPacket = ( ( const IPPacket_t * ) pucEthernetBuffer );
484
485 /* Per https://tools.ietf.org/html/rfc791, the four-bit Internet Header
486 * Length field contains the length of the internet header in 32-bit words. */
487 ucVersionHeaderLength = pxIPPacket->xIPHeader.ucVersionHeaderLength;
488 ucVersionHeaderLength = ( ucVersionHeaderLength & ( uint8_t ) 0x0FU ) << 2;
489 uxIPHeaderLength = ( UBaseType_t ) ucVersionHeaderLength;
490
491 /* Check for minimum packet size. */
492 if( uxBufferLength < ( sizeof( IPPacket_t ) + ( uxIPHeaderLength - ipSIZE_OF_IPv4_HEADER ) ) )
493 {
494 usChecksum = ipINVALID_LENGTH;
495 DEBUG_SET_TRACE_VARIABLE( xLocation, 2 );
496 break;
497 }
498
499 usLength = pxIPPacket->xIPHeader.usLength;
500 usLength = FreeRTOS_ntohs( usLength );
501
502 if( usLength < uxIPHeaderLength )
503 {
504 usChecksum = ipINVALID_LENGTH;
505 DEBUG_SET_TRACE_VARIABLE( xLocation, 3 );
506 break;
507 }
508
509 if( uxBufferLength < ( size_t ) ( ipSIZE_OF_ETH_HEADER + ( size_t ) usLength ) )
510 {
511 usChecksum = ipINVALID_LENGTH;
512 DEBUG_SET_TRACE_VARIABLE( xLocation, 4 );
513 break;
514 }
515
516 /* Identify the next protocol. */
517 ucProtocol = pxIPPacket->xIPHeader.ucProtocol;
518
519 /* N.B., if this IP packet header includes Options, then the following
520 * assignment results in a pointer into the protocol packet with the Ethernet
521 * and IP headers incorrectly aligned. However, either way, the "third"
522 * protocol (Layer 3 or 4) header will be aligned, which is the convenience
523 * of this calculation. */
524
525 /* MISRA Ref 11.3.1 [Misaligned access] */
526 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
527 /* coverity[misra_c_2012_rule_11_3_violation] */
528 pxProtPack = ( ( ProtocolPacket_t * ) &( pucEthernetBuffer[ uxIPHeaderLength - ipSIZE_OF_IPv4_HEADER ] ) );
529
530 /* Switch on the Layer 3/4 protocol. */
531 if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
532 {
533 if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_UDP_HEADER ) )
534 {
535 usChecksum = ipINVALID_LENGTH;
536 DEBUG_SET_TRACE_VARIABLE( xLocation, 5 );
537 break;
538 }
539
540 if( xOutgoingPacket != pdFALSE )
541 {
542 /* Clear the UDP checksum field before calculating it. */
543 pxProtPack->xUDPPacket.xUDPHeader.usChecksum = 0U;
544 }
545 else
546 {
547 usChecksumFound = pxProtPack->xUDPPacket.xUDPHeader.usChecksum;
548 }
549
550 #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
551 {
552 pcType = "UDP";
553 }
554 #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
555 }
556 else if( ucProtocol == ( uint8_t ) ipPROTOCOL_TCP )
557 {
558 if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_TCP_HEADER ) )
559 {
560 usChecksum = ipINVALID_LENGTH;
561 DEBUG_SET_TRACE_VARIABLE( xLocation, 6 );
562 break;
563 }
564
565 if( xOutgoingPacket != pdFALSE )
566 {
567 /* Clear the TCP checksum field before calculating it. */
568 pxProtPack->xTCPPacket.xTCPHeader.usChecksum = 0U;
569 }
570 else
571 {
572 usChecksumFound = pxProtPack->xTCPPacket.xTCPHeader.usChecksum;
573 }
574
575 #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
576 {
577 pcType = "TCP";
578 }
579 #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
580 }
581 else if( ( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) ||
582 ( ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
583 {
584 if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_ICMP_HEADER ) )
585 {
586 usChecksum = ipINVALID_LENGTH;
587 DEBUG_SET_TRACE_VARIABLE( xLocation, 7 );
588 break;
589 }
590
591 if( xOutgoingPacket != pdFALSE )
592 {
593 /* Clear the ICMP/IGMP checksum field before calculating it. */
594 pxProtPack->xICMPPacket.xICMPHeader.usChecksum = 0U;
595 }
596 else
597 {
598 usChecksumFound = pxProtPack->xICMPPacket.xICMPHeader.usChecksum;
599 }
600
601 #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
602 {
603 if( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
604 {
605 pcType = "ICMP";
606 }
607 else
608 {
609 pcType = "IGMP";
610 }
611 }
612 #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
613 }
614 else
615 {
616 /* Unhandled protocol, other than ICMP, IGMP, UDP, or TCP. */
617 usChecksum = ipUNHANDLED_PROTOCOL;
618 DEBUG_SET_TRACE_VARIABLE( xLocation, 8 );
619 break;
620 }
621
622 /* The protocol and checksum field have been identified. Check the direction
623 * of the packet. */
624 if( xOutgoingPacket != pdFALSE )
625 {
626 /* This is an outgoing packet. The CRC-field has been cleared. */
627 }
628 else if( ( usChecksumFound == 0U ) && ( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) )
629 {
630 #if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 )
631 {
632 /* Sender hasn't set the checksum, drop the packet because
633 * ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS is not set. */
634 usChecksum = ipWRONG_CRC;
635 #if ( ipconfigHAS_PRINTF != 0 )
636 {
637 static BaseType_t xCount = 0;
638
639 /* Exclude this from branch coverage as this is only used for debugging. */
640 if( xCount < 5 ) /* LCOV_EXCL_BR_LINE */
641 {
642 FreeRTOS_printf( ( "usGenerateProtocolChecksum: UDP packet from %xip without CRC dropped\n",
643 FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ) ) );
644 xCount++;
645 }
646 }
647 #endif /* ( ipconfigHAS_PRINTF != 0 ) */
648 }
649 #else /* if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */
650 {
651 /* Sender hasn't set the checksum, no use to calculate it. */
652 usChecksum = ipCORRECT_CRC;
653 }
654 #endif /* if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */
655 DEBUG_SET_TRACE_VARIABLE( xLocation, 9 );
656 break;
657 }
658 else
659 {
660 /* Other incoming packet than UDP. */
661 }
662
663 usLength = pxIPPacket->xIPHeader.usLength;
664 usLength = FreeRTOS_ntohs( usLength );
665 ulLength = ( uint32_t ) usLength;
666 ulLength -= ( ( uint16_t ) uxIPHeaderLength ); /* normally minus 20 */
667
668 if( ( ulLength < ( ( uint32_t ) sizeof( pxProtPack->xUDPPacket.xUDPHeader ) ) ) ||
669 ( ulLength > ( ( uint32_t ) ipconfigNETWORK_MTU - ( uint32_t ) uxIPHeaderLength ) ) )
670 {
671 #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
672 {
673 FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: len invalid: %lu\n", pcType, ulLength ) );
674 }
675 #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
676
677 /* Again, in a 16-bit return value there is no space to indicate an
678 * error. For incoming packets, 0x1234 will cause dropping of the packet.
679 * For outgoing packets, there is a serious problem with the
680 * format/length */
681 usChecksum = ipINVALID_LENGTH;
682 DEBUG_SET_TRACE_VARIABLE( xLocation, 10 );
683 break;
684 }
685
686 if( ucProtocol <= ( uint8_t ) ipPROTOCOL_IGMP )
687 {
688 /* ICMP/IGMP do not have a pseudo header for CRC-calculation. */
689 usChecksum = ( uint16_t )
690 ( ~usGenerateChecksum( 0U,
691 ( const uint8_t * ) &( pxProtPack->xICMPPacket.xICMPHeader ), ( size_t ) ulLength ) );
692 }
693 else
694 {
695 /* For UDP and TCP, sum the pseudo header, i.e. IP protocol + length
696 * fields */
697 usChecksum = ( uint16_t ) ( ulLength + ( ( uint16_t ) ucProtocol ) );
698
699 /* And then continue at the IPv4 source and destination addresses. */
700 usChecksum = ( uint16_t )
701 ( ~usGenerateChecksum( usChecksum,
702 ( const uint8_t * ) &( pxIPPacket->xIPHeader.ulSourceIPAddress ),
703 ( size_t ) ( ( 2U * ( size_t ) ipSIZE_OF_IPv4_ADDRESS ) + ulLength ) ) );
704 /* Sum TCP header and data. */
705 }
706
707 if( xOutgoingPacket == pdFALSE )
708 {
709 /* This is in incoming packet. If the CRC is correct, it should be zero. */
710 if( usChecksum == 0U )
711 {
712 usChecksum = ( uint16_t ) ipCORRECT_CRC;
713 }
714 else
715 {
716 usChecksum = ( uint16_t ) ipWRONG_CRC;
717 }
718 }
719 else
720 {
721 if( ( usChecksum == 0U ) && ( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) )
722 {
723 /* In case of UDP, a calculated checksum of 0x0000 is transmitted
724 * as 0xffff. A value of zero would mean that the checksum is not used. */
725 usChecksum = ( uint16_t ) 0xffffu;
726 }
727 }
728
729 usChecksum = FreeRTOS_htons( usChecksum );
730
731 if( xOutgoingPacket != pdFALSE )
732 {
733 switch( ucProtocol )
734 {
735 case ipPROTOCOL_UDP:
736 pxProtPack->xUDPPacket.xUDPHeader.usChecksum = usChecksum;
737 break;
738
739 case ipPROTOCOL_TCP:
740 pxProtPack->xTCPPacket.xTCPHeader.usChecksum = usChecksum;
741 break;
742
743 case ipPROTOCOL_ICMP:
744 pxProtPack->xICMPPacket.xICMPHeader.usChecksum = usChecksum;
745 break;
746
747 default: /* ipPROTOCOL_IGMP */
748 pxProtPack->xICMPPacket.xICMPHeader.usChecksum = usChecksum;
749 break;
750 }
751
752 usChecksum = ( uint16_t ) ipCORRECT_CRC;
753 }
754
755 #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
756 else if( ( xOutgoingPacket == pdFALSE ) && ( usChecksum != ipCORRECT_CRC ) )
757 {
758 FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: ID %04X: from %lxip to %lxip bad crc: %04X\n",
759 pcType,
760 FreeRTOS_ntohs( pxIPPacket->xIPHeader.usIdentification ),
761 FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ),
762 FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulDestinationIPAddress ),
763 FreeRTOS_ntohs( usChecksumFound ) ) );
764 }
765 else
766 {
767 /* Nothing. */
768 }
769 #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
770 } while( ipFALSE_BOOL );
771
772 if( ( usChecksum == ipUNHANDLED_PROTOCOL ) ||
773 ( usChecksum == ipINVALID_LENGTH ) )
774 {
775 /* NOP if ipconfigHAS_PRINTF != 0 */
776 FreeRTOS_printf( ( "CRC error: %04x location %ld\n", usChecksum, xLocation ) );
777 }
778
779 return usChecksum;
780 }
781 /*-----------------------------------------------------------*/
782
783 /**
784 * This method generates a checksum for a given IPv4 header, per RFC791 (page 14).
785 * The checksum algorithm is described as:
786 * "[T]he 16 bit one's complement of the one's complement sum of all 16 bit words in the
787 * header. For purposes of computing the checksum, the value of the checksum field is zero."
788 *
789 * In a nutshell, that means that each 16-bit 'word' must be summed, after which
790 * the number of 'carries' (overflows) is added to the result. If that addition
791 * produces an overflow, that 'carry' must also be added to the final result. The final checksum
792 * should be the bitwise 'not' (ones-complement) of the result if the packet is
793 * meant to be transmitted, but this method simply returns the raw value, probably
794 * because when a packet is received, the checksum is verified by checking that
795 * ((received & calculated) == 0) without applying a bitwise 'not' to the 'calculated' checksum.
796 *
797 * This logic is optimized for microcontrollers which have limited resources, so the logic looks odd.
798 * It iterates over the full range of 16-bit words, but it does so by processing several 32-bit
799 * words at once whenever possible. Its first step is to align the memory pointer to a 32-bit boundary,
800 * after which it runs a fast loop to process multiple 32-bit words at once and adding their 'carries'.
801 * Finally, it finishes up by processing any remaining 16-bit words, and adding up all of the 'carries'.
802 * With 32-bit arithmetic, the number of 16-bit 'carries' produced by sequential additions can be found
803 * by looking at the 16 most-significant bits of the 32-bit integer, since a 32-bit int will continue
804 * counting up instead of overflowing after 16 bits. That is why the actual checksum calculations look like:
805 * union.u32 = ( uint32_t ) union.u16[ 0 ] + union.u16[ 1 ];
806 *
807 * Arguments:
808 * ulSum: This argument provides a value to initialise the progressive summation
809 * of the header's values to. It is often 0, but protocols like TCP or UDP
810 * can have pseudo-header fields which need to be included in the checksum.
811 * pucNextData: This argument contains the address of the first byte which this
812 * method should process. The method's memory iterator is initialised to this value.
813 * uxDataLengthBytes: This argument contains the number of bytes that this method
814 * should process.
815 */
816
817 /**
818 * @brief Calculates the 16-bit checksum of an array of bytes
819 *
820 * @param[in] usSum: The initial sum, obtained from earlier data.
821 * @param[in] pucNextData: The actual data.
822 * @param[in] uxByteCount: The number of bytes.
823 *
824 * @return The 16-bit one's complement of the one's complement sum of all 16-bit
825 * words in the header
826 */
usGenerateChecksum(uint16_t usSum,const uint8_t * pucNextData,size_t uxByteCount)827 uint16_t usGenerateChecksum( uint16_t usSum,
828 const uint8_t * pucNextData,
829 size_t uxByteCount )
830 {
831 /* MISRA/PC-lint doesn't like the use of unions. Here, they are a great
832 * aid though to optimise the calculations. */
833 xUnion32 xSum2;
834 xUnion32 xSum;
835 xUnion32 xTerm;
836 xUnionPtr xSource;
837 uintptr_t uxAlignBits;
838 uint32_t ulCarry = 0U;
839 uint16_t usTemp;
840 size_t uxDataLengthBytes = uxByteCount;
841 size_t uxSize;
842 uintptr_t ulX;
843
844 /* Small MCUs often spend up to 30% of the time doing checksum calculations
845 * This function is optimised for 32-bit CPUs; Each time it will try to fetch
846 * 32-bits, sums it with an accumulator and counts the number of carries. */
847
848 /* Swap the input (little endian platform only). */
849 usTemp = FreeRTOS_ntohs( usSum );
850 xSum.u32 = ( uint32_t ) usTemp;
851 xTerm.u32 = 0U;
852
853 xSource.u8ptr = pucNextData;
854
855 /* MISRA Ref 11.4.3 [Casting pointer to int for verification] */
856 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
857 /* coverity[misra_c_2012_rule_11_4_violation] */
858 uxAlignBits = ( ( ( uintptr_t ) pucNextData ) & 0x03U );
859
860 /*
861 * If pucNextData is non-aligned then the checksum is starting at an
862 * odd position and we need to make sure the usSum value now in xSum is
863 * as if it had been "aligned" in the same way.
864 */
865 if( ( uxAlignBits & 1U ) != 0U )
866 {
867 xSum.u32 = ( ( xSum.u32 & 0xffU ) << 8 ) | ( ( xSum.u32 & 0xff00U ) >> 8 );
868 }
869
870 /* If byte (8-bit) aligned... */
871 if( ( ( uxAlignBits & 1U ) != 0U ) && ( uxDataLengthBytes >= ( size_t ) 1U ) )
872 {
873 xTerm.u8[ 1 ] = *( xSource.u8ptr );
874 xSource.u8ptr++;
875 uxDataLengthBytes--;
876 /* Now xSource is word (16-bit) aligned. */
877 }
878
879 /* If half-word (16-bit) aligned... */
880 if( ( ( uxAlignBits == 1U ) || ( uxAlignBits == 2U ) ) && ( uxDataLengthBytes >= 2U ) )
881 {
882 xSum.u32 += *( xSource.u16ptr );
883 xSource.u16ptr++;
884 uxDataLengthBytes -= 2U;
885 /* Now xSource is word (32-bit) aligned. */
886 }
887
888 /* Word (32-bit) aligned, do the most part. */
889
890 uxSize = ( size_t ) ( ( uxDataLengthBytes / 4U ) * 4U );
891
892 if( uxSize >= ( 3U * sizeof( uint32_t ) ) )
893 {
894 uxSize -= ( 3U * sizeof( uint32_t ) );
895 }
896 else
897 {
898 uxSize = 0U;
899 }
900
901 /* In this loop, four 32-bit additions will be done, in total 16 bytes.
902 * Indexing with constants (0,1,2,3) gives faster code than using
903 * post-increments. */
904 for( ulX = 0U; ulX < uxSize; ulX += 4U * sizeof( uint32_t ) )
905 {
906 /* Use a secondary Sum2, just to see if the addition produced an
907 * overflow. */
908 xSum2.u32 = xSum.u32 + xSource.u32ptr[ 0 ];
909
910 if( xSum2.u32 < xSum.u32 )
911 {
912 ulCarry++;
913 }
914
915 /* Now add the secondary sum to the major sum, and remember if there was
916 * a carry. */
917 xSum.u32 = xSum2.u32 + xSource.u32ptr[ 1 ];
918
919 if( xSum2.u32 > xSum.u32 )
920 {
921 ulCarry++;
922 }
923
924 /* And do the same trick once again for indexes 2 and 3 */
925 xSum2.u32 = xSum.u32 + xSource.u32ptr[ 2 ];
926
927 if( xSum2.u32 < xSum.u32 )
928 {
929 ulCarry++;
930 }
931
932 xSum.u32 = xSum2.u32 + xSource.u32ptr[ 3 ];
933
934 if( xSum2.u32 > xSum.u32 )
935 {
936 ulCarry++;
937 }
938
939 /* And finally advance the pointer 4 * 4 = 16 bytes. */
940 xSource.u32ptr = &( xSource.u32ptr[ 4 ] );
941 }
942
943 /* Now add all carries. */
944 xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ] + ulCarry;
945
946 uxDataLengthBytes %= 16U;
947
948 /* Half-word aligned. */
949 uxSize = ( ( uxDataLengthBytes & ~( ( size_t ) 1U ) ) );
950
951 for( ulX = 0U; ulX < uxSize; ulX += 1U * sizeof( uint16_t ) )
952 {
953 /* At least one more short. */
954 xSum.u32 += xSource.u16ptr[ 0 ];
955 xSource.u16ptr = &xSource.u16ptr[ 1 ];
956 }
957
958 if( ( uxDataLengthBytes & ( size_t ) 1U ) != 0U ) /* Maybe one more ? */
959 {
960 xTerm.u8[ 0 ] = xSource.u8ptr[ 0 ];
961 }
962
963 /* MISRA Ref 2.2.1 [Unions and dead code] */
964 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */
965 /* coverity[misra_c_2012_rule_2_2_violation] */
966 /* coverity[assigned_value] */
967 xSum.u32 += xTerm.u32;
968
969 /* Now add all carries again. */
970
971 /* Assigning value from "xTerm.u32" to "xSum.u32" here, but that stored value is overwritten before it can be used. */
972 /* MISRA Ref 2.2.1 [Unions and dead code] */
973 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */
974 /* coverity[misra_c_2012_rule_2_2_violation] */
975 /* coverity[value_overwrite] */
976 xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ];
977
978 /* MISRA Ref 2.2.1 [Unions and dead code] */
979 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-22 */
980 /* coverity[misra_c_2012_rule_2_2_violation] */
981 /* coverity[value_overwrite] */
982 xSum.u32 = ( uint32_t ) xSum.u16[ 0 ] + xSum.u16[ 1 ];
983
984 if( ( uxAlignBits & 1U ) != 0U )
985 {
986 /* Quite unlikely, but pucNextData might be non-aligned, which would
987 * mean that a checksum is calculated starting at an odd position. */
988 xSum.u32 = ( ( xSum.u32 & 0xffU ) << 8 ) | ( ( xSum.u32 & 0xff00U ) >> 8 );
989 }
990
991 /* swap the output (little endian platform only). */
992 return FreeRTOS_htons( ( ( uint16_t ) xSum.u32 ) );
993 }
994 /*-----------------------------------------------------------*/
995
996 #if ( ipconfigHAS_PRINTF != 0 )
997
998 #ifndef ipMONITOR_MAX_HEAP
999
1000 /* As long as the heap has more space than e.g. 1 MB, there
1001 * will be no messages. */
1002 #define ipMONITOR_MAX_HEAP ( 1024U * 1024U )
1003 #endif /* ipMONITOR_MAX_HEAP */
1004
1005 #ifndef ipMONITOR_PERCENTAGE_90
1006 /* Make this number lower to get less logging messages. */
1007 #define ipMONITOR_PERCENTAGE_90 ( 90U )
1008 #endif
1009
1010 #define ipMONITOR_PERCENTAGE_100 ( 100U )
1011
1012 /**
1013 * @brief A function that monitors a three resources: the heap, the space in the message
1014 * queue of the IP-task, the number of available network buffer descriptors.
1015 */
vPrintResourceStats(void)1016 void vPrintResourceStats( void )
1017 {
1018 UBaseType_t uxCurrentBufferCount;
1019 size_t uxMinSize;
1020
1021 /* When setting up and testing a project with FreeRTOS+TCP, it is
1022 * can be helpful to monitor a few resources: the number of network
1023 * buffers and the amount of available heap.
1024 * This function will issue some logging when a minimum value has
1025 * changed. */
1026 uxCurrentBufferCount = uxGetMinimumFreeNetworkBuffers();
1027
1028 if( uxLastMinBufferCount > uxCurrentBufferCount )
1029 {
1030 /* The logging produced below may be helpful
1031 * while tuning +TCP: see how many buffers are in use. */
1032 uxLastMinBufferCount = uxCurrentBufferCount;
1033 FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
1034 uxGetNumberOfFreeNetworkBuffers(),
1035 uxCurrentBufferCount ) );
1036 }
1037
1038 uxMinSize = xPortGetMinimumEverFreeHeapSize();
1039
1040 if( uxMinLastSize == 0U )
1041 {
1042 /* Probably the first time this function is called. */
1043 uxMinLastSize = uxMinSize;
1044 }
1045 else if( uxMinSize >= ipMONITOR_MAX_HEAP )
1046 {
1047 /* There is more than enough heap space. No need for logging. */
1048 }
1049 /* Write logging if there is a 10% decrease since the last time logging was written. */
1050 else if( ( uxMinLastSize * ipMONITOR_PERCENTAGE_90 ) > ( uxMinSize * ipMONITOR_PERCENTAGE_100 ) )
1051 {
1052 uxMinLastSize = uxMinSize;
1053 FreeRTOS_printf( ( "Heap: current %lu lowest %lu\n", xPortGetFreeHeapSize(), uxMinSize ) );
1054 }
1055 else
1056 {
1057 /* Nothing to log. */
1058 }
1059
1060 #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
1061 {
1062 UBaseType_t uxCurrentCount = 0u;
1063
1064 uxCurrentCount = uxGetMinimumIPQueueSpace();
1065
1066 if( uxLastMinQueueSpace != uxCurrentCount )
1067 {
1068 /* The logging produced below may be helpful
1069 * while tuning +TCP: see how many buffers are in use. */
1070 uxLastMinQueueSpace = uxCurrentCount;
1071 FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
1072 }
1073 }
1074 #endif /* ipconfigCHECK_IP_QUEUE_SPACE */
1075 }
1076 #endif /* ( ipconfigHAS_PRINTF != 0 ) */
1077 /*-----------------------------------------------------------*/
1078
1079 /**
1080 * @brief Utility function: Convert error number to a human readable
1081 * string. Declaration in FreeRTOS_errno_TCP.h.
1082 *
1083 * @param[in] xErrnum: The error number.
1084 * @param[in] pcBuffer: Buffer big enough to be filled with the human readable message.
1085 * @param[in] uxLength: Maximum length of the buffer.
1086 *
1087 * @return The buffer filled with human readable error string.
1088 */
FreeRTOS_strerror_r(BaseType_t xErrnum,char * pcBuffer,size_t uxLength)1089 const char * FreeRTOS_strerror_r( BaseType_t xErrnum,
1090 char * pcBuffer,
1091 size_t uxLength )
1092 {
1093 const char * pcName;
1094
1095 switch( xErrnum )
1096 {
1097 case pdFREERTOS_ERRNO_EADDRINUSE:
1098 pcName = "EADDRINUSE";
1099 break;
1100
1101 case pdFREERTOS_ERRNO_ENOMEM:
1102 pcName = "ENOMEM";
1103 break;
1104
1105 case pdFREERTOS_ERRNO_EADDRNOTAVAIL:
1106 pcName = "EADDRNOTAVAIL";
1107 break;
1108
1109 case pdFREERTOS_ERRNO_ENOPROTOOPT:
1110 pcName = "ENOPROTOOPT";
1111 break;
1112
1113 case pdFREERTOS_ERRNO_EBADF:
1114 pcName = "EBADF";
1115 break;
1116
1117 case pdFREERTOS_ERRNO_ENOSPC:
1118 pcName = "ENOSPC";
1119 break;
1120
1121 case pdFREERTOS_ERRNO_ECANCELED:
1122 pcName = "ECANCELED";
1123 break;
1124
1125 case pdFREERTOS_ERRNO_ENOTCONN:
1126 pcName = "ENOTCONN";
1127 break;
1128
1129 case pdFREERTOS_ERRNO_EINPROGRESS:
1130 pcName = "EINPROGRESS";
1131 break;
1132
1133 case pdFREERTOS_ERRNO_EOPNOTSUPP:
1134 pcName = "EOPNOTSUPP";
1135 break;
1136
1137 case pdFREERTOS_ERRNO_EINTR:
1138 pcName = "EINTR";
1139 break;
1140
1141 case pdFREERTOS_ERRNO_ETIMEDOUT:
1142 pcName = "ETIMEDOUT";
1143 break;
1144
1145 case pdFREERTOS_ERRNO_EINVAL:
1146 pcName = "EINVAL";
1147 break;
1148
1149 case pdFREERTOS_ERRNO_EWOULDBLOCK:
1150 pcName = "EWOULDBLOCK";
1151 break; /* same as EAGAIN */
1152
1153 case pdFREERTOS_ERRNO_EISCONN:
1154 pcName = "EISCONN";
1155 break;
1156
1157 default:
1158 /* MISRA Ref 21.6.1 [snprintf and logging] */
1159 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-216 */
1160 /* coverity[misra_c_2012_rule_21_6_violation] */
1161 ( void ) snprintf( pcBuffer, uxLength, "Errno %d", ( int ) xErrnum );
1162 pcName = NULL;
1163 break;
1164 }
1165
1166 if( pcName != NULL )
1167 {
1168 /* MISRA Ref 21.6.1 [snprintf and logging] */
1169 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-216 */
1170 /* coverity[misra_c_2012_rule_21_6_violation] */
1171 ( void ) snprintf( pcBuffer, uxLength, "%s", pcName );
1172 }
1173
1174 if( uxLength > 0U )
1175 {
1176 pcBuffer[ uxLength - 1U ] = '\0';
1177 }
1178
1179 return pcBuffer;
1180 }
1181 /*-----------------------------------------------------------*/
1182
1183 /**
1184 * @brief Get the highest value of two int32's.
1185 * @param[in] a: the first value.
1186 * @param[in] b: the second value.
1187 * @return The highest of the two values.
1188 */
FreeRTOS_max_int32(int32_t a,int32_t b)1189 int32_t FreeRTOS_max_int32( int32_t a,
1190 int32_t b )
1191 {
1192 return ( a >= b ) ? a : b;
1193 }
1194 /*-----------------------------------------------------------*/
1195
1196 /**
1197 * @brief Get the highest value of two uint32_t's.
1198 * @param[in] a: the first value.
1199 * @param[in] b: the second value.
1200 * @return The highest of the two values.
1201 */
FreeRTOS_max_uint32(uint32_t a,uint32_t b)1202 uint32_t FreeRTOS_max_uint32( uint32_t a,
1203 uint32_t b )
1204 {
1205 return ( a >= b ) ? a : b;
1206 }
1207 /*-----------------------------------------------------------*/
1208
1209 /**
1210 * @brief Get the highest value of two size_t's.
1211 * @param[in] a: the first value.
1212 * @param[in] b: the second value.
1213 * @return The highest of the two values.
1214 */
FreeRTOS_max_size_t(size_t a,size_t b)1215 size_t FreeRTOS_max_size_t( size_t a,
1216 size_t b )
1217 {
1218 return ( a >= b ) ? a : b;
1219 }
1220 /*-----------------------------------------------------------*/
1221
1222 /**
1223 * @brief Get the lowest value of two int32_t's.
1224 * @param[in] a: the first value.
1225 * @param[in] b: the second value.
1226 * @return The lowest of the two values.
1227 */
FreeRTOS_min_int32(int32_t a,int32_t b)1228 int32_t FreeRTOS_min_int32( int32_t a,
1229 int32_t b )
1230 {
1231 return ( a <= b ) ? a : b;
1232 }
1233 /*-----------------------------------------------------------*/
1234
1235 /**
1236 * @brief Get the lowest value of two uint32_t's.
1237 * @param[in] a: the first value.
1238 * @param[in] b: the second value.
1239 * @return The lowest of the two values.
1240 */
FreeRTOS_min_uint32(uint32_t a,uint32_t b)1241 uint32_t FreeRTOS_min_uint32( uint32_t a,
1242 uint32_t b )
1243 {
1244 return ( a <= b ) ? a : b;
1245 }
1246 /*-----------------------------------------------------------*/
1247
1248 /**
1249 * @brief Get the lowest value of two size_t's.
1250 * @param[in] a: the first value.
1251 * @param[in] b: the second value.
1252 * @return The lowest of the two values.
1253 */
FreeRTOS_min_size_t(size_t a,size_t b)1254 size_t FreeRTOS_min_size_t( size_t a,
1255 size_t b )
1256 {
1257 return ( a <= b ) ? a : b;
1258 }
1259 /*-----------------------------------------------------------*/
1260
1261 /**
1262 * @brief Round-up a number to a multiple of 'd'.
1263 * @param[in] a: the first value.
1264 * @param[in] d: the second value.
1265 * @return A multiple of d.
1266 */
FreeRTOS_round_up(uint32_t a,uint32_t d)1267 uint32_t FreeRTOS_round_up( uint32_t a,
1268 uint32_t d )
1269 {
1270 uint32_t ulResult = a;
1271
1272 configASSERT( d != 0U );
1273
1274 if( d != 0U )
1275 {
1276 ulResult = d * ( ( a + d - 1U ) / d );
1277 }
1278
1279 return ulResult;
1280 }
1281 /*-----------------------------------------------------------*/
1282
1283 /**
1284 * @brief Round-down a number to a multiple of 'd'.
1285 * @param[in] a: the first value.
1286 * @param[in] d: the second value.
1287 * @return A multiple of d.
1288 */
FreeRTOS_round_down(uint32_t a,uint32_t d)1289 uint32_t FreeRTOS_round_down( uint32_t a,
1290 uint32_t d )
1291 {
1292 uint32_t ulResult = 0;
1293
1294 configASSERT( d != 0U );
1295
1296 if( d != 0U )
1297 {
1298 ulResult = d * ( a / d );
1299 }
1300
1301 return ulResult;
1302 }
1303 /*-----------------------------------------------------------*/
1304
1305 /**
1306 * @brief Convert character array (of size 4) to equivalent 32-bit value.
1307 * @param[in] pucPtr: The character array.
1308 * @return 32-bit equivalent value extracted from the character array.
1309 *
1310 * @note Going by MISRA rules, these utility functions should not be defined
1311 * if they are not being used anywhere. But their use depends on the
1312 * application and hence these functions are defined unconditionally.
1313 */
ulChar2u32(const uint8_t * pucPtr)1314 uint32_t ulChar2u32( const uint8_t * pucPtr )
1315 {
1316 return ( ( ( uint32_t ) pucPtr[ 0 ] ) << 24 ) |
1317 ( ( ( uint32_t ) pucPtr[ 1 ] ) << 16 ) |
1318 ( ( ( uint32_t ) pucPtr[ 2 ] ) << 8 ) |
1319 ( ( ( uint32_t ) pucPtr[ 3 ] ) );
1320 }
1321 /*-----------------------------------------------------------*/
1322
1323 /**
1324 * @brief Convert character array (of size 2) to equivalent 16-bit value.
1325 * @param[in] pucPtr: The character array.
1326 * @return 16-bit equivalent value extracted from the character array.
1327 *
1328 * @note Going by MISRA rules, these utility functions should not be defined
1329 * if they are not being used anywhere. But their use depends on the
1330 * application and hence these functions are defined unconditionally.
1331 */
usChar2u16(const uint8_t * pucPtr)1332 uint16_t usChar2u16( const uint8_t * pucPtr )
1333 {
1334 return ( uint16_t )
1335 ( ( ( ( uint32_t ) pucPtr[ 0 ] ) << 8 ) |
1336 ( ( ( uint32_t ) pucPtr[ 1 ] ) ) );
1337 }
1338 /*-----------------------------------------------------------*/
1339