Inserting IPv4 header checksum into dummy IP header

L

lancer6238

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
 
I

Ike Naar

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

Without the memcpy, you calculate

checksum(450005300000400020110000214f027bcc5c4600) = 971f
checksum(4500053000004000201100002d4f027bcc5c4600) = 9713
^^
1 byte changed

with the memcpy, you calculate

checksum(450005300000400020110000214f027bcc5c4600) = 971f
checksum(450005300000400020111f972d4f027bcc5c4600) = fff3
^^^^^^
3 bytes changed

What else would you expect? The memcpy changes the header.
If the header changes, it will have a different checksum.

By the way, you're casting away const in the memcpy call.
memcpy((void *)&ptr[10], &ip_checksum, 2);

where ptr is a pointer to readonly memory.
The behaviour of your program is undefined after this point.
 
C

Chad

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

Without the memcpy, you calculate

  checksum(450005300000400020110000214f027bcc5c4600) = 971f
  checksum(4500053000004000201100002d4f027bcc5c4600) = 9713
                                   ^^
                                 1 byte changed

with the memcpy, you calculate

  checksum(450005300000400020110000214f027bcc5c4600) = 971f
  checksum(450005300000400020111f972d4f027bcc5c4600) = fff3
                               ^^^^^^
                             3 bytes changed

What else would you expect? The memcpy changes the header.
If the header changes, it will have a different checksum.

I don't get how memcpy() changes the header in this case.
 
C

Chad

[snip]
Why does the checksum value change depending on whether the memcpy
line is present, and how can I resolve this?
Without the memcpy, you calculate
  checksum(450005300000400020110000214f027bcc5c4600) = 971f
  checksum(4500053000004000201100002d4f027bcc5c4600) = 9713
                                   ^^
                                 1 byte changed
with the memcpy, you calculate
  checksum(450005300000400020110000214f027bcc5c4600) = 971f
  checksum(450005300000400020111f972d4f027bcc5c4600) = fff3
                               ^^^^^^
                             3 bytes changed
What else would you expect? The memcpy changes the header.
If the header changes, it will have a different checksum.

I don't get how memcpy() changes the header in this case.

The only thing that I can think of is that memcpy() is doing a deep
copy.
 
S

Stephen Sprunk

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

Without the memcpy, you calculate

checksum(450005300000400020110000214f027bcc5c4600) = 971f
checksum(4500053000004000201100002d4f027bcc5c4600) = 9713
^^
1 byte changed

with the memcpy, you calculate

checksum(450005300000400020110000214f027bcc5c4600) = 971f
checksum(450005300000400020111f972d4f027bcc5c4600) = fff3
^^^^^^
3 bytes changed

What else would you expect? The memcpy changes the header.
If the header changes, it will have a different checksum.

Since the checksum is stored in a field located inside the header that
is the subject of the checksum, the sender calculates it as if that
field were all-bits-zero and then stuffs the resulting value into that
field.

The receiver records the value of that field, sets it to all-bits-zero,
calculates the checksum using the same algorithm, and then compares the
result to the recorded value.

I suspect what's going wrong here is related to this procedure, which he
might not be following correctly.

S
 
S

Stephen Sprunk

The only thing that I can think of is that memcpy() is doing a deep
copy.

memcpy() does not do deep copies, nor would that apply in this case
anyway since the source of the copy is a simple uint16_t.

However, do note that memcpy() will copy the value of ip_checksum in
your machine's native byte order, which may well differ from IP's
network byte order. That's why nearly every system with a TCP/IP stack
provides convenience functions such as htons() to convert between host
and network byte order.

(You may be tempted to find out IP's actual byte order, but if you need
to know that, you're doing it wrong. Use the convenience functions and
let the implementation figure out what, if anything, it needs to do.)

S
 
D

David Thompson

On 01 Dec 2010 10:17, Chad wrote:
However, do note that memcpy() will copy the value of ip_checksum in
your machine's native byte order, which may well differ from IP's
network byte order. That's why nearly every system with a TCP/IP stack
provides convenience functions such as htons() to convert between host
and network byte order.

(You may be tempted to find out IP's actual byte order, but if you need
to know that, you're doing it wrong. Use the convenience functions and
let the implementation figure out what, if anything, it needs to do.)
True in general, but one of the clever(?) features of the 1sC IP
checksum is that it works if computed (and stored or fetched,
consistently) on byte-swapped values (if 8-bit byte). Both 2sC and 1sC
carry from low byte to high, but 1sC also 'end around carries' high to
low so it doesn't really matter which is which. See RFC 1071.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top