/*
 * FreeRTOS+TCP <DEVELOPMENT BRANCH>
 * 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_TCP_Transmission_IPV6.c
 * @brief Module which prepares the packet to be sent through
 * a socket for FreeRTOS+TCP.
 * It depends on  FreeRTOS_TCP_WIN.c, which handles the TCP windowing
 * schemes.
 *
 * Endianness: in this module all ports and IP addresses are stored in
 * host byte-order, except fields in the IP-packets
 */

/* Standard includes. */
#include <stdint.h>
#include <stdio.h>

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_IP_Private.h"
#include "NetworkInterface.h"
#include "NetworkBufferManagement.h"
#include "FreeRTOS_ARP.h"
#include "FreeRTOSIPConfigDefaults.h"
#include "FreeRTOS_ND.h"

#include "FreeRTOS_TCP_IP.h"
#include "FreeRTOS_TCP_Reception.h"
#include "FreeRTOS_TCP_Transmission.h"
#include "FreeRTOS_TCP_State_Handling.h"
#include "FreeRTOS_TCP_Utils.h"

/* Just make sure the contents doesn't get compiled if TCP is not enabled. */
/* *INDENT-OFF* */
#if( ipconfigUSE_IPv6 != 0 ) && ( ipconfigUSE_TCP == 1 )
/* *INDENT-ON* */

/*------------------------------------------------------------------------*/


/**
 * @brief  Return (or send) a packet to the peer. The data is stored in pxBuffer,
 *         which may either point to a real network buffer or to a TCP socket field
 *         called 'xTCP.xPacket'. A temporary xNetworkBuffer will be used to pass
 *         the data to the NIC.
 *
 * @param[in] pxSocket The socket owning the connection.
 * @param[in] pxDescriptor The network buffer descriptor carrying the packet.
 * @param[in] ulLen Length of the packet being sent.
 * @param[in] xReleaseAfterSend pdTRUE if the ownership of the descriptor is
 *                               transferred to the network interface.
 */
void prvTCPReturnPacket_IPV6( FreeRTOS_Socket_t * pxSocket,
                              NetworkBufferDescriptor_t * pxDescriptor,
                              uint32_t ulLen,
                              BaseType_t xReleaseAfterSend )
{
    TCPPacket_IPv6_t * pxTCPPacket = NULL;
    ProtocolHeaders_t * pxProtocolHeaders = NULL;
    IPHeader_IPv6_t * pxIPHeader = NULL;
    BaseType_t xDoRelease = xReleaseAfterSend;
    EthernetHeader_t * pxEthernetHeader = NULL;
    NetworkBufferDescriptor_t * pxNetworkBuffer = pxDescriptor;
    NetworkBufferDescriptor_t xTempBuffer;
    /* memcpy() helper variables for MISRA Rule 21.15 compliance*/
    MACAddress_t xMACAddress;
    const void * pvCopySource = NULL;
    void * pvCopyDest = NULL;
    const size_t uxIPHeaderSize = ipSIZE_OF_IPv6_HEADER;
    IPv6_Address_t xDestinationIPAddress;

    do
    {
        /* Use do/while to be able to break out of the flow */
        if( ( pxNetworkBuffer == NULL ) && ( pxSocket == NULL ) )
        {
            /* Either 'pxNetworkBuffer' or 'pxSocket' should be defined. */
            break;
        }

        /* For sending, a pseudo network buffer will be used, as explained above. */

        if( pxNetworkBuffer == NULL )
        {
            pxNetworkBuffer = &xTempBuffer;

            ( void ) memset( &xTempBuffer, 0, sizeof( xTempBuffer ) );
            #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
                {
                    pxNetworkBuffer->pxNextBuffer = NULL;
                }
            #endif
            pxNetworkBuffer->pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket;
            pxNetworkBuffer->xDataLength = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket );
            xDoRelease = pdFALSE;
        }

        #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
            {
                if( xDoRelease == pdFALSE )
                {
                    /* A zero-copy network driver wants to pass the packet buffer
                     * to DMA, so a new buffer must be created. */
                    pxNetworkBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, ( size_t ) pxNetworkBuffer->xDataLength );

                    if( pxNetworkBuffer != NULL )
                    {
                        xDoRelease = pdTRUE;
                    }
                    else
                    {
                        FreeRTOS_debug_printf( ( "prvTCPReturnPacket: duplicate failed\n" ) );
                    }
                }
            }
        #endif /* ipconfigZERO_COPY_TX_DRIVER */

        configASSERT( pxNetworkBuffer->pucEthernetBuffer != NULL );

        /* 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] */
        pxIPHeader = ( ( IPHeader_IPv6_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER ] ) );

        #ifndef __COVERITY__
            if( pxNetworkBuffer != NULL ) /* LCOV_EXCL_BR_LINE the 2nd branch will never be reached */
        #endif
        {
            /* Map the Ethernet buffer onto a TCPPacket_t struct for easy access to the fields. */

            /* 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] */
            pxTCPPacket = ( TCPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer;
            pxEthernetHeader = ( EthernetHeader_t * ) &( pxTCPPacket->xEthernetHeader );
            /* 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] */
            pxProtocolHeaders = ( ProtocolHeaders_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSize ] );

            if( pxNetworkBuffer->pxEndPoint == NULL )
            {
                prvTCPReturn_SetEndPoint( pxSocket, pxNetworkBuffer, uxIPHeaderSize );

                if( pxNetworkBuffer->pxEndPoint == NULL )
                {
                    if( xDoRelease != pdFALSE )
                    {
                        vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
                    }

                    pxNetworkBuffer = NULL;
                    break;
                }
            }

            /* Fill the packet, swapping from- and to-addresses. */
            if( pxSocket != NULL )
            {
                prvTCPReturn_CheckTCPWindow( pxSocket, pxNetworkBuffer, uxIPHeaderSize );
                prvTCPReturn_SetSequenceNumber( pxSocket, pxNetworkBuffer, uxIPHeaderSize, ulLen );
                ( void ) memcpy( pxIPHeader->xDestinationAddress.ucBytes, pxSocket->u.xTCP.xRemoteIP.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
                ( void ) memcpy( pxIPHeader->xSourceAddress.ucBytes, pxNetworkBuffer->pxEndPoint->ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
            }
            else
            {
                IPv6_Address_t xTempAddress;

                /* Sending data without a socket, probably replying with a RST flag
                 * Just swap the two sequence numbers. */
                vFlip_32( pxProtocolHeaders->xTCPHeader.ulSequenceNumber, pxProtocolHeaders->xTCPHeader.ulAckNr );
                ( void ) memcpy( xTempAddress.ucBytes, pxIPHeader->xDestinationAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
                ( void ) memcpy( pxIPHeader->xDestinationAddress.ucBytes, pxIPHeader->xSourceAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
                ( void ) memcpy( pxIPHeader->xSourceAddress.ucBytes, xTempAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
            }

            /* In IPv6, the "payload length" does not include the size of the IP-header */
            pxIPHeader->usPayloadLength = FreeRTOS_htons( ulLen - sizeof( IPHeader_IPv6_t ) );

            #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
                {
                    /* calculate the TCP checksum for an outgoing packet. */
                    uint32_t ulTotalLength = ulLen + ipSIZE_OF_ETH_HEADER;
                    ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxNetworkBuffer->pucEthernetBuffer, ulTotalLength, pdTRUE );
                }
            #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 */

            vFlip_16( pxProtocolHeaders->xTCPHeader.usSourcePort, pxProtocolHeaders->xTCPHeader.usDestinationPort );

            /* Important: tell NIC driver how many bytes must be sent. */
            pxNetworkBuffer->xDataLength = ( size_t ) ulLen;
            pxNetworkBuffer->xDataLength += ipSIZE_OF_ETH_HEADER;

            #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
                {
                    pxNetworkBuffer->pxNextBuffer = NULL;
                }
            #endif

            ( void ) memcpy( xDestinationIPAddress.ucBytes, pxIPHeader->xDestinationAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
            eARPLookupResult_t eResult;

            eResult = eNDGetCacheEntry( &xDestinationIPAddress, &xMACAddress, &( pxNetworkBuffer->pxEndPoint ) );

            if( eResult == eARPCacheHit )
            {
                pvCopySource = &xMACAddress;
            }
            else
            {
                pvCopySource = &pxEthernetHeader->xSourceAddress;
            }

            /* Fill in the destination MAC addresses. */
            pvCopyDest = &pxEthernetHeader->xDestinationAddress;
            ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxEthernetHeader->xDestinationAddress ) );

            /*
             * Use helper variables for memcpy() to remain
             * compliant with MISRA Rule 21.15.  These should be
             * optimized away.
             */
            /* The source MAC addresses is fixed to 'ipLOCAL_MAC_ADDRESS'. */
            pvCopySource = pxNetworkBuffer->pxEndPoint->xMACAddress.ucBytes;
            pvCopyDest = &pxEthernetHeader->xSourceAddress;
            ( void ) memcpy( pvCopyDest, pvCopySource, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES );

            #if ( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 )
                {
                    if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES )
                    {
                        BaseType_t xIndex;

                        for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ )
                        {
                            pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0U;
                        }

                        pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES;
                    }
                }
            #endif /* if( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */

            /* Send! */
            iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer );

            configASSERT( pxNetworkBuffer->pxEndPoint->pxNetworkInterface != NULL );
            configASSERT( pxNetworkBuffer->pxEndPoint->pxNetworkInterface->pfOutput != NULL );

            NetworkInterface_t * pxInterface = pxNetworkBuffer->pxEndPoint->pxNetworkInterface;
            ( void ) pxInterface->pfOutput( pxInterface, pxNetworkBuffer, xDoRelease );

            if( xDoRelease == pdFALSE )
            {
                /* Swap-back some fields, as pxBuffer probably points to a socket field
                 * containing the packet header. */
                vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort );

                ( void ) memcpy( pxIPHeader->xSourceAddress.ucBytes, pxIPHeader->xDestinationAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
            }
            else
            {
                /* Nothing to do: the buffer has been passed to DMA and will be released after use */
            }
        } /* if( pxNetworkBuffer != NULL ) */
    } while( ipFALSE_BOOL );
}
/*-----------------------------------------------------------*/

/**
 * @brief Let ARP look-up the MAC-address of the peer and initialise the first SYN
 *        packet.
 *
 * @param[in] pxSocket The socket owning the TCP connection. The first packet shall
 *               be created in this socket.
 *
 * @return pdTRUE: if the packet was successfully created and the first SYN can be sent.
 *         Else pdFALSE.
 *
 * @note Connecting sockets have a special state: eCONNECT_SYN. In this phase,
 *       the Ethernet address of the target will be found using ARP. In case the
 *       target IP address is not within the netmask, the hardware address of the
 *       gateway will be used.
 */
BaseType_t prvTCPPrepareConnect_IPV6( FreeRTOS_Socket_t * pxSocket )
{
    TCPPacket_IPv6_t * pxTCPPacket = NULL;
    IPHeader_IPv6_t * pxIPHeader = NULL;
    eARPLookupResult_t eReturned;
    IP_Address_t xRemoteIP;
    MACAddress_t xEthAddress;
    BaseType_t xReturn = pdTRUE;
    uint32_t ulInitialSequenceNumber = 0;
    ProtocolHeaders_t * pxProtocolHeaders = NULL;
    NetworkEndPoint_t * pxEndPoint = NULL;

    #if ( ipconfigHAS_PRINTF != 0 )
        {
            /* Only necessary for nicer logging. */
            ( void ) memset( xEthAddress.ucBytes, 0, sizeof( xEthAddress.ucBytes ) );
        }
    #endif /* ipconfigHAS_PRINTF != 0 */

    ( void ) memset( xRemoteIP.xIP_IPv6.ucBytes, 0, ipSIZE_OF_IPv6_ADDRESS );
    ( void ) memcpy( xRemoteIP.xIP_IPv6.ucBytes, pxSocket->u.xTCP.xRemoteIP.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
    eReturned = eNDGetCacheEntry( &( xRemoteIP.xIP_IPv6 ), &( xEthAddress ), &( pxEndPoint ) );
    FreeRTOS_printf( ( "eNDGetCacheEntry: %d with end-point %p\n", eReturned, ( void * ) pxEndPoint ) );

    if( pxEndPoint != NULL )
    {
        pxSocket->pxEndPoint = pxEndPoint;
    }

    /* 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] */
    pxProtocolHeaders = ( ( ProtocolHeaders_t * )
                          &( pxSocket->u.xTCP.xPacket.u.ucLastPacket[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) );

    switch( eReturned )
    {
        case eARPCacheHit:    /* An ARP table lookup found a valid entry. */
            break;            /* We can now prepare the SYN packet. */

        case eARPCacheMiss:   /* An ARP table lookup did not find a valid entry. */
        case eCantSendPacket: /* There is no IP address, or an ARP is still in progress. */
        default:
            /* Count the number of times it could not find the ARP address. */
            pxSocket->u.xTCP.ucRepCount++;

            FreeRTOS_printf( ( "Looking up %pip with%s end-point\n", ( void * ) xRemoteIP.xIP_IPv6.ucBytes, ( pxEndPoint != NULL ) ? "" : "out" ) );

            if( pxEndPoint != NULL )
            {
                size_t uxNeededSize;
                NetworkBufferDescriptor_t * pxNetworkBuffer;

                uxNeededSize = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + sizeof( ICMPHeader_IPv6_t );
                pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxNeededSize, 0U );

                if( pxNetworkBuffer != NULL )
                {
                    pxNetworkBuffer->pxEndPoint = pxEndPoint;
                    vNDSendNeighbourSolicitation( pxNetworkBuffer, &( xRemoteIP.xIP_IPv6 ) );
                }
            }

            xReturn = pdFALSE;
            break;
    }

    if( xReturn != pdFALSE )
    {
        /* Get a difficult-to-predict initial sequence number for this 4-tuple. */
        ulInitialSequenceNumber = ulApplicationGetNextSequenceNumber( pxSocket->xLocalAddress.ulIP_IPv4,
                                                                      pxSocket->usLocalPort,
                                                                      pxSocket->u.xTCP.xRemoteIP.ulIP_IPv4,
                                                                      pxSocket->u.xTCP.usRemotePort );

        /* Check for a random number generation error. */
        if( ulInitialSequenceNumber == 0U )
        {
            xReturn = pdFALSE;
        }
    }

    if( xReturn != pdFALSE )
    {
        /* The MAC-address of the peer (or gateway) has been found,
         * now prepare the initial TCP packet and some fields in the socket. Map
         * the buffer onto the TCPPacket_IPv6_t struct to easily access it's field. */

        /* 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] */
        pxTCPPacket = ( ( TCPPacket_IPv6_t * ) pxSocket->u.xTCP.xPacket.u.ucLastPacket );
        pxIPHeader = &( pxTCPPacket->xIPHeader );

        /* reset the retry counter to zero. */
        pxSocket->u.xTCP.ucRepCount = 0U;

        /* And remember that the connect/SYN data are prepared. */
        pxSocket->u.xTCP.bits.bConnPrepared = pdTRUE_UNSIGNED;

        /* Now that the Ethernet address is known, the initial packet can be
         * prepared. */
        ( void ) memset( pxSocket->u.xTCP.xPacket.u.ucLastPacket, 0, sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) );

        /* Write the Ethernet address in Source, because it will be swapped by
         * prvTCPReturnPacket(). */
        ( void ) memcpy( ( void * ) ( &( pxTCPPacket->xEthernetHeader.xSourceAddress ) ), ( const void * ) ( &xEthAddress ), sizeof( xEthAddress ) );

        if( pxSocket->bits.bIsIPv6 != pdFALSE_UNSIGNED )
        {
            /* 'ipIPv4_FRAME_TYPE' is already in network-byte-order. */
            pxTCPPacket->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE;

            pxIPHeader->ucVersionTrafficClass = ( uint8_t ) 0x60U;
            pxIPHeader->ucTrafficClassFlow = ( uint8_t ) 0x00;
            pxIPHeader->usFlowLabel = ( uint16_t ) 0x0000U;
            pxIPHeader->usPayloadLength = FreeRTOS_htons( sizeof( TCPHeader_t ) );
            pxIPHeader->ucNextHeader = ( uint8_t ) ipPROTOCOL_TCP;
            pxIPHeader->ucHopLimit = 128;
            /* The Source and Destination addresses will be swapped later. */
            ( void ) memcpy( pxIPHeader->xSourceAddress.ucBytes, pxSocket->u.xTCP.xRemoteIP.xIP_IPv6.ucBytes, sizeof( pxIPHeader->xSourceAddress.ucBytes ) );
            ( void ) memcpy( pxIPHeader->xDestinationAddress.ucBytes, pxSocket->xLocalAddress.xIP_IPv6.ucBytes, sizeof( pxIPHeader->xDestinationAddress.ucBytes ) );
            pxEndPoint = pxSocket->pxEndPoint;
        }
    }

    if( pxEndPoint != NULL )
    {
        pxSocket->pxEndPoint = pxEndPoint;

        pxProtocolHeaders->xTCPHeader.usSourcePort = FreeRTOS_htons( pxSocket->u.xTCP.usRemotePort );
        pxProtocolHeaders->xTCPHeader.usDestinationPort = FreeRTOS_htons( pxSocket->usLocalPort );

        /* We are actively connecting, so the peer's Initial Sequence Number (ISN)
         * isn't known yet. */
        pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber = 0U;

        /* Start with ISN (Initial Sequence Number). */
        pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulInitialSequenceNumber;

        /* The TCP header size is 20 bytes, divided by 4 equals 5, which is put in
         * the high nibble of the TCP offset field. */
        pxProtocolHeaders->xTCPHeader.ucTCPOffset = 0x50U;

        /* Only set the SYN flag. */
        pxProtocolHeaders->xTCPHeader.ucTCPFlags = tcpTCP_FLAG_SYN;

        /* Set the value of usMSS for this socket. */
        prvSocketSetMSS( pxSocket );

        /* The initial sequence numbers at our side are known.  Later
         * vTCPWindowInit() will be called to fill in the peer's sequence numbers, but
         * first wait for a SYN+ACK reply. */
        prvTCPCreateWindow( pxSocket );
    }
    else
    {
        FreeRTOS_printf( ( "prvTCPPrepareConnect: No pxEndPoint yet?\n" ) );
        xReturn = pdFAIL;
    }

    return xReturn;
}
/*-----------------------------------------------------------*/


/**
 * @brief Common code for sending a TCP protocol control packet (i.e. no options, no
 *        payload, just flags).
 *
 * @param[in] pxNetworkBuffer The network buffer received from the peer.
 * @param[in] ucTCPFlags The flags to determine what kind of packet this is.
 *
 * @return pdFAIL always indicating that the packet was not consumed.
 */
BaseType_t prvTCPSendSpecialPktHelper_IPV6( NetworkBufferDescriptor_t * pxNetworkBuffer,
                                            uint8_t ucTCPFlags )
{
    #if ( ipconfigIGNORE_UNKNOWN_PACKETS == 1 )
        /* Configured to ignore unknown packets just suppress a compiler warning. */
        ( void ) pxNetworkBuffer;
        ( void ) ucTCPFlags;
    #else
        {
            /* Map the ethernet buffer onto the TCPPacket_t struct for easy access to the fields. */

            /* 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] */
            TCPPacket_IPv6_t * pxTCPPacket = ( ( TCPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
            const uint32_t ulSendLength =
                ipSIZE_OF_IPv6_HEADER + ipSIZE_OF_TCP_HEADER; /* Plus 0 options. */

            uint8_t ucFlagsReceived = pxTCPPacket->xTCPHeader.ucTCPFlags;
            pxTCPPacket->xTCPHeader.ucTCPFlags = ucTCPFlags;
            pxTCPPacket->xTCPHeader.ucTCPOffset = ( ipSIZE_OF_TCP_HEADER ) << 2;

            if( ( ucFlagsReceived & tcpTCP_FLAG_SYN ) != 0U )
            {
                /* A synchronize packet is received. It counts as 1 pseudo byte of data,
                 * so increase the variable with 1. Before sending a reply, the values of
                 * 'ulSequenceNumber' and 'ulAckNr' will be swapped. */
                uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.ulSequenceNumber );
                ulSequenceNumber++;
                pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( ulSequenceNumber );
            }

            prvTCPReturnPacket( NULL, pxNetworkBuffer, ulSendLength, pdFALSE );
        }
    #endif /* !ipconfigIGNORE_UNKNOWN_PACKETS */

    /* The packet was not consumed. */
    return pdFAIL;
}
/*-----------------------------------------------------------*/


/* *INDENT-OFF* */
#endif /* ( ipconfigUSE_IPv6 != 0 ) && ( ipconfigUSE_TCP == 1 ) */
/* *INDENT-ON* */