helloworlds

not a noun, it's a verb

【TCP/IP】チェックサムを計算していく

今回は、ちょっとした計算をしようかと思います。

ということで、IPチェックサムについて取り上げてみようかと思います。

TCP/IPについて学んでいる時にチェックサムという単語をみかけていて、 その時は読み飛ばしていましたが、気になって仕方がないので記事にしてみました。

ず~~~っと気になっていると、なんだか落ち着かないときってありますよねw

では、さっそくみていきます。

チェックサムとは?

16bitで構成されていて、IPヘッダが壊れていないかをチェックするために存在します。

簡単にいうと、IPプロトコルで通信されている時に、エラーのチェックをする方法といったところでしょう。

ここまでならわかります。

ただ、調べてみると以下のような言葉で説明されているのをよくみます。

チェックサムというのはエラー・チェックの方法の1つで、チェック範囲の値をすべて足し算し、その結果の値と足し算するとすべてのビットが1になるような値を設定します(これを1の補数といいます)。受信した側は、同じ演算を行ってみて、結果が正しくなければエラーが発生したと判断するわけです

引用: パケットを正しく届けるために用意されたIPの機能(第7回) | 日経 xTECH(クロステック)

わたしはここで「どういうこと?」ってなりました。

だって、TCP/IP勉強している最中に言われても、完全に理解しているわけじゃないからよく頭に入ってこなinn...ww

けっこう読み飛ばしてしまう原因なのかと思っておりますw

このチェックサムについて、どうやらなにかを計算するということはわかりました。

「なにを計算するの? どうやって計算するの?」

これを引き続きみていきます。

なにを計算するの?

まず、チェックサムといっても、IPヘッダにもTCPヘッダにも含まれているものです。

IP、TCPUDPにおいて計算方法は同じなので、ここではまとめてチェックサムといいます。

チェックサムの対象は、以下のようになっています。

  • IP -> IPヘッダ部
  • TCP -> データ全体
  • UDP -> データ全体(計算しなくてもよいが、計算することを推奨している)

IPがIPヘッダ部分だけを計算対象とするのは、ルータでIPパケットの分割が行なわれると、 すべてのデータが揃わないので、チェックサムの計算ができなくなるためです。

どうやって計算する?

ここからが頭をよく使って理解していくところです。

さきほども書いたように、どれも計算方法は同じです。

先に具体的な計算方法の解説を書こうと思ったのですが、 先に例として以下の記事(見出し: tcpdumpの出力)でも使用したパケットで計算してみようかと思います。

o21o21.hatenablog.jp

長くなるので、読み飛ばしたい方は、補数とは?を読んで計算方法だけ読んでください。

実際に計算してみる

ここにtcpdumpで出力したパケットがあります。

4500 0034 5125 4000 ff06 0821 0a00 0abb
0a00 03c3 1f36 b702 2e7d 893b 223b 4fd8
8011 00e6 22a4 0000 0101 080a 6aa2 79cb
4b56 71d1

見る限りIPv4のパケットですね。

このパケットの出力から見てみると、IPヘッダとTCPヘッダに含まれる情報が出力されています。

今回は、IPヘッダの計算をしてみようかと思うので、そこだけ抜き出します。

4500 0034 5125 4000 ff06 0821 0a00 0abb
0a00 03c3

チェックサムのフィールドは、0821にあたります。

なので、0821は除外して計算します。

便利な計算サイト(16進数計算機)があるので、一気に足します。

4500 + 0034 + 5125 + 4000 + ff06 + 0a00 + 0abb + 0a00 + 03c3
= 1f7dd

1f7ddと答えが出ました。

これを2進数に変換します。

これも変換サイトがあるので活用します。

2進数に変換すると、11111011111011101になります。これを4bitずつに分けて、16bitの区切りにします。

1 1111 0111 1101 1101

余った今回でいうと1を残りの16bitで区切った2進数と足します。 (計算サイト)

1 + 1111 0111 1101 1101 = 1111011111011110

答えは、1111011111011110になりました。

こちらを4bitずつに区切ります。そして、bitを反転させます。

1111 0111 1101 1110
↓
0000 1000 0010 0001

反転させた2進数(0000 1000 0010 0001)を、16進数に変換します。

すると、821と変換できました。

この821は、計算するときに除外したチェックサムのパケット0821と同等だとわかります。

今回は2進数にしたとき、最初の4bitが0000なので、16進数にしたとき0が抜けていますが、まあ同等です。

と、このようにしてチェックサムを計算できました。

さて、このように計算してきた仕組みをこれから順をおってみていきたいと思います。

補数とは

この記事を書くにあたって、以下の記事を参考させてもらいました。本当にわかりやすく理解しやすかったです。

補数表現とは?1の補数と2の補数の違いと計算方法まとめ | サービス | プロエンジニア

また、これから進数が出てくるの、2進数や16進数についてわからん!という方は、以下の過去記事を参考にしてみてください。

o21o21.hatenablog.jp

補数とは、

ある基数法において、ある自然数 a に足したとき桁が1つ上がる(桁が1つ増える)数のうち最も小さい数をいう。

引用: 補数 - Wikipedia

また、こうして説明されている記事もあったりします。

マイナスがついた数字を、マイナスを使わずに表現する数のこと。

いきなり補数という言葉が出てきました。

実際、数字を使っていった方がわかりやすいと思うのでまず手始めにこんな計算から。

例えば、10の補数で考えてみます。

10の補数で、6と言われたら、4補数になります。

簡単ですね!なんか、お店でお会計するときに1980円だとして、小銭を使おうと2000円と80円出すとお釣りは100円になる、なんかこれに似ていますね。

このレジの例でいくと、10進数で考えて計算しています。

もう少し、砕いていきます。

(※ ここから^を累乗として表します。)

元の桁が1980で4桁です。これは、10^4で、10000です。

10進数の10の補数ということにすると、1980の補数は、8020になるよってかんじです。

# 10進数の10の補数で表現
1980の補数は、8020 : (10^4 = 10000)

このことを、補数といいます。

元の数(1980)と、補数(8020)を足した場合に桁が上がりが発生する数のうち最小の数です。」

そして、補数には、減基数という考え方もあります。

これは、

元の数(1980)と、補数(8020)を足した場合に桁が上がりが発生しない数のうち最大の数が補数になる。」という考え方です。

つまり、1980が元の数ということであれば、8019が補数ということです。

引き算が使えない?

上で、「マイナスを使わずに表現できるのが補数」と書きました。 これはいったいどういうことなのでしょうか。

当たり前といっちゃあ、当たり前なのですが、コンピュータのなかでは0 or 1ですべてが表現されています。

ということは、マイナスなんて使用することはできないのです。

なんか、コンピュータだから引き算くらい当たり前にできるよね!ってかんじのイメージが強いですが、 そんなことはありません。電卓アプリを使っていても、結局内部では0/1で表現されていることになりますからね!

ということで、登場するのがやはり補数ということになります。

補数を使うことはわかったのですが、マイナスを表現するには、どうしたらいいのでしょうか。

また、簡単な計算でイメージを掴みます。

こんな計算があったとします。

1280 - 131 = 1149

そして、以下のように考えます。

# まず、最大桁数1280があって、4桁であると認識します
10 ^ 4 = 10,000

# 131の補数
10000 - 131 = 9869

# 元の1280と、131の補数を足します
1280 + 9869 = 11149

答えは、11149とでました。

そして、先頭の1を取り除きます。すると、1149になりました。 見事、1280 - 131の答えが求まりました。これは常に成り立つ式になります。

ここまでくると、最初にとりあえずやってみたチェックサム計算の仕組みがなんとなーく結びついてきたのではないしょうか!

そして、今やってきたようなことをまとめて、10進数の「10の補数」と「9の補数」という概念でした。(「n進数」の補数)

一般的なのは、2進数の「1の補数」と「2の補数」という概念です。

これからそれををみていきたいと思います。

2進数の「1の補数」と「2の補数」

まず、1の補数からみていきましょう。

1の補数

2進数の1の補数は、減基数をつかった補数です。

なので、n ^ m - 1ということになります。(n進数、元の数の桁数m桁)

さっそく計算していきます。

今回はこの2進数、1001001 をつかって計算していきます。

# 公式にあてはめてみる
2^7 - 1 = 127

# 127を2進数に変換
1111111

# 補数を求める
1111111 - 1001001 = 0110110

求められた2進数は、0110110です。

これを反転(否定という言い方もする?)します。

0110110
↓
1001001

1001001は、元の数ですね!

なんと、実は元の数を反転するだけで、答えを求めることができます。

2の補数

2進数の1の補数の場合は、基数をつかった補数です。 なので、2進数の元の数と補数を足すと、桁上りしまよーということですね。

なので、-1はしないので、n ^ mということになりますね。(n進数、元の数の桁数m桁)

あとは、1の補数のときと同じです。パッとやっていきます。

さっきと同様に2進数、1001001 をつかいますー。

2^7 = 128
10000000 - 1001001 = 0110111

求められた数字は、0110111です。

あれ、これは1の補数のときに求めた数に対して、1を足しただけ?...

1の補数: 0110110

2の補数: 0110111

うん、そうみたいですw

つまり、こちらも簡単に求めるには、元の数を0/1を入れ替える(反転させる)、そして、1を足す

ただそれだけでした。でも、一応計算式を知っていた方が今後応用は効くかと思います。

まとめ

これで大体一番最初にやった計算の意味がつかめたでしょうか。

① 最初にパケットから、IPヘッダの内容を抜き出す

これは、IPヘッダのチェックサムを計算するからですね。

チェックサム以外の16進数を16bitずつ足していく

2進数でも計算できないことはないですが、パケットが16進数なので16進数に頭を変換させます。

③ 2で計算した16進数の答えを2進数に変換します。

④ 16bitを超える(桁上がった)bitと、16bitで区切れた値を足す

16bitずつ足していったので、桁を超える部分は16bitで区切る

⑤ 1の補数にする

4で求めた2進数を反転させます。

⑥ 反転させた2進数を16進数に変換します

もとは、パケット(16進数)だったので、これで最初除外していた値と照らし合わせることができます。

以上.

参考

もっと詳しくチェックサムについて知りたい方は以下のページにアクセスしてみてください。 チェックサムIETFが発行している、技術仕様が日本語訳で載っています。

www5d.biglobe.ne.jp