YANO's digital garage

Copyright ©YANO All rights reserved. https://www.bravotouring.com/~yano/

Last-modified: 2024-04-17 (水)


[一語一絵/IT系]

ICMP Checksum / 2009-06-12 (金)

ICMPのチェックサム計算でハマった記録。

そもそもは転送するパケットをこっそり加工して転送しようというトリッキーなソフトウェア作成で、"ICMP_ECHOREPLY"をシーケンス番号が「3の倍数」と「3が付く数字」の時は"ICMP_DEST_UNREACH"に擬装しようという趣旨。

pingコマンドそのものは期待通りに通らなくなって、tcpdumpで見てもちゃんと"Unreachable"と表示されているのでOK。と思ったが、pingなんで"Unreachable"て出ないんだっけ?と思いながら、"tcpdump -vv"したところ、"bad Checksum"という怪しげな出力に気が付いた。

ならば試しに…と"ICMP_DEST_UNREACH"以前に"ICMP_ECHO"のチェックサムを再計算したところ元の値とは異なってた。当然相手側からの応答はなくなり、こりゃ「チェックサム計算処理」の嫌疑が濃厚だ。

計算ロジックは以前使った実績ある関数を持ってきたのだが、無責任にもうっかり弄っちゃった可能性もあると思い、[External]IP,ICMP,UDP checksum calculationと比較したところ問題なさげ。ICMPヘッダーサイズは8バイト固定なので、データ長の指定を"sizeof(struct icmphdr)"から'8'に書換えてみたけれど、案の定事態は微動だにせず。

いろいろ調べたところ[External]PC Online[External]TCP/IP再入門に到達。記事によると

 ICMPヘッダーにあるチェックサムは、ICMPパケット全体を対象にした計算値です。ICMPパケット全体を16ビット単位に分割したうえで、チェックサム部分にゼロを入れておき、全体のチェックサムを計算します。なお、このチェックサムでは、IPヘッダー部分は計算に含まれません。

 データ部分は、可変長で、メッセージごとに違う内容が入ります。ICMPパケット自体には、パケット全体の長さを指定するフィールドはありません。しかし、IPヘッダーが全体のパケット・サイズを指定しているため、この情報を使うことでデータ部分の長さを求めることができます。
との事。

なんと、チェックサム算出対象はICMPヘッダーではなくICMPパケット全体で、しかもIPヘッダーに存在する全体長からIPヘッダーの長さを差し引いてICMPパケット全体の長さを求めるのがスジだそうだ。

教えに従ってようやく無事"ICMP_ECHO"のチェックサムが通るようになった。が、肝心の"ICMP_DEST_UNREACH"[External]Stray Penguin[External]ICMPヘッダの「2.9.2. ICMP到達不能メッセージ (Destination Unreachable)」によると

 これに加えて、小さなデータ部を持つこともでき、その中身はインターネットヘッダ (IPヘッダ) のヘッダ全てと、元の IP データグラムのうちの 64 ビットを写したものとなる。次のレベルのプロトコルがポート定義などを持つのなら 64 ビットデータ部からそれが読み取れるはずだと解釈される。
との事。

data部に「ICMP_ECHOのIPヘッダ全てと、ICMPヘッダから64ビット」合わせて28バイトを搭載してIPヘッダのチェックサムを再設定する事により、pingでも"Unreachable"が表示されるようになり、万事OK。

【参照】
●thomas's home page http://www.fenix.ne.jp/~thomas/
IP,ICMP,UDP checksum calculation
●PC Online ビジネスパーソンのパソコン活用情報サイト http://pc.nikkeibp.co.jp/
IPにはないエラー通知の仕組みを提供する――ICMP・その1(第10回):TCP/IP再入門
●Stray Penguin - Linux Memo - http://www.asahi-net.or.jp/~AA4T-NNGK/
Iptables tutorial >> ICMPヘッダ