将IPv4标头校验和插入虚拟IP标头

时间:2010-12-01 10:12:18

标签: .net c header checksum ipv4

我正在尝试仅根据有效负载内容创建数据包。因此,我将不得不创建虚拟IPv4和UDP标头。我在将IPv4校验和值插入虚拟IP头时遇到了一些问题。校验和值使用Wireshark使用的算法计算。我只是略微修改了代码,所以我可以将校验和值插入IP头。

我的代码如下(使用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
}

上面的代码是简化版。在实际的代码中,我创建了带有头文件和内容的数据包,并将它们写入pcap文件。然后使用Wireshark检查IP校验和是否正确,即与Wireshark本身计算的值相同。我的问题是,如果没有ip_cal_checksum()中的memcpy行,我会为所有创建的数据包获取正确的校验和值。但是,使用memcpy行时,只有第一个校验和是正确的,并且大多数(如果不是全部)其他校验和值都是错误的。

例如,使用上面的代码,无论是否存在memcpy行,第一个计算的校验和值都是0x971f。但是,第二个计算的校验和值是0x9713,没有memcpy行,0xfff3和memcpy。

为什么校验和值会根据memcpy行是否存在而改变,以及如何解决这个问题?

谢谢。

此致 莱恩

1 个答案:

答案 0 :(得分:2)

memcpy行修改标题(通过在其中设置校验和),并且修改后的标题是第二个校验和的输入。

在计算校验和之前,应将校验和字段设置为0,以获得正确的结果。