uint16_t usGenerateProtocolChecksum()

in FreeRTOS_IP.c [2671:2994]


uint16_t usGenerateProtocolChecksum( uint8_t * pucEthernetBuffer,
                                     size_t uxBufferLength,
                                     BaseType_t xOutgoingPacket )
{
    uint32_t ulLength;
    uint16_t usChecksum;           /* The checksum as calculated. */
    uint16_t usChecksumFound = 0U; /* The checksum as found in the incoming packet. */
    const IPPacket_t * pxIPPacket;
    UBaseType_t uxIPHeaderLength;
    ProtocolPacket_t * pxProtPack;
    uint8_t ucProtocol;

    #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
        const char * pcType;
    #endif
    uint16_t usLength;
    uint16_t ucVersionHeaderLength;
    DEBUG_DECLARE_TRACE_VARIABLE( BaseType_t, xLocation, 0 );

    /* Introduce a do-while loop to allow use of break statements.
     * Note: MISRA prohibits use of 'goto', thus replaced with breaks. */
    do
    {
        /* Check for minimum packet size. */
        if( uxBufferLength < sizeof( IPPacket_t ) )
        {
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 1 );
            break;
        }

        /* Parse the packet length. */
        pxIPPacket = ipCAST_CONST_PTR_TO_CONST_TYPE_PTR( IPPacket_t, pucEthernetBuffer );

        /* Per https://tools.ietf.org/html/rfc791, the four-bit Internet Header
         * Length field contains the length of the internet header in 32-bit words. */
        ucVersionHeaderLength = pxIPPacket->xIPHeader.ucVersionHeaderLength;
        ucVersionHeaderLength = ( ucVersionHeaderLength & ( uint8_t ) 0x0FU ) << 2;
        uxIPHeaderLength = ( UBaseType_t ) ucVersionHeaderLength;

        /* Check for minimum packet size. */
        if( uxBufferLength < ( sizeof( IPPacket_t ) + ( uxIPHeaderLength - ipSIZE_OF_IPv4_HEADER ) ) )
        {
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 2 );
            break;
        }

        usLength = pxIPPacket->xIPHeader.usLength;
        usLength = FreeRTOS_ntohs( usLength );

        if( uxBufferLength < ( size_t ) ( ipSIZE_OF_ETH_HEADER + ( size_t ) usLength ) )
        {
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 3 );
            break;
        }

        /* Identify the next protocol. */
        ucProtocol = pxIPPacket->xIPHeader.ucProtocol;

        /* N.B., if this IP packet header includes Options, then the following
         * assignment results in a pointer into the protocol packet with the Ethernet
         * and IP headers incorrectly aligned. However, either way, the "third"
         * protocol (Layer 3 or 4) header will be aligned, which is the convenience
         * of this calculation. */
        pxProtPack = ipCAST_PTR_TO_TYPE_PTR( ProtocolPacket_t, &( pucEthernetBuffer[ uxIPHeaderLength - ipSIZE_OF_IPv4_HEADER ] ) );

        /* Switch on the Layer 3/4 protocol. */
        if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
        {
            if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_UDP_HEADER ) )
            {
                usChecksum = ipINVALID_LENGTH;
                DEBUG_SET_TRACE_VARIABLE( xLocation, 4 );
                break;
            }

            if( xOutgoingPacket != pdFALSE )
            {
                /* Clear the UDP checksum field before calculating it. */
                pxProtPack->xUDPPacket.xUDPHeader.usChecksum = 0U;
            }
            else
            {
                usChecksumFound = pxProtPack->xUDPPacket.xUDPHeader.usChecksum;
            }

            #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
                {
                    pcType = "UDP";
                }
            #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
        }
        else if( ucProtocol == ( uint8_t ) ipPROTOCOL_TCP )
        {
            if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_TCP_HEADER ) )
            {
                usChecksum = ipINVALID_LENGTH;
                DEBUG_SET_TRACE_VARIABLE( xLocation, 5 );
                break;
            }

            if( xOutgoingPacket != pdFALSE )
            {
                /* Clear the TCP checksum field before calculating it. */
                pxProtPack->xTCPPacket.xTCPHeader.usChecksum = 0U;
            }
            else
            {
                usChecksumFound = pxProtPack->xTCPPacket.xTCPHeader.usChecksum;
            }

            #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
                {
                    pcType = "TCP";
                }
            #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
        }
        else if( ( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) ||
                 ( ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
        {
            if( uxBufferLength < ( uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_ICMP_HEADER ) )
            {
                usChecksum = ipINVALID_LENGTH;
                DEBUG_SET_TRACE_VARIABLE( xLocation, 6 );
                break;
            }

            if( xOutgoingPacket != pdFALSE )
            {
                /* Clear the ICMP/IGMP checksum field before calculating it. */
                pxProtPack->xICMPPacket.xICMPHeader.usChecksum = 0U;
            }
            else
            {
                usChecksumFound = pxProtPack->xICMPPacket.xICMPHeader.usChecksum;
            }

            #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
                {
                    if( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
                    {
                        pcType = "ICMP";
                    }
                    else
                    {
                        pcType = "IGMP";
                    }
                }
            #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
        }
        else
        {
            /* Unhandled protocol, other than ICMP, IGMP, UDP, or TCP. */
            usChecksum = ipUNHANDLED_PROTOCOL;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 7 );
            break;
        }

        /* The protocol and checksum field have been identified. Check the direction
         * of the packet. */
        if( xOutgoingPacket != pdFALSE )
        {
            /* This is an outgoing packet. The CRC-field has been cleared. */
        }
        else if( ( usChecksumFound == 0U ) && ( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) )
        {
            #if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 )
                {
                    /* Sender hasn't set the checksum, drop the packet because
                     * ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS is not set. */
                    usChecksum = ipWRONG_CRC;
                    #if ( ipconfigHAS_PRINTF != 0 )
                        {
                            static BaseType_t xCount = 0;

                            if( xCount < 5 )
                            {
                                FreeRTOS_printf( ( "usGenerateProtocolChecksum: UDP packet from %xip without CRC dropped\n",
                                                   FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ) ) );
                                xCount++;
                            }
                        }
                    #endif /* ( ipconfigHAS_PRINTF != 0 ) */
                }
            #else /* if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */
                {
                    /* Sender hasn't set the checksum, no use to calculate it. */
                    usChecksum = ipCORRECT_CRC;
                }
            #endif /* if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */
            DEBUG_SET_TRACE_VARIABLE( xLocation, 8 );
            break;
        }
        else
        {
            /* Other incoming packet than UDP. */
        }

        usLength = pxIPPacket->xIPHeader.usLength;
        usLength = FreeRTOS_ntohs( usLength );
        ulLength = ( uint32_t ) usLength;
        ulLength -= ( ( uint16_t ) uxIPHeaderLength ); /* normally minus 20 */

        if( ( ulLength < ( ( uint32_t ) sizeof( pxProtPack->xUDPPacket.xUDPHeader ) ) ) ||
            ( ulLength > ( ( uint32_t ) ipconfigNETWORK_MTU - ( uint32_t ) uxIPHeaderLength ) ) )
        {
            #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
                {
                    FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: len invalid: %u\n", pcType, ( unsigned ) ulLength ) );
                }
            #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */

            /* Again, in a 16-bit return value there is no space to indicate an
             * error.  For incoming packets, 0x1234 will cause dropping of the packet.
             * For outgoing packets, there is a serious problem with the
             * format/length */
            usChecksum = ipINVALID_LENGTH;
            DEBUG_SET_TRACE_VARIABLE( xLocation, 9 );
            break;
        }

        if( ucProtocol <= ( uint8_t ) ipPROTOCOL_IGMP )
        {
            /* ICMP/IGMP do not have a pseudo header for CRC-calculation. */
            usChecksum = ( uint16_t )
                         ( ~usGenerateChecksum( 0U,
                                                ( const uint8_t * ) &( pxProtPack->xICMPPacket.xICMPHeader ), ( size_t ) ulLength ) );
        }
        else
        {
            uint32_t ulByteCount = ulLength;
            ulByteCount += 2U * ipSIZE_OF_IPv4_ADDRESS;

            /* For UDP and TCP, sum the pseudo header, i.e. IP protocol + length
             * fields */
            usChecksum = ( uint16_t ) ( ulLength + ( ( uint16_t ) ucProtocol ) );

            /* And then continue at the IPv4 source and destination addresses. */
            usChecksum = ( uint16_t )
                         ( ~usGenerateChecksum( usChecksum,
                                                ipPOINTER_CAST( const uint8_t *, &( pxIPPacket->xIPHeader.ulSourceIPAddress ) ),
                                                ulByteCount ) );
            /* Sum TCP header and data. */
        }

        if( xOutgoingPacket == pdFALSE )
        {
            /* This is in incoming packet. If the CRC is correct, it should be zero. */
            if( usChecksum == 0U )
            {
                usChecksum = ( uint16_t ) ipCORRECT_CRC;
            }
            else
            {
                usChecksum = ( uint16_t ) ipWRONG_CRC;
            }
        }
        else
        {
            if( ( usChecksum == 0U ) && ( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) )
            {
                /* In case of UDP, a calculated checksum of 0x0000 is transmitted
                 * as 0xffff. A value of zero would mean that the checksum is not used. */
                usChecksum = ( uint16_t ) 0xffffu;
            }
        }

        usChecksum = FreeRTOS_htons( usChecksum );

        if( xOutgoingPacket != pdFALSE )
        {
            switch( ucProtocol )
            {
                case ipPROTOCOL_UDP:
                    pxProtPack->xUDPPacket.xUDPHeader.usChecksum = usChecksum;
                    break;

                case ipPROTOCOL_TCP:
                    pxProtPack->xTCPPacket.xTCPHeader.usChecksum = usChecksum;
                    break;

                case ipPROTOCOL_ICMP:
                case ipPROTOCOL_IGMP:
                    pxProtPack->xICMPPacket.xICMPHeader.usChecksum = usChecksum;
                    break;

                default:

                    /* When there is a default statement, MISRA complains,
                     * but without a default statement, MISRA also complains. */
                    break;
            }

            usChecksum = ( uint16_t ) ipCORRECT_CRC;
        }

        #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
            else if( ( xOutgoingPacket == pdFALSE ) && ( usChecksum != ipCORRECT_CRC ) )
            {
                FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: ID %04X: from %xip to %xip bad crc: %04X\n",
                                         pcType,
                                         FreeRTOS_ntohs( pxIPPacket->xIPHeader.usIdentification ),
                                         ( unsigned ) FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ),
                                         ( unsigned ) FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulDestinationIPAddress ),
                                         FreeRTOS_ntohs( usChecksumFound ) ) );
            }
            else
            {
                /* Nothing. */
            }
        #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
    } while( ipFALSE_BOOL );

    if( ( usChecksum == ipUNHANDLED_PROTOCOL ) ||
        ( usChecksum == ipINVALID_LENGTH ) )
    {
        /* NOP if ipconfigHAS_PRINTF != 0 */
        FreeRTOS_printf( ( "CRC error: %04x location %ld\n", usChecksum, xLocation ) );
    }

    return usChecksum;
}