Как вычислить контрольную сумму IP-заголовка RFC 791?
Я перевожу свои собственные IP-пакеты для проекта обучить себя в Ruby, и мне нужно вычислить контрольную сумму заголовка IP (как описано в RFC 791 p.14). Один из связанных с этим вопросов, который возник, когда я набрал здесь свой вопрос, указал мне на RFC 1071, так что я, вероятно, большую часть пути прошёл, но просто для добавления в Stack Overflow, может кто-нибудь (возможно, 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 ответ
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;
}
Немного смущает то, что мой код не принимает побитовое НЕ суммы в последней строке функции 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