Как вычислить контрольную сумму заголовка IP RFC 791? - PullRequest
2 голосов
/ 26 декабря 2009

Я перевожу свои собственные IP-пакеты для проекта обучить себя Ruby, и мне нужно вычислить контрольную сумму заголовка IP (как описано в RFC 791 p.14). Один из связанных вопросов, который всплыл, когда я набрал свой вопрос, указал мне на RFC 1071, так что я, вероятно, большую часть пути продвинулся, но для добавления переполнения стека кто-нибудь (возможно, Future Josh) может предоставить некоторый код Ruby для вычисления контрольной суммы, предполагая следующий бит Ruby:

    def build_ip_packet(tos, ttl, protocol, dst_addr, data)
        len = (IP_HEADER_LEN * 4) + data.size

        ip_header = %w{ #{IP_VERSION} #{IP_HEADER_LEN} #{tos} #{len} #{IP_IDENTIFICATION} #{IP_FLAGS_BIT_0} #{IP_FLAGS_BIT_1} #{IP_FLAGS_BIT_2} #{IP_FRAG_OFFSET} #{ttl} #{protocol} #{hdr_checksum} #{src_addr} #{dst_addr} }

        [...]
    end

Константы определены в верхней части файла, но они должны быть понятны, если вы смотрите RFC791 стр.11.

1 Ответ

2 голосов
/ 28 декабря 2009

RFC 1071 предоставляет следующий пример реализации в C:

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;

}

Я написал следующий код C для модульного тестирования:

#include <stdio.h>
#include <stdlib.h>


unsigned short checksum (int count, unsigned short * addr) {
    unsigned long sum = 0;

    while (count > 1) {
        sum += *addr++;
        count -= 2;
    }

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

    while (sum >> 16)
        sum = (sum & 0xffff) + (sum >> 16);

    return (unsigned short)sum;
}

void test (const unsigned short expected, const unsigned short got) {
    printf(
        "%s  expected 0x%04x, got 0x%04x\n",
        (expected == got ? "OK" : "NOK"),
        expected,
        got
    );
}

int main(void) {
    unsigned short *buf = calloc(1024, sizeof(unsigned short));

    buf[0] = 0x0000;

    test(
        0x0,
        checksum(2, buf)
    );

    buf[0] = 0x0001;
    buf[1] = 0xf203;
    buf[2] = 0xf4f5;
    buf[3] = 0xf6f7;

    test(
        0xddf2,
        checksum(8, buf)
    );

    return 0;
}

Немного смущает, что мой код не принимает побитовое НЕ sum в последней строке функции checkum (), как это делает реализация RFC, но добавление побитового НЕ прерывает мои модульные тесты.

Запуск кода дает:

: jglover@jglover-3; /tmp/rfc-1071-checksum 
OK  expected 0x0000, got 0x0000
OK  expected 0xddf2, got 0xddf2

Я перенес это на Ruby следующим образом:

def rfc1071_checksum(header_fields)
    checksum = 0

    header_fields.each{|field| checksum += field}

    while (checksum >> 16) != 0
        checksum = (checksum & 0xffff) + (checksum >> 16)
    end

    return checksum
end

Я называю метод так:

def build_ip_packet(tos, ttl, protocol, dst_addr, data)
    len = (IP_HEADER_LEN * 4) + data.size

    # Header checksum is set to 0 for computing the checksum; see RFC 791 p.14
    hdr_checksum = 0

    src_addr = IPAddr.new('127.0.0.1')

    header_fields = [
        ( ((IP_VERSION << 4) + IP_HEADER_LEN) << 8 ) + ( tos      ),
        len,
        IP_IDENTIFICATION,
        ( (IP_FLAGS_BIT_0 << 15) + (IP_FLAGS_BIT_1 << 14) + (IP_FLAGS_BIT_2 << 13) + (IP_FRAG_OFFSET << 12)),
        ( ttl                                 << 8 ) + ( protocol ),
        hdr_checksum,
        src_addr & 0xff00,  # 16 most significant bits
        src_addr & 0x00ff,  # 16 least significant bits
        dst_addr & 0xff00,  # 16 most significant bits
        dst_addr & 0x00ff,  # 16 least significant bits
    ]

    hdr_checksum = rfc1071_checksum(header_fields)

    return nil
end

А вот и юнит-тесты:

require 'test/unit'
require 'lib/traceroute'

class TestTraceroute < MiniTest::Unit::TestCase
    def test_rfc1071_checksum
        [
            [ 0x0000, [ 0x0000 ] ],
            [
                0xddf2,
                [
                    0x0001f203,
                    0xf4f5f6f7,
                ]
            ],
        ].each{|input_record|
            got      = Traceroute.new.rfc1071_checksum(input_record[1])
            expected = input_record[0]

            assert_equal(got, expected, "rfc1071_checksum: #{input_record[1].inspect}")
        }
    end
end
...