1
0
mirror of https://github.com/FreeRTOS/FreeRTOS-Plus-TCP synced 2025-10-20 13:23:44 +08:00
Files
FreeRTOS-Plus-TCP/source/FreeRTOS_IPv6_Sockets.c
2024-12-11 04:15:08 +00:00

661 lines
22 KiB
C

/*
* FreeRTOS+TCP V4.3.0
* Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file FreeRTOS_IPv6_Sockets.c
* @brief Implements the Sockets API based on Berkeley sockets for the FreeRTOS+TCP network stack.
* Sockets are used by the application processes to interact with the IP-task which in turn
* interacts with the hardware.
*/
/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_IPv6_Sockets.h"
/* *INDENT-OFF* */
#if( ipconfigUSE_IPv6 != 0 )
/* *INDENT-ON* */
#if ( ipconfigUSE_TCP == 1 )
/**
* @brief Called by pxTCPSocketLookup(), this function will check if a socket
* is connected to a remote IP-address. It will be called from a loop
* iterating through all sockets.
* @param[in] pxSocket The socket to be inspected.
* @param[in] pxAddress The IPv4/IPv6 address.
* @return The socket in case it is connected to the remote IP-address.
*/
FreeRTOS_Socket_t * pxTCPSocketLookup_IPv6( FreeRTOS_Socket_t * pxSocket,
const IPv46_Address_t * pxAddress )
{
FreeRTOS_Socket_t * pxResult = NULL;
if( ( pxSocket != NULL ) && ( pxAddress != NULL ) )
{
if( pxSocket->bits.bIsIPv6 != pdFALSE_UNSIGNED )
{
if( pxAddress->xIs_IPv6 != pdFALSE )
{
if( memcmp( pxSocket->u.xTCP.xRemoteIP.xIP_IPv6.ucBytes, pxAddress->xIPAddress.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
{
/* For sockets not in listening mode, find a match with
* uxLocalPort, ulRemoteIP AND uxRemotePort. */
pxResult = pxSocket;
}
}
}
else
{
if( pxAddress->xIs_IPv6 == pdFALSE )
{
if( pxSocket->u.xTCP.xRemoteIP.ulIP_IPv4 == pxAddress->xIPAddress.ulIP_IPv4 )
{
/* For sockets not in listening mode, find a match with
* uxLocalPort, ulRemoteIP AND uxRemotePort. */
pxResult = pxSocket;
}
}
}
}
return pxResult;
}
#endif /* if ( ( ipconfigUSE_TCP == 1 ) */
/**
* @brief Called by prvSendUDPPacket(), this function will UDP packet
* fields and IPv6 address for the packet to be send.
* @param[in] pxNetworkBuffer The packet to be sent.
* @param[in] pxDestinationAddress The IPv4 socket address.
* @return Returns NULL, always.
*/
void * xSend_UDP_Update_IPv6( NetworkBufferDescriptor_t * pxNetworkBuffer,
const struct freertos_sockaddr * pxDestinationAddress )
{
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
UDPPacket_IPv6_t * pxUDPPacket_IPv6 = ( ( UDPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
pxNetworkBuffer->xIPAddress.ulIP_IPv4 = 0U;
configASSERT( pxDestinationAddress != NULL );
( void ) memcpy( pxUDPPacket_IPv6->xIPHeader.xDestinationAddress.ucBytes, pxDestinationAddress->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
( void ) memcpy( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, pxDestinationAddress->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
pxUDPPacket_IPv6->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE;
return NULL;
}
/**
* @brief Called by FreeRTOS_recvfrom(), this function will update socket
* address with IPv6 address from the packet received.
* @param[in] pxNetworkBuffer The packet received.
* @param[in] pxSourceAddress The IPv4 socket address.
* @return The Payload Offset.
*/
size_t xRecv_Update_IPv6( const NetworkBufferDescriptor_t * pxNetworkBuffer,
struct freertos_sockaddr * pxSourceAddress )
{
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
const UDPPacket_IPv6_t * pxUDPPacketV6 = ( ( const UDPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
size_t uxPayloadOffset = 0;
if( pxUDPPacketV6->xEthernetHeader.usFrameType == ipIPv6_FRAME_TYPE )
{
if( pxSourceAddress != NULL )
{
( void ) memcpy( ( void * ) pxSourceAddress->sin_address.xIP_IPv6.ucBytes,
( const void * ) pxUDPPacketV6->xIPHeader.xSourceAddress.ucBytes,
ipSIZE_OF_IPv6_ADDRESS );
pxSourceAddress->sin_family = ( uint8_t ) FREERTOS_AF_INET6;
pxSourceAddress->sin_port = pxNetworkBuffer->usPort;
}
uxPayloadOffset = ipUDP_PAYLOAD_OFFSET_IPv6;
}
return uxPayloadOffset;
}
/**
* @brief Converts a 4 bit (nibble) value to a readable hex character, e.g. 14 becomes 'e'.
* @param usValue The value to be converted, must be between 0 and 15.
* @return The character, between '0' and '9', or between 'a' and 'f'.
*/
char cHexToChar( uint16_t usValue )
{
char cReturn = '0';
if( usValue <= 9U )
{
cReturn = ( char ) ( cReturn + usValue );
}
else if( usValue <= 15U )
{
cReturn = 'a';
cReturn = ( char ) ( cReturn + ( usValue - ( uint16_t ) 10 ) );
}
else
{
/* The value passed to 'usValue' has been and-ed with 0x0f,
* so this else clause should never be reached. */
configASSERT( 0 == 1 );
}
return cReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Convert a short numeric value to a hex string of at most 4 characters.
* The resulting string is **not** null-terminated. The resulting string
* will not have leading zero's, except when 'usValue' equals zero.
* @param[in] pcBuffer The buffer to which the string is written.
* @param[in] uxBufferSize The size of the buffer pointed to by 'pcBuffer'.
* @param[in] usValue The 16-bit value to be converted.
* @return The number of bytes written to 'pcBuffer'.
*/
socklen_t uxHexPrintShort( char * pcBuffer,
size_t uxBufferSize,
uint16_t usValue )
{
const size_t uxNibbleCount = 4U;
size_t uxNibble;
socklen_t uxIndex = 0U;
uint16_t usShifter = usValue;
BaseType_t xHadNonZero = pdFALSE;
for( uxNibble = 0; uxNibble < uxNibbleCount; uxNibble++ )
{
uint16_t usNibble = ( usShifter >> 12 ) & 0x0FU;
if( usNibble != 0U )
{
xHadNonZero = pdTRUE;
}
if( ( xHadNonZero != pdFALSE ) || ( uxNibble == ( uxNibbleCount - 1U ) ) )
{
if( uxIndex >= ( uxBufferSize - 1U ) )
{
break;
}
pcBuffer[ uxIndex ] = cHexToChar( usNibble );
uxIndex++;
}
usShifter = ( uint16_t ) ( usShifter << 4 );
}
return uxIndex;
}
/*-----------------------------------------------------------*/
/**
* @brief Scan the binary IPv6 address and find the longest train of consecutive zero's.
* The result of this search will be stored in 'xZeroStart' and 'xZeroLength'.
* @param pxSet the set of parameters as used by FreeRTOS_inet_ntop6().
*/
void prv_ntop6_search_zeros( struct sNTOP6_Set * pxSet )
{
BaseType_t xIndex = 0; /* The index in the IPv6 address: 0..7. */
BaseType_t xCurStart = 0; /* The position of the first zero found so far. */
BaseType_t xCurLength = 0; /* The number of zero's seen so far. */
const BaseType_t xShortCount = 8; /* An IPv6 address consists of 8 shorts. */
/* Default: when xZeroStart is negative, it won't match with any xIndex. */
pxSet->xZeroStart = -1;
/* Look for the longest train of zero's 0:0:0:... */
for( ; xIndex < xShortCount; xIndex++ )
{
uint16_t usValue = pxSet->pusAddress[ xIndex ];
if( usValue == 0U )
{
if( xCurLength == 0 )
{
/* Remember the position of the first zero. */
xCurStart = xIndex;
}
/* Count consecutive zeros. */
xCurLength++;
}
if( ( usValue != 0U ) || ( xIndex == ( xShortCount - 1 ) ) )
{
/* Has a longer train of zero's been found? */
if( ( xCurLength > 1 ) && ( pxSet->xZeroLength < xCurLength ) )
{
/* Remember the number of consecutive zeros. */
pxSet->xZeroLength = xCurLength;
/* Remember the index of the first zero found. */
pxSet->xZeroStart = xCurStart;
}
/* Reset the counter of consecutive zeros. */
xCurLength = 0;
}
}
}
/*-----------------------------------------------------------*/
/**
* @brief The location is now at the longest train of zero's. Two colons have to
* be printed without a numeric value, e.g. "ff02::1".
* @param pcDestination the output buffer where the colons will be printed.
* @param uxSize the remaining length of the output buffer.
* @param pxSet the set of parameters as used by FreeRTOS_inet_ntop6().
* @return pdPASS in case the output buffer is big enough to contain the colons.
* @note uxSize must be at least 2, enough to print "::". The string will get
* null-terminated later on.
*/
static BaseType_t prv_ntop6_write_zeros( char * pcDestination,
size_t uxSize,
struct sNTOP6_Set * pxSet )
{
BaseType_t xReturn = pdPASS;
const BaseType_t xShortCount = 8; /* An IPv6 address consists of 8 shorts. */
if( pxSet->uxTargetIndex <= ( uxSize - 1U ) )
{
pcDestination[ pxSet->uxTargetIndex ] = ':';
pxSet->uxTargetIndex++;
if( ( pxSet->xIndex + pxSet->xZeroLength ) == xShortCount )
{
/* Reached the last index, write a second ":". */
if( pxSet->uxTargetIndex <= ( uxSize - 1U ) )
{
pcDestination[ pxSet->uxTargetIndex ] = ':';
pxSet->uxTargetIndex++;
}
else
{
/* Can not write the second colon. */
xReturn = pdFAIL;
}
}
else
{
/* Otherwise the function prv_ntop6_write_short() will places the second colon. */
}
}
else
{
/* Can not write the first colon. */
xReturn = pdFAIL;
}
return xReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Write a short value, as a hex number with at most 4 characters. E.g. the
* value 15 will be printed as "f".
* @param pcDestination the output buffer where the hex number is to be printed.
* @param uxSize the remaining length of the output buffer.
* @param pxSet the set of parameters as used by FreeRTOS_inet_ntop6().
* @return pdPASS in case the output buffer is big enough to contain the string.
* @note uxSize must be at least 4, enough to print "abcd". The string will get
* null-terminated later on.
*/
static BaseType_t prv_ntop6_write_short( char * pcDestination,
size_t uxSize,
struct sNTOP6_Set * pxSet )
{
socklen_t uxLength;
BaseType_t xReturn = pdPASS;
const size_t uxBytesPerShortValue = 4U;
if( pxSet->xIndex > 0 )
{
if( pxSet->uxTargetIndex >= ( uxSize - 1U ) )
{
xReturn = pdFAIL;
}
else
{
pcDestination[ pxSet->uxTargetIndex ] = ':';
pxSet->uxTargetIndex++;
}
}
if( xReturn == pdPASS )
{
/* If there is enough space to write a short. */
if( pxSet->uxTargetIndex <= ( uxSize - uxBytesPerShortValue ) )
{
/* Write hex value of short. at most 4 + 1 bytes. */
uxLength = uxHexPrintShort( &( pcDestination[ pxSet->uxTargetIndex ] ),
uxBytesPerShortValue + 1U,
FreeRTOS_ntohs( pxSet->pusAddress[ pxSet->xIndex ] ) );
/* uxLength will be non zero and positive always. */
pxSet->uxTargetIndex += uxLength;
}
else
{
xReturn = pdFAIL;
}
}
return xReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief This function converts a binary IPv6 address to a human readable notation.
*
* @param[in] pvSource The binary address, 16 bytes long..
* @param[out] pcDestination The human-readable ( hexadecimal ) notation of the
* address.
* @param[in] uxSize The size of pvDestination. A value of 40 is recommended.
*
* @return pdPASS if the translation was successful or else pdFAIL.
*/
const char * FreeRTOS_inet_ntop6( const void * pvSource,
char * pcDestination,
socklen_t uxSize )
{
const char * pcReturn; /* The return value, which is either 'pcDestination' or NULL. */
struct sNTOP6_Set xSet; /* A set of values for easy exchange with the helper functions prv_ntop6_xxx(). */
( void ) memset( &( xSet ), 0, sizeof( xSet ) );
xSet.pusAddress = pvSource;
if( uxSize < 3U )
{
/* Can not even print :: */
}
else
{
prv_ntop6_search_zeros( &( xSet ) );
while( xSet.xIndex < 8 )
{
if( xSet.xIndex == xSet.xZeroStart )
{
if( prv_ntop6_write_zeros( pcDestination, uxSize, &( xSet ) ) == pdFAIL )
{
break;
}
xSet.xIndex += xSet.xZeroLength;
}
else
{
if( prv_ntop6_write_short( pcDestination, uxSize, &( xSet ) ) == pdFAIL )
{
break;
}
xSet.xIndex++;
}
}
}
if( xSet.xIndex < 8 )
{
/* Didn't reach the last nibble: clear the string. */
pcReturn = NULL;
}
else
{
pcDestination[ xSet.uxTargetIndex ] = '\0';
pcReturn = pcDestination;
}
return pcReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Converting a readable IPv6 address to its binary form, add one nibble.
*
* @param[in] pxSet A set of variables describing the conversion.
* @param[in] ucNew The hex value, between 0 and 15
* @param[in] ch The character, such as '5', 'f', or ':'.
*
* @return pdTRUE when the nibble was added, otherwise pdFALSE.
*/
static BaseType_t prv_inet_pton6_add_nibble( struct sPTON6_Set * pxSet,
uint8_t ucNew,
char ch )
{
BaseType_t xReturn = pdPASS;
if( ucNew != ( uint8_t ) socketINVALID_HEX_CHAR )
{
/* Shift in 4 bits. */
pxSet->ulValue <<= 4;
pxSet->ulValue |= ( uint32_t ) ucNew;
/* Remember that ulValue is valid now. */
pxSet->xHadDigit = pdTRUE;
/* Check if the number is not becoming larger than 16 bits. */
if( pxSet->ulValue > 0xffffU )
{
/* The highest nibble has already been set,
* an overflow would occur. Break out of the for-loop. */
xReturn = pdFAIL;
}
}
else if( ch == ':' )
{
if( pxSet->xHadDigit == pdFALSE )
{
/* A "::" sequence has been received. Check if it is not a third colon. */
if( pxSet->xColon >= 0 )
{
xReturn = pdFAIL;
}
else
{
/* Two or more zero's are expected, starting at position 'xColon'. */
pxSet->xColon = pxSet->xTargetIndex;
}
}
else
{
if( pxSet->xTargetIndex <= pxSet->xHighestIndex )
{
/* Store a short value at position 'xTargetIndex'. */
pxSet->pucTarget[ pxSet->xTargetIndex ] = ( uint8_t ) ( ( pxSet->ulValue >> 8 ) & 0xffU );
pxSet->pucTarget[ pxSet->xTargetIndex + 1 ] = ( uint8_t ) ( pxSet->ulValue & 0xffU );
pxSet->xTargetIndex += 2;
pxSet->xHadDigit = pdFALSE;
pxSet->ulValue = 0U;
}
else
{
xReturn = pdFAIL;
}
}
}
else
{
/* When an IPv4 address or rubbish is provided, this statement will be reached. */
xReturn = pdFAIL;
}
return xReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Convert an ASCII character to its corresponding hexadecimal value.
* A :: block was found, now fill in the zero's.
* @param[in] pxSet A set of variables describing the conversion.
*/
static void prv_inet_pton6_set_zeros( struct sPTON6_Set * pxSet )
{
/* The number of bytes that were written after the :: */
const BaseType_t xCount = pxSet->xTargetIndex - pxSet->xColon;
const BaseType_t xTopIndex = ( BaseType_t ) ipSIZE_OF_IPv6_ADDRESS;
BaseType_t xIndex;
BaseType_t xTarget = xTopIndex - 1;
BaseType_t xSource = pxSet->xColon + ( xCount - 1 );
/* Inserting 'xCount' zero's. */
for( xIndex = 0; xIndex < xCount; xIndex++ )
{
pxSet->pucTarget[ xTarget ] = pxSet->pucTarget[ xSource ];
pxSet->pucTarget[ xSource ] = 0;
xTarget--;
xSource--;
}
pxSet->xTargetIndex = ( BaseType_t ) ipSIZE_OF_IPv6_ADDRESS;
}
/*-----------------------------------------------------------*/
/**
* @brief Convert an IPv6 address in hexadecimal notation to a binary format of 16 bytes.
*
* @param[in] pcSource The address in hexadecimal notation.
* @param[out] pvDestination The address in binary format, 16 bytes long.
*
* @return The 32-bit representation of IP(v4) address.
*/
BaseType_t FreeRTOS_inet_pton6( const char * pcSource,
void * pvDestination )
{
char ch;
uint8_t ucNew;
BaseType_t xResult;
struct sPTON6_Set xSet;
const char * pcIterator = pcSource;
( void ) memset( &( xSet ), 0, sizeof( xSet ) );
xSet.xColon = -1;
xSet.pucTarget = pvDestination;
( void ) memset( xSet.pucTarget, 0, ipSIZE_OF_IPv6_ADDRESS );
xResult = 0;
/* Leading :: requires some special handling. */
if( strcmp( pcIterator, "::" ) == 0 )
{
xResult = 1;
}
else
{
if( pcIterator[ 0 ] == ':' )
{
pcIterator++;
}
/* The last bytes will be written at position 14 and 15. */
xSet.xHighestIndex = ( BaseType_t ) ipSIZE_OF_IPv6_ADDRESS;
xSet.xHighestIndex -= ( BaseType_t ) sizeof( uint16_t );
/* The value in ulValue is not yet valid. */
xSet.xHadDigit = pdFALSE;
xSet.ulValue = 0U;
for( ; ; )
{
ch = *( pcIterator );
pcIterator++;
if( ch == ( char ) '\0' )
{
/* The string is parsed now.
* Store the last short, if present. */
if( ( xSet.xHadDigit != pdFALSE ) &&
( xSet.xTargetIndex <= xSet.xHighestIndex ) )
{
/* Add the last value seen, network byte order ( MSB first ). */
xSet.pucTarget[ xSet.xTargetIndex ] = ( uint8_t ) ( ( xSet.ulValue >> 8 ) & 0xffU );
xSet.pucTarget[ xSet.xTargetIndex + 1 ] = ( uint8_t ) ( xSet.ulValue & 0xffU );
xSet.xTargetIndex += 2;
}
/* Break out of the for-ever loop. */
break;
}
/* Convert from a readable character to a hex value. */
ucNew = ucASCIIToHex( ch );
/* See if this is a digit or a colon. */
xResult = prv_inet_pton6_add_nibble( &( xSet ), ucNew, ch );
if( xResult == pdFALSE )
{
/* The new character was not accepted. */
break;
}
} /* for( ;; ) */
if( xSet.xColon >= 0 )
{
/* The address contains a block of zero. */
prv_inet_pton6_set_zeros( &( xSet ) );
}
if( xSet.xTargetIndex == ( BaseType_t ) ipSIZE_OF_IPv6_ADDRESS )
{
xResult = 1;
}
}
if( xResult != 1 )
{
xSet.pucTarget = ( uint8_t * ) pvDestination;
( void ) memset( xSet.pucTarget, 0, ipSIZE_OF_IPv6_ADDRESS );
}
return xResult;
}
/*-----------------------------------------------------------*/
/* *INDENT-OFF* */
#endif /* ipconfigUSE_IPv6 != 0 */
/* *INDENT-ON* */