Discussion:
Inserting IPv4 header checksum into dummy IP header
(too old to reply)
Rayne
2010-12-01 10:09:26 UTC
Permalink
Hi,

I'm trying to create a packet given only the payload content.
Therefore, I will have to create dummy IPv4 and UDP headers. I'm
having some problem inserting the IPv4 checksum value into the dummy
IP header. The checksum value is calculated using the algorithm used
by Wireshark. I've only modified the code slightly so I can insert the
checksum value into the IP header.

My code is as follows (using Microsoft Visual Studio .NET 2003):

/********************** main.c ***********************/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include <tchar.h>
#include <strsafe.h>
#include "in_cksum.h"

#define SIZE_IP_HDR 20

unsigned char ip_header[] = {0x45, 0x00, 0x05, 0x30, 0x00, 0x00, 0x40,
0x00, 0x20, 0x11, 0x00, 0x00, 0x21, 0x4f, 0x02, 0x7b, 0xcc, 0x5c,
0x46, 0x00};

int main(int argc, char **argv)
{
ip_cal_checksum(ip_header, SIZE_IP_HDR);
ip_header[12] = 0x2d;
ip_cal_checksum(ip_header, SIZE_IP_HDR);
return 0;
}


/********************** in_cksum.h ***********************/
#ifndef IN_CKSUM_H
#define IN_CKSUM_H

typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;

typedef struct
{
const uint8_t *ptr;
int len;
} vec_t;

int in_cksum(const vec_t *vec, int veclen);
uint16_t calculate_cksum(const vec_t *vec, int veclen);
void ip_cal_checksum(const uint8_t *ptr, int len);

#endif /* IN_CKSUM_H */


/********************** in_cksum.c ***********************/
#include "in_cksum.h"

#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1];
ADDCARRY(sum);}

int in_cksum(const vec_t *vec, int veclen)
{
register const uint16_t *w;
register int sum = 0;
register int mlen = 0;
int byte_swapped = 0;

union {
uint8_t c[2];
uint16_t s;
} s_util;
union {
uint16_t s[2];
uint32_t l;
} l_util;

for (; veclen != 0; vec++, veclen--) {
if (vec->len == 0)
continue;
w = (const uint16_t *)vec->ptr;
if (mlen == -1) {
/*
* The first byte of this chunk is the continuation
* of a word spanning between this chunk and the
* last chunk.
*
* s_util.c[0] is already saved when scanning previous
* chunk.
*/
s_util.c[1] = *(const uint8_t *)w;
sum += s_util.s;
w = (const uint16_t *)((const uint8_t *)w + 1);
mlen = vec->len - 1;
} else
mlen = vec->len;
/*
* Force to even boundary.
*/
if ((1 & (unsigned long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
s_util.c[0] = *(const uint8_t *)w;
w = (const uint16_t *)((const uint8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
/*
* Unroll the loop to make overhead from
* branches &c small.
*/
while ((mlen -= 32) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
w += 16;
}
mlen += 32;
while ((mlen -= 8) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
w += 4;
}
mlen += 8;
if (mlen == 0 && byte_swapped == 0)
continue;
REDUCE;
while ((mlen -= 2) >= 0) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= 8;
byte_swapped = 0;
if (mlen == -1) {
s_util.c[1] = *(const uint8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
s_util.c[0] = *(const uint8_t *)w;
}
if (mlen == -1) {
/* The last mbuf has odd # of bytes. Follow the
standard (the odd byte may be shifted left by 8 bits
or not as determined by endian-ness of the machine) */
s_util.c[1] = 0;
sum += s_util.s;
}
REDUCE;
return (~sum & 0xffff);
}

uint16_t calculate_cksum(const vec_t *vec, int veclen)
{
register const uint16_t *w;
register int sum = 0;
register int mlen = 0;
int byte_swapped = 0;

union {
uint8_t c[2];
uint16_t s;
} s_util;
union {
uint16_t s[2];
uint32_t l;
} l_util;

for (; veclen != 0; vec++, veclen--) {
if (vec->len == 0)
continue;
w = (const uint16_t *)vec->ptr;
if (mlen == -1) {
/*
* The first byte of this chunk is the continuation
* of a word spanning between this chunk and the
* last chunk.
*
* s_util.c[0] is already saved when scanning previous
* chunk.
*/
s_util.c[1] = *(const uint8_t *)w;
sum += s_util.s;
w = (const uint16_t *)((const uint8_t *)w + 1);
mlen = vec->len - 1;
} else
mlen = vec->len;
/*
* Force to even boundary.
*/
if ((1 & (unsigned long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
s_util.c[0] = *(const uint8_t *)w;
w = (const uint16_t *)((const uint8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
/*
* Unroll the loop to make overhead from
* branches &c small.
*/
while ((mlen -= 32) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
w += 16;
}
mlen += 32;
while ((mlen -= 8) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
w += 4;
}
mlen += 8;
if (mlen == 0 && byte_swapped == 0)
continue;
REDUCE;
while ((mlen -= 2) >= 0) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= 8;
byte_swapped = 0;
if (mlen == -1) {
s_util.c[1] = *(const uint8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
s_util.c[0] = *(const uint8_t *)w;
}
if (mlen == -1) {
/* The last mbuf has odd # of bytes. Follow the
standard (the odd byte may be shifted left by 8 bits
or not as determined by endian-ness of the machine) */
s_util.c[1] = 0;
sum += s_util.s;
}
REDUCE;
return (~sum & 0xffff);
}

void ip_cal_checksum(const uint8_t *ptr, int len)
{
vec_t cksum_vec[1];
uint16_t ip_checksum = 0;

cksum_vec[0].ptr = ptr;
cksum_vec[0].len = len;
ip_checksum = calculate_cksum(&cksum_vec[0], 1);
printf("%x\n", ip_checksum);
memcpy((void *)&ptr[10], &ip_checksum, 2); // copy checksum value to
IP header
}


The code above is a simplified version. In the actual code, I have
created the packet with the headers and content and wrote them into a
pcap file. Using Wireshark, I then check if the IP checksum is
correct, i.e. the same value as that calculated by Wireshark itself.
My problem is that without the memcpy line in ip_cal_checksum(), I get
the correct checksum values for all the packets created. However, with
the memcpy line, only the first checksum is correct, and most, if not
all of the other checksum values are wrong.

For example, using the code above, the first calculated checksum value
is 0x971f regardless of the presence of the memcpy line. However, the
second calculated checksum value is 0x9713 WITHOUT the memcpy line,
and 0xfff3 WITH the memcpy.

Why does the checksum value change depending on whether the memcpy
line is present, and how can I resolve this?

Thank you.

Regards,
Rayne
Tom Handal
2010-12-04 22:02:20 UTC
Permalink
Post by Rayne
Hi,
I'm trying to create a packet given only the payload content.
Therefore, I will have to create dummy IPv4 and UDP headers. I'm
having some problem inserting the IPv4 checksum value into the dummy
IP header. The checksum value is calculated using the algorithm used
by Wireshark. I've only modified the code slightly so I can insert the
checksum value into the IP header.
/********************** main.c ***********************/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include <tchar.h>
#include <strsafe.h>
#include "in_cksum.h"
#define SIZE_IP_HDR 20
unsigned char ip_header[] = {0x45, 0x00, 0x05, 0x30, 0x00, 0x00, 0x40,
0x00, 0x20, 0x11, 0x00, 0x00, 0x21, 0x4f, 0x02, 0x7b, 0xcc, 0x5c,
0x46, 0x00};
int main(int argc, char **argv)
{
        ip_cal_checksum(ip_header, SIZE_IP_HDR);
        ip_header[12] = 0x2d;
        ip_cal_checksum(ip_header, SIZE_IP_HDR);
        return 0;
}
/********************** in_cksum.h ***********************/
#ifndef IN_CKSUM_H
#define IN_CKSUM_H
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef struct
{
        const uint8_t *ptr;
        int len;
} vec_t;
int in_cksum(const vec_t *vec, int veclen);
uint16_t calculate_cksum(const vec_t *vec, int veclen);
void ip_cal_checksum(const uint8_t *ptr, int len);
#endif /* IN_CKSUM_H */
/********************** in_cksum.c ***********************/
#include "in_cksum.h"
#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1];
ADDCARRY(sum);}
int in_cksum(const vec_t *vec, int veclen)
{
        register const uint16_t *w;
        register int sum = 0;
        register int mlen = 0;
        int byte_swapped = 0;
        union {
                uint8_t c[2];
                uint16_t s;
        } s_util;
        union {
                uint16_t s[2];
                uint32_t l;
        } l_util;
        for (; veclen != 0; vec++, veclen--) {
                if (vec->len == 0)
                        continue;
                w = (const uint16_t *)vec->ptr;
                if (mlen == -1) {
                        /*
                         * The first byte of this chunk is the continuation
                         * of a word spanning between this chunk and the
                         * last chunk.
                         *
                         * s_util.c[0] is already saved when scanning previous
                         * chunk.
                         */
                        s_util.c[1] = *(const uint8_t *)w;
                        sum += s_util.s;
                        w = (const uint16_t *)((const uint8_t *)w + 1);
                        mlen = vec->len - 1;
                } else
                        mlen = vec->len;
                /*
                 * Force to even boundary.
                 */
                if ((1 & (unsigned long) w) && (mlen > 0)) {
                        REDUCE;
                        sum <<= 8;
                        s_util.c[0] = *(const uint8_t *)w;
                        w = (const uint16_t *)((const uint8_t *)w + 1);
                        mlen--;
                        byte_swapped = 1;
                }
                /*
                 * Unroll the loop to make overhead from
                 * branches &c small.
                 */
                while ((mlen -= 32) >= 0) {
                        sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
                        sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
                        sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
                        sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
                        w += 16;
                }
                mlen += 32;
                while ((mlen -= 8) >= 0) {
                        sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
                        w += 4;
                }
                mlen += 8;
                if (mlen == 0 && byte_swapped == 0)
                        continue;
                REDUCE;
                while ((mlen -= 2) >= 0) {
                        sum += *w++;
                }
                if (byte_swapped) {
                        REDUCE;
                        sum <<= 8;
                        byte_swapped = 0;
                        if (mlen == -1) {
                                s_util.c[1] = *(const uint8_t *)w;
                                sum += s_util.s;
                                mlen = 0;
                        } else
                                mlen = -1;
                } else if (mlen == -1)
                        s_util.c[0] = *(const uint8_t *)w;
        }
        if (mlen == -1) {
                /* The last mbuf has odd # of bytes. Follow the
                   standard (the odd byte may be shifted left by 8 bits
                   or not as determined by endian-ness of the machine) */
                s_util.c[1] = 0;
                sum += s_util.s;
        }
        REDUCE;
        return (~sum & 0xffff);
}
uint16_t calculate_cksum(const vec_t *vec, int veclen)
{
        register const uint16_t *w;
        register int sum = 0;
        register int mlen = 0;
        int byte_swapped = 0;
        union {
                uint8_t c[2];
                uint16_t s;
        } s_util;
        union {
                uint16_t s[2];
                uint32_t l;
        } l_util;
        for (; veclen != 0; vec++, veclen--) {
                if (vec->len == 0)
                        continue;
                w = (const uint16_t *)vec->ptr;
                if (mlen == -1) {
                        /*
                         * The first byte of this chunk is the continuation
                         * of a word spanning between this chunk and the
                         * last chunk.
                         *
                         * s_util.c[0] is already saved when scanning previous
                         * chunk.
                         */
                        s_util.c[1] = *(const uint8_t *)w;
                        sum += s_util.s;
                        w = (const uint16_t *)((const uint8_t *)w + 1);
                        mlen = vec->len - 1;
                } else
                        mlen = vec->len;
                /*
                 * Force to even boundary.
                 */
                if ((1 & (unsigned long) w) && (mlen > 0)) {
                        REDUCE;
                        sum <<= 8;
                        s_util.c[0] = *(const uint8_t *)w;
                        w = (const uint16_t *)((const uint8_t *)w + 1);
                        mlen--;
                        byte_swapped = 1;
                }
                /*
                 * Unroll the loop to make overhead from
                 * branches &c small.
                 */
                while ((mlen -= 32) >= 0) {
                        sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
                        sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
                        sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
                        sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
                        w += 16;
                }
                mlen += 32;
                while ((mlen -= 8) >= 0) {
                        sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
                        w += 4;
                }
                mlen += 8;
                if (mlen == 0 && byte_swapped == 0)
                        continue;
                REDUCE;
                while ((mlen -= 2) >= 0) {
                        sum += *w++;
                }
                if (byte_swapped) {
                        REDUCE;
                        sum <<= 8;
                        byte_swapped = 0;
                        if (mlen == -1) {
                                s_util.c[1] = *(const uint8_t *)w;
                                sum += s_util.s;
                                mlen = 0;
                        } else
                                mlen = -1;
                } else if (mlen == -1)
                        s_util.c[0] = *(const uint8_t *)w;
        }
        if (mlen == -1) {
                /* The last mbuf has odd # of bytes. Follow the
                   standard (the odd byte may be shifted left by 8 bits
                   or not as determined by endian-ness of the machine) */
                s_util.c[1] = 0;
                sum += s_util.s;
        }
        REDUCE;
        return (~sum & 0xffff);
}
void ip_cal_checksum(const uint8_t *ptr, int len)
{
        vec_t cksum_vec[1];
        uint16_t ip_checksum = 0;
        cksum_vec[0].ptr = ptr;
        cksum_vec[0].len = len;
        ip_checksum = calculate_cksum(&cksum_vec[0], 1);
        printf("%x\n", ip_checksum);
        memcpy((void *)&ptr[10], &ip_checksum, 2);  // copy checksum value to
IP header
}
The code above is a simplified version. In the actual code, I have
created the packet with the headers and content and wrote them into a
pcap file. Using Wireshark, I then check if the IP checksum is
correct, i.e. the same value as that calculated by Wireshark itself.
My problem is that without the memcpy line in ip_cal_checksum(), I get
the correct checksum values for all the packets created. However, with
the memcpy line, only the first checksum is correct, and most, if not
all of the other checksum values are wrong.
For example, using the code above, the first calculated checksum value
is 0x971f regardless of the presence of the memcpy line. However, the
second calculated checksum value is 0x9713 WITHOUT the memcpy line,
and 0xfff3 WITH the memcpy.
Why does the checksum value change depending on whether the memcpy
line is present, and how can I resolve this?
Thank you.
Regards,
Rayne
The IP Checksum algorithm is very simple actually. The IP header
checksum is just the checksum of the IP header itself, but please note
that the checksum is calculate with certain fields set to 0.

You can see the algorithm and the fields that need to be set to zero
here: http://www.netfor2.com/ipsum.htm

Here is the code:

/*
**************************************************************************
Function: ip_sum_calc
Description: Calculate the 16 bit IP sum.
***************************************************************************
*/
typedef unsigned short u16;
typedef unsigned long u32;

u16 ip_sum_calc(u16 len_ip_header, u16 buff[])
{
u16 word16;
u32 sum=0;
u16 i;

// make 16 bit words out of every two adjacent 8 bit words in the
packet
// and add them up
for (i=0;i<len_ip_header;i=i+2){
word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF);
sum = sum + (u32) word16;
}

// take only 16 bits out of the 32 bit sum and add up the carries
while (sum>>16)
sum = (sum & 0xFFFF)+(sum >> 16);

// one's complement the result
sum = ~sum;

return ((u16) sum);
}

Regards
Tom Handal

Loading...