Skip to content

Commit 402c35a

Browse files
committed
Fix TCP IPv4 tx checksum length for padded RX frames
prvTCPReturnPacket_IPV4() passed pxNetworkBuffer->xDataLength to usGenerateProtocolChecksum(), but at that point xDataLength still holds the length of the incoming frame (e.g. 60 bytes for a SYN padded to the Ethernet minimum) rather than the outgoing reply (typically 66 bytes for a SYNACK with options). The checksum was computed over the wrong byte range, yielding a bogus value. Mirror the approach used in prvTCPReturnPacket_IPV6() and pass ulLen + ipSIZE_OF_ETH_HEADER explicitly. Add a unit test that installs a content-dependent stub for usGenerateProtocolChecksum and asserts the written checksum matches the sum over the outgoing packet length. Verified the test fails against the unfixed source and passes after the fix.
1 parent ef50082 commit 402c35a

2 files changed

Lines changed: 131 additions & 1 deletion

File tree

source/FreeRTOS_TCP_Transmission_IPv4.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,16 @@ void prvTCPReturnPacket_IPV4( FreeRTOS_Socket_t * pxSocket,
208208

209209
#if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
210210
{
211+
uint32_t ulTotalLength;
212+
211213
/* calculate the IP header checksum, in case the driver won't do that. */
212214
pxIPHeader->usHeaderChecksum = 0x00U;
213215
pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), uxIPHeaderSize );
214216
pxIPHeader->usHeaderChecksum = ( uint16_t ) ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
215217

216218
/* calculate the TCP checksum for an outgoing packet. */
217-
( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxTCPPacket, pxNetworkBuffer->xDataLength, pdTRUE );
219+
ulTotalLength = ulLen + ipSIZE_OF_ETH_HEADER;
220+
( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxTCPPacket, ulTotalLength, pdTRUE );
218221
}
219222
#endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */
220223

test/unit-test/FreeRTOS_TCP_Transmission/FreeRTOS_TCP_Transmission_utest.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2653,3 +2653,130 @@ void test_prvTCPSendReset( void )
26532653
TEST_ASSERT_EQUAL( tcpTCP_FLAG_ACK | tcpTCP_FLAG_RST, pxTCPPacket->xTCPHeader.ucTCPFlags );
26542654
TEST_ASSERT_EQUAL( 0x50, pxTCPPacket->xTCPHeader.ucTCPOffset );
26552655
}
2656+
2657+
/* Stub for usGenerateProtocolChecksum that computes a simple byte-sum
2658+
* "checksum" over [ETH_HEADER, uxBufferLength) and writes it to the TCP
2659+
* checksum field. A snapshot of the buffer at stub-invocation time is
2660+
* saved so the test can recompute the expected value independent of any
2661+
* post-checksum mutations the SUT performs (MAC rewrite, port flip, ...). */
2662+
static uint8_t ucChecksumStubSnapshot[ ipconfigNETWORK_MTU ];
2663+
static size_t xChecksumStubBufferLength;
2664+
2665+
static uint16_t usGenerateProtocolChecksum_ByteSum( uint8_t * pucEthernetBuffer,
2666+
size_t uxBufferLength,
2667+
BaseType_t xOutgoingPacket,
2668+
int cmock_num_calls )
2669+
{
2670+
const size_t xChecksumOffset = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + 16U;
2671+
uint16_t usSum = 0U;
2672+
size_t i;
2673+
2674+
( void ) xOutgoingPacket;
2675+
( void ) cmock_num_calls;
2676+
2677+
pucEthernetBuffer[ xChecksumOffset ] = 0U;
2678+
pucEthernetBuffer[ xChecksumOffset + 1U ] = 0U;
2679+
2680+
for( i = ipSIZE_OF_ETH_HEADER; i < uxBufferLength; i++ )
2681+
{
2682+
usSum = ( uint16_t ) ( usSum + pucEthernetBuffer[ i ] );
2683+
}
2684+
2685+
pucEthernetBuffer[ xChecksumOffset ] = ( uint8_t ) ( usSum >> 8 );
2686+
pucEthernetBuffer[ xChecksumOffset + 1U ] = ( uint8_t ) ( usSum & 0xFFU );
2687+
2688+
memcpy( ucChecksumStubSnapshot, pucEthernetBuffer, uxBufferLength );
2689+
xChecksumStubBufferLength = uxBufferLength;
2690+
return ipCORRECT_CRC;
2691+
}
2692+
2693+
/**
2694+
* @brief Verify that prvTCPReturnPacket computes the TCP checksum over the
2695+
* outgoing packet length, not over the stale xDataLength which may
2696+
* reflect an incoming frame padded to the Ethernet minimum (60 bytes).
2697+
*/
2698+
void test_prvTCPReturnPacket_ChecksumReceivesOutgoingLength( void )
2699+
{
2700+
const uint32_t ulLen = ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + 12U; /* 52 */
2701+
const size_t xRxDataLength = ipconfigETHERNET_MINIMUM_PACKET_BYTES; /* 60 */
2702+
const size_t xChecksumOffset = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + 16U;
2703+
size_t i;
2704+
uint16_t usExpectedChecksum;
2705+
uint16_t usActualChecksum;
2706+
2707+
pxSocket = &xSocket;
2708+
pxNetworkBuffer = &xNetworkBuffer;
2709+
pxNetworkBuffer->pucEthernetBuffer = ucEthernetBuffer;
2710+
pxNetworkBuffer->xDataLength = xRxDataLength;
2711+
2712+
TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;
2713+
struct xNetworkEndPoint xEndPoint = { 0 };
2714+
struct xNetworkInterface xInterface;
2715+
struct xNetworkEndPoint * pxEndPoint = &xEndPoint;
2716+
2717+
xEndPoint.pxNetworkInterface = &xInterface;
2718+
xEndPoint.ipv4_settings.ulIPAddress = 0xC0C0C0C0;
2719+
xEndPoint.pxNetworkInterface->pfOutput = &NetworkInterfaceOutputFunction_Stub;
2720+
NetworkInterfaceOutputFunction_Stub_Called = 0;
2721+
pxSocket->pxEndPoint = &xEndPoint;
2722+
pxNetworkBuffer->pxEndPoint = &xEndPoint;
2723+
2724+
pxSocket->u.xTCP.rxStream = ( StreamBuffer_t * ) 0x12345678;
2725+
pxSocket->u.xTCP.uxRxStreamSize = 1500;
2726+
pxSocket->u.xTCP.bits.bLowWater = pdFALSE;
2727+
pxSocket->u.xTCP.bits.bRxStopped = pdFALSE;
2728+
pxSocket->u.xTCP.usMSS = 1000;
2729+
pxSocket->u.xTCP.ucMyWinScaleFactor = 0;
2730+
pxSocket->u.xTCP.bits.bSendKeepAlive = pdFALSE;
2731+
pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = 100;
2732+
pxTCPWindow->xSize.ulRxWindowLength = 500;
2733+
pxTCPWindow->rx.ulCurrentSequenceNumber = 50;
2734+
2735+
/* Populate the tail of the buffer (bytes ulLen+ETH..xRxDataLength) with a
2736+
* distinctive pattern so that including them in the sum produces a
2737+
* different result than summing only the outgoing bytes. */
2738+
for( i = 0; i < sizeof( ucEthernetBuffer ); i++ )
2739+
{
2740+
ucEthernetBuffer[ i ] = 0U;
2741+
}
2742+
2743+
for( i = ulLen + ipSIZE_OF_ETH_HEADER; i < xRxDataLength; i++ )
2744+
{
2745+
ucEthernetBuffer[ i ] = 0xFFU;
2746+
}
2747+
2748+
uxIPHeaderSizePacket_IgnoreAndReturn( ipSIZE_OF_IPv4_HEADER );
2749+
uxStreamBufferFrontSpace_ExpectAnyArgsAndReturn( 1000 );
2750+
FreeRTOS_min_uint32_ExpectAnyArgsAndReturn( 500 );
2751+
usGenerateChecksum_ExpectAnyArgsAndReturn( 0x1111 );
2752+
usGenerateProtocolChecksum_Stub( usGenerateProtocolChecksum_ByteSum );
2753+
eARPGetCacheEntry_ExpectAnyArgsAndReturn( eResolutionCacheHit );
2754+
eARPGetCacheEntry_ReturnThruPtr_ppxEndPoint( &pxEndPoint );
2755+
2756+
xChecksumStubBufferLength = 0U;
2757+
2758+
prvTCPReturnPacket( pxSocket, pxNetworkBuffer, ulLen, pdFALSE );
2759+
2760+
TEST_ASSERT_NOT_EQUAL( 0U, xChecksumStubBufferLength );
2761+
2762+
/* Read the checksum the stub actually wrote into the packet, from the
2763+
* snapshot taken at stub invocation (before any post-checksum mutations
2764+
* by the SUT). */
2765+
usActualChecksum = ( uint16_t ) ( ( ucChecksumStubSnapshot[ xChecksumOffset ] << 8 ) |
2766+
ucChecksumStubSnapshot[ xChecksumOffset + 1U ] );
2767+
2768+
/* Recompute the expected checksum on the same snapshot, summing over the
2769+
* full outgoing packet length. With the bug the stub saw only
2770+
* xRxDataLength bytes and its sum differs from this reference. */
2771+
ucChecksumStubSnapshot[ xChecksumOffset ] = 0U;
2772+
ucChecksumStubSnapshot[ xChecksumOffset + 1U ] = 0U;
2773+
2774+
usExpectedChecksum = 0U;
2775+
2776+
for( i = ipSIZE_OF_ETH_HEADER; i < ulLen + ipSIZE_OF_ETH_HEADER; i++ )
2777+
{
2778+
usExpectedChecksum = ( uint16_t ) ( usExpectedChecksum + ucChecksumStubSnapshot[ i ] );
2779+
}
2780+
2781+
TEST_ASSERT_EQUAL_HEX16( usExpectedChecksum, usActualChecksum );
2782+
}

0 commit comments

Comments
 (0)