Ask Your Question
0

tcpchecksum calculator

asked 2022-12-24 08:04:46 +0000

chakka.lokesh gravatar image

I've written small code for calculating tcp checksum. This is working fine if TCP payload is of even length. But if it is odd length, it is coming 1 less. Can someone help me identifying where the issue(s) is ....

static uint32_t check_sum_step( uint32_t check_sum, const uint16_t new )
{
     uint32_t over_flow;

     check_sum += htons(new);
     while( check_sum & 0xffff0000 )
     {   
          over_flow = check_sum >> 16; 
          check_sum &= 0x0000ffff;
          check_sum += over_flow;
     }   
     return check_sum;
}

uint16_t tcp_udp_check_sum_16_rfc( char const * const begin_ptr, char * const end_ptr, uint8_t *src_ip_8, uint8_t const *dest_ip_8, const uint8_t ip_addr_size_bits, const uint8_t protocol )
{
     uint16_t const * const begin = (uint16_t*)begin_ptr;
     uint16_t const * const end = (uint16_t*)end_ptr;
     uint16_t const * const src_ip_16 = (uint16_t*)src_ip_8;
     uint16_t const * const dest_ip_16 = (uint16_t*)dest_ip_8;
     const uint8_t ip_addr_size_bits_16 = ip_addr_size_bits/16;
     uint16_t const *addr = begin;
     uint32_t check_sum = 0;
     uint16_t i;

     for( i=0; i<ip_addr_size_bits_16; i++ )
     {
          check_sum = check_sum_step( check_sum, src_ip_16[i] );
          check_sum = check_sum_step( check_sum, dest_ip_16[i] );
     }   
     end_ptr[0] = 0;
     for( ; addr<end; addr++ )
     {   
          check_sum = check_sum_step( check_sum, addr[0] );
     }   
     check_sum = check_sum + protocol + ( (uint16_t)( end_ptr - begin_ptr ) );
     check_sum = check_sum_step( check_sum, 0 );
     check_sum = ~check_sum;
     return (uint16_t)check_sum;
}

/*    int main()
    {
    uint8_t src_ipv4_addr[4] = { 192, 168, 10, 11 };
    uint8_t dest_ipv4_addr[4] = { 192, 168, 10, 22 };
    char tcp_hdr_payload[] = { 0xb3, 0xa4, 0x2a, 0xf8, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xc9, 0x50, 0x10, 0x1b, 0x5d, 0xc4, 0xad, 0x00, 0x00, 0x62, 0x62, 0x62, 0x62, 0x03 };
    uint16_t checksum = tcp_udp_check_sum_16_rfc( tcp_hdr_payload, tcp_hdr_payload+20+5, src_ipv4_addr, dest_ipv4_addr, 32, IPPROTO_TCP );
    printf( "%s %d checksum: %u\n", __func__, __LINE__, checksum );
    }
*/
edit retag flag offensive close merge delete

1 Answer

Sort by » oldest newest most voted
1

answered 2022-12-26 13:59:35 +0000

André gravatar image

updated 2022-12-26 15:35:03 +0000

But if it is odd length, it is coming 1 less.

That is because of this line:

uint16_t const * const dest_ip_16 = (uint16_t*)dest_ip_8;

You are converting a pointer to char to a pointer to short (16 bit word). Since words must be word-aligned, it cannot point to an odd memory address (char* can), so it is rounded down (lowest bit cleared).
But why bother converting it in the first place?

Note that an overflow of an addition can only be 1 digit (the value 1, that is why CPU's have a 'carry bit flag'). Instead of the check for overflow after every addition, it is faster to do that after all additions. Also calling a function (check_sum_step) for just one calculation makes your code slow. That is fine if you just want to understand how it works, but don't use it in a real application.

The TCP/UDP/IP checksums are done in one's complement arithmetic. This has an unique property: it doesn't matter if the calculation is done in Big Endian or Little Endian, so no need for htons() calls.
It also means that you can do the additions in bigger chunks (32 or 64 bits).
But all modern processors use two's complement arithmetic; to avoid having both +0 and -0. (To do -a in 1's is in c-code neg_a = ~a, but in 2's neg_a = -a or neg_a = ~a + 1.) So this needs to be corrected 2's to 1's complement by adding the overflow.

So you can do the calculation using a 64-bit variable unsigned long long sum and add in chunks of 32-bit sum += *buf++; (hey, an IPv4 address is also 32-bit!), add remaining if less than 32 bit.
After that 'fold' 64 bit to 32 sum = (sum >> 32) + (sum & 0xFFFFFFFF); sum += (sum >> 32); and 32 to 16 sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16);. Finally return the one's complement result return ~sum;

If you want to dig deeper into how-to and performance, than read this: https://locklessinc.com/articles/tcp_...

edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

2 followers

Stats

Asked: 2022-12-24 08:02:20 +0000

Seen: 1,506 times

Last updated: Dec 26 '22