首页 > 解决方案 > 字符数组的 RFC 1071 校验和

问题描述

我很难理解RFC 1071中的以下校验和算法:

The following "C" code algorithm computes the checksum with an inner
loop that sums 16-bits at a time in a 32-bit accumulator.

in 6
{
    /* Compute Internet Checksum for "count" bytes
    *         beginning at location "addr".
    */
    register long sum = 0;

    while( count > 1 )  {
        /*  This is the inner loop */
        sum += * (unsigned short) addr++;
        count -= 2;
    }

    /*  Add left-over byte, if any */
    if( count > 0 )
        sum += * (unsigned char *) addr;

    /*  Fold 32-bit sum to 16 bits */
    while (sum>>16)
        sum = (sum & 0xffff) + (sum >> 16);
    
    checksum = ~sum;
}

我的目标是获取一个 char 数组并计算其校验和,但我不确定未定义的变量是什么。addr和/或的数据类型是什么checksum,如何将 char 数组转换为可用于校验和过程的格式?我知道 count 是存储在 addr 中的字节数。

编辑:到目前为止,我正在考虑将 char 数组转换为整数,然后获取字节数:

int char_int = sscanf(char_array, "%d", &i);
int addr = char_int;
int count = sizeof(char_int);

标签: cchecksum

解决方案


由于内部循环以 16 位增量处理数据,addr因此必须是指向 16 位值的指针,即uint16_t * addr.

checksum是您希望存储最终结果的任何数据类型。如果您正在计算 16 位校验和,它也将是uint16_t.

注意sum应该是unsigned long,不是long。它在实践中有效,因为网络数据包通常不够大,以至于校验和会溢出 a long(数据包必须至少为 32K 字节)。但是,如果您正在编写通用代码,则应该进行防御性编码。

您可以在以下位置找到使用适当的可移植数据类型的实现:

http://www.microhowto.info/howto/calculate_an_internet_protocol_checksum_in_c.html

uint16_t ip_checksum(void* vdata,size_t length) {
    // Cast the data pointer to one that can be indexed.
    char* data=(char*)vdata;

    // Initialise the accumulator.
    uint32_t acc=0xffff;

    // Handle complete 16-bit blocks.
    for (size_t i=0;i+1<length;i+=2) {
        uint16_t word;
        memcpy(&word,data+i,2);
        acc+=ntohs(word);
        if (acc>0xffff) {
            acc-=0xffff;
        }
    }

    // Handle any partial block at the end of the data.
    if (length&1) {
        uint16_t word=0;
        memcpy(&word,data+length-1,1);
        acc+=ntohs(word);
        if (acc>0xffff) {
            acc-=0xffff;
        }
    }

    // Return the checksum in network byte order.
    return htons(~acc);
}

推荐阅读