Zeroing ICMP _and_ IP header checksum when offloading calculation to NXP RT CPU

Hi there,

I am using FreeRTOS+TCP 4.2.0 on a NXP i.MXRT1170.
First steps are completed, obtaining an IP address with DHCP works however I noticed some issues with sending ICMP packet responses.

  1. When the FreeRTOS IP stack is providing the checksums, everything works as expected
  2. When the checksum calculation is offloaded to the CPU (and disabled in FreeRTOS) by using the TX accelerator of the CPU by providing two settings to the enet_config_t struct
/*!< Insert IP header checksum and protocol checksum. */
	config.txAccelerConfig = kENET_TxAccelIpCheckEnabled |	
							 kENET_TxAccelProtoCheckEnabled; 

then DHCP works as expected, ICMP packets are received, responses are sent however the responses are disregarded by the server due to a wrong IP header checksum.
The ICMP checksum in that case is correctly calculated but the IP header checksum (only in ICMP packets) is always 0xffff. I thought that this would be the case when the checksum is calculated twice, so I added a line in FreeRTOS_ICMP.c which zeros not only the ICMP header checksum but also the IP header checksum like this

        #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
        {
            /* calculate the IP header checksum, in case the driver won't do that. */
            pxIPHeader->usHeaderChecksum = 0x00U;
            pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), uxIPHeaderSizePacket( pxNetworkBuffer ) );
            pxIPHeader->usHeaderChecksum = ( uint16_t ) ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );

            /* calculate the ICMP checksum for an outgoing packet. */
            ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxICMPPacket, pxNetworkBuffer->xDataLength, pdTRUE );
        }
        #else
        {
            /* Just to prevent compiler warnings about unused parameters. */
            ( void ) pxNetworkBuffer;

            /* Many EMAC peripherals will only calculate the ICMP checksum
             * correctly if the field is nulled beforehand. */
            pxICMPHeader->usChecksum = 0U;
             /* mod: also setting ip header checksum to zero*/
            pxIPHeader->usHeaderChecksum = 0x00U;
        }
        #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */

With this modification, everything works as expected when offloading the checksum calculation to the CPU.
I don’t know if this causes any side effects for other CPUs.

Best regards,
Rainer

The RT 1170 reference manual says “For ICMP-over-IPv4 packets, the Checksum field in the ICMP
packet must always be 16’h0000 in both modes, because
pseudo-headers are not defined for such packets. If it does not
equal 16’h0000, an incorrect checksum may be inserted into the
packet.” So your change should be ok just for RT1170, hard to tell whether other EMAC’s/CPU need checksum zeroing without confirming the same with respective reference manual.

Since this is a change thats specific to a particular hardware/platform, I would suggest placing these changes in your network interface file. Something like this should work:

BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxBuffer,
                                    BaseType_t bReleaseAfterSend )
{


    #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
    {
        ProtocolPacket_t * pxPacket;

        pxPacket = ( ProtocolPacket_t * ) ( pxBuffer->pucEthernetBuffer );

        if( ( pxPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) &&
            ( pxPacket->xICMPPacket.xIPHeader.ucProtocol != ipPROTOCOL_UDP ) &&
            ( pxPacket->xICMPPacket.xIPHeader.ucProtocol != ipPROTOCOL_TCP ) )
        {
            pxPacket->xICMPPacket.xIPHeader.usHeaderChecksum = 0x0U;
        }
    }
    #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */


      .
      .
      .



    }
    #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */
1 Like

I could not find this statement in the RT1170 reference manual, but there is a very similar statement in the STM32F75 manual.

Btw: I did not insert the zeroing of the ICMP checksum. That was part of FreeRTOS+TCP already.
I only added zeroing the ip packet checksum.

Thanks!
That works like a charm.