2017-12-27 25 views
0

私はIPv6のために私自身の単純化されたTCP/IPスタックを実装しようとしていましたが、現在私の目標はICMPv6エコー要求に答えることです。
私は後の計算に必要なデータを格納するための、以下の構造を使用:CのICMPv6チェックサム計算が間違った結果を返す

typedef uint16_t n_uint16_t;  //network byte order 
typedef uint32_t n_uint32_t; 

n_uint16_t htons(uint16_t n); 
n_uint32_t htonl(uint32_t n); 

struct ipv6hdr { 
    n_uint32_t  vtcfl;   //version, traffic class, flow label 

    n_uint16_t  payload_len; 
    unsigned char nexthdr; 
    unsigned char hop_limit; 

    unsigned char saddr[IP6_ALEN]; //IP6_ALEN = 16 
    unsigned char daddr[IP6_ALEN]; 
}; 

struct icmp6hdr { 
    unsigned char type; 
    unsigned char code; 
    n_uint16_t  cksum; 

    union { 
     n_uint32_t  un_data32[1]; /* type-specific field */ 
     n_uint16_t  un_data16[2]; /* type-specific field */ 
     unsigned char un_data8[4]; /* type-specific field */ 
    } dataun; 
}; 

(endianityを処理するためにも定義されたタイプがある)

IはICMPv6のチェックサムを計算するために以下の機能を使用します。最初の2つは、ICMPv6パケットとIPv6疑似ヘッダフィールドからバッファを作成しています。次に、バッファの16ビットのフィールドの和の1の補数を計算します。

n_uint16_t icmpv6_chksum(struct ipv6hdr *ip6, struct icmp6hdr *icmp) { 
    unsigned char buf[65535]; 
    unsigned char *ptr = &(buf[0]); 
    int chksumlen = 0; 

    //ICMPv6 type 
    memcpy(ptr, &icmp->type, sizeof(icmp->type)); 
    ptr += sizeof(icmp->type); 
    chksumlen += sizeof(icmp->type); 

    //ICMPv6 code 
    memcpy(ptr, &icmp->code, sizeof(icmp->code)); 
    ptr += sizeof(icmp->code); 
    chksumlen += sizeof(icmp->code); 

    //ICMPv6 payload 
    memcpy(ptr, &icmp->dataun.un_data32, sizeof(icmp->dataun.un_data32)); 
    ptr += sizeof(icmp->dataun.un_data32); 
    chksumlen += sizeof(icmp->dataun); 

    return pseudoheaderchksum(buf, ptr, ip6, chksumlen); 
} 

n_uint16_t pseudoheaderchksum(unsigned char *buf, unsigned char *ptr, struct ipv6hdr *ip6, int chksumlen) { 
    //source address 
    memcpy(ptr, &ip6->saddr, sizeof(ip6->saddr)); 
    ptr += sizeof(ip6->saddr); 
    chksumlen += sizeof(ip6->saddr); 

    //dest address 
    memcpy(ptr, &ip6->daddr, sizeof(ip6->daddr)); 
    ptr += sizeof(ip6->daddr); 
    chksumlen += sizeof(ip6->daddr); 

    //upper layer length 
    n_uint32_t upprlen = 0; 
    upprlen += sizeof(ip6->payload_len); 
    memcpy(ptr, &upprlen, sizeof(upprlen)); 
    ptr += 4; 
    chksumlen += 4; 

    //3 bytes of zeros, then next header byte 
    *ptr = 0; 
    ptr++; 
    *ptr = 0; 
    ptr++; 
    *ptr = 0; 
    ptr++; 
    chksumlen += 3; 
    memcpy(ptr, &ip6->nexthdr, sizeof(ip6->nexthdr)); 
    ptr += sizeof(ip6->nexthdr); 
    chksumlen += sizeof(ip6->nexthdr); 
    return chksum((uint16_t *) buf, chksumlen); 
} 

//counting internet checksum 
n_uint16_t chksum(uint16_t *buf, int len) { 
    int count = len; 
    n_uint32_t sum = 0; 
    n_uint16_t res = 0; 
    while (count > 1) { 
     sum += (*(buf)); 
     buf++; 
     count -= 2; 
    } 
    //if number of bytes was odd 
    if (count > 0) { 
     sum += *(unsigned char *) buf; 
    } 
    while (sum >> 16) { 
     sum = sum + (sum >> 16); 
    } 
    res = (n_uint16_t) sum; 
    return ~res; 
} 

残念ながら、既存のパケットでテストすると、キャプチャされたICMPv6のチェックサムは私の計算結果とは異なります。何が間違っていますか?

PS。私は、生のイーサネットパケットをキャプチャまたは送信するためにlibpcapを使用しています。


EDIT:関数は

icmpv6_chksum何をすべきかについてのより詳細な説明は、 - のICMPv6パケットプラス、それがカプセル化されていますしたIPv6パケットのヘッダの構造を取得します。 ICMPv6タイプ、ICMPv6コード、ICMPv6メッセージ(基本的にICMPv6パケット全体、チェックサムフィールド以外は計算中はゼロになります)の値をコピーします。
バッファ、最初の空の位置ポインタとIPv6ヘッダーをpseudoheaderchecksumに渡します。

pseudoheaderchecksum - バッファ、最初の空の位置ポインタ、およびIPv6ヘッダーを取得します。 IPv6送信元アドレス、IPv6宛先アドレス、データ長(この場合はICMPv6パケット長)をコピーし、次に3バイトのゼロバイトとIPv6の次のヘッダー値(= ICMPv6ヘッダー)をコピーします。
この機能は、いわゆる「IPv6擬似ヘッダ」と呼ばれるバッファに追加されます。 RFC 2460.
今すぐ満たされたバッファは、chksum関数に渡されます。この関数は、バッファのインターネットチェックサムをカウントすることになっています。

chksum - バッファーとその長さをバイト数で取得します。バッファの16ビットフラグメントを合計します(必要に応じて最終的に奇数バイトを追加します)。オーバーフローは16ビットの合計に加算されます。最後に、関数は計算結果の1の補数(2進反転)を返します。
この機能では、RFC 1071で説明されているように、インターネットチェックサムを計算しようとしています。模範的な実装を見ると、正しいとは限りませんが、アルゴリズムの記述はしばしばあいまいです。


編集2
さて、私は私が今

unsigned char *dataが来たすべてです
n_uint16_t icmpv6_chksum(struct ipv6hdr *ip6, struct icmp6hdr *icmp, unsigned char* data, int len) { 
    unsigned char buf[65535]; 
    unsigned char *ptr = &(buf[0]); 
    int chksumlen = 0; 

    //ICMPv6 type 
    memcpy(ptr, &icmp->type, sizeof(icmp->type)); 
    ptr += sizeof(icmp->type); 
    chksumlen += sizeof(icmp->type); 

    //ICMPv6 code 
    memcpy(ptr, &icmp->code, sizeof(icmp->code)); 
    ptr += sizeof(icmp->code); 
    chksumlen += sizeof(icmp->code); 

    //ICMPv6 rest of header 
    memcpy(ptr, &icmp->dataun.un_data32, sizeof(icmp->dataun.un_data32)); 
    ptr += sizeof(icmp->dataun.un_data32); 
    chksumlen += sizeof(icmp->dataun); 

    unsigned char *tmp = data; 
    for(int i=0; i<len; i++){ 
     memcpy(ptr, &tmp, sizeof(unsigned char)); 
     ptr += sizeof(unsigned char); 
     tmp += sizeof(unsigned char); 
     chksumlen += sizeof(unsigned char); 
    } 
    return pseudoheader_chksum(buf, ptr, ip6, chksumlen); 
} 

、ICMPv6メッセージ本文(ヘッダを以下の通りの内容)が含まれていなかったことに気づきましたICMPv6ヘッダーの後(イーサネットFCSを除く)。
残念ながら、チェックサムカウントはまだ正しく動作していません。

+0

このチェックサムアルゴリズムの正確で完全な説明を追加してください!私は 'htonl()'とか友人の呼び出しを見ないのですが、なぜですか? –

+0

これらの構造体には、「パックされた」プラグマが必要なのでしょうか? –

+0

チェックサムにIPv6パケットヘッダーを含めないでください。疑似ヘッダをICMPデータグラムの前に追加し、疑似ヘッダとデータグラム上のチェックサムを計算します。 –

答えて

0

数字がホスト順になっている必要があるときと、ネットワーク順になっている必要があるときを把握していないと思われます。 n_uint16_tuint16_tの間で前後にキャストするだけでは何もしません。 htons() and ntohs()などの関連機能を使用する必要があります。

関連する問題