RFC 1071 fournit l'exemple d'implémentation suivante dans 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;
}
J'ai écrit le code C suivant pour tester l'unité il:
#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;
}
Il est un peu déconcertant que mon code ne prend pas le NOT au bit de sum dans la dernière ligne de la fonction checksum() comme le RFC impl mais l'ajout du bitwise n'a pas brisé mes tests unitaires.
Exécution des rendements de code:
: [email protected]; /tmp/rfc-1071-checksum
OK expected 0x0000, got 0x0000
OK expected 0xddf2, got 0xddf2
j'ai porté cela à Ruby comme suit:
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
j'appelle la méthode comme ceci:
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
Et voici l'unité tests:
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
Notez que mes tests unitaires échouent actuellement, donc j'ai un bug quelque part. Je vais mettre à jour la réponse et accepter quand les tests seront passés. –