Inserting IPv4 header checksum into dummy IP header

Discussion in 'C Programming' started by lancer6238@yahoo.com, Dec 1, 2010.

  1. Guest

    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
    , Dec 1, 2010
    #1
    1. Advertising

  2. Ike Naar Guest

    On 2010-12-01, <> wrote:
    > [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.

    --

    SDF Public Access UNIX System - http://sdf.lonestar.org
    Ike Naar, Dec 1, 2010
    #2
    1. Advertising

  3. Chad Guest

    On Dec 1, 3:10 am, Ike Naar <> wrote:
    > On 2010-12-01, <> wrote:
    >
    > > [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.
    Chad, Dec 1, 2010
    #3
  4. Chad Guest

    On Dec 1, 8:10 am, Chad <> wrote:
    > On Dec 1, 3:10 am, Ike Naar <> wrote:
    >
    >
    >
    >
    >
    > > On 2010-12-01, <> wrote:

    >
    > > > [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.
    Chad, Dec 1, 2010
    #4
  5. On 01 Dec 2010 05:10, Ike Naar wrote:
    > On 2010-12-01, <> wrote:
    >> [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

    --
    Stephen Sprunk "God does not play dice." --Albert Einstein
    CCIE #3723 "God is an inveterate gambler, and He throws the
    K5SSS dice at every possible opportunity." --Stephen Hawking
    Stephen Sprunk, Dec 1, 2010
    #5
  6. On 01 Dec 2010 10:17, Chad wrote:
    > On Dec 1, 8:10 am, Chad <> wrote:
    >> On Dec 1, 3:10 am, Ike Naar <> wrote:
    >>> 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.


    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

    --
    Stephen Sprunk "God does not play dice." --Albert Einstein
    CCIE #3723 "God is an inveterate gambler, and He throws the
    K5SSS dice at every possible opportunity." --Stephen Hawking
    Stephen Sprunk, Dec 1, 2010
    #6
  7. On Wed, 01 Dec 2010 17:29:52 -0600, Stephen Sprunk
    <> wrote:

    > On 01 Dec 2010 10:17, Chad wrote:

    <snip>
    > 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.
    David Thompson, Dec 14, 2010
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ben
    Replies:
    0
    Views:
    550
  2. Christian Reiser

    Schema for IPv4: valid or not?

    Christian Reiser, Aug 13, 2004, in forum: XML
    Replies:
    0
    Views:
    522
    Christian Reiser
    Aug 13, 2004
  3. qazmlp

    code for validating IPv4 address

    qazmlp, Jul 27, 2003, in forum: C Programming
    Replies:
    14
    Views:
    991
    Default User
    Jul 29, 2003
  4. tweak
    Replies:
    14
    Views:
    2,760
    Eric Sosman
    Jun 11, 2004
  5. David Bear

    ipv4 class

    David Bear, Apr 12, 2005, in forum: Python
    Replies:
    0
    Views:
    377
    David Bear
    Apr 12, 2005
Loading...

Share This Page