2009-12-26 22 views
2

Je roule mes propres paquets IP pour un projet Teach-yourself-Ruby, et j'ai besoin de calculer la somme de contrôle d'en-tête IP (comme décrit dans RFC 791 p.14). Une des questions connexes qui a surgi quand j'ai tapé ma question ici m'a pointé à RFC 1071, donc je suis probablement la plupart du temps, mais juste pour ajouter à Stack Overflow, quelqu'un (peut-être Future Josh) peut-il fournir du code Ruby pour calculer la somme de contrôle, en supposant que le bit suivant de Ruby:Comment puis-je calculer une somme de contrôle d'en-tête IP RFC 791?

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 

les constantes sont définies au début du fichier, mais ils doivent être explicites si vous cherchez à RFC791 p.11.

Répondre

2

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 
+0

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. –