今回は、ちょっとした計算をしようかと思います。
ということで、IPチェックサムについて取り上げてみようかと思います。
TCP/IPについて学んでいる時にチェックサムという単語をみかけていて、 その時は読み飛ばしていましたが、気になって仕方がないので記事にしてみました。
ず~~~っと気になっていると、なんだか落ち着かないときってありますよねw
では、さっそくみていきます。
チェックサムとは?
16bitで構成されていて、IPヘッダが壊れていないかをチェックするために存在します。
簡単にいうと、IPプロトコルで通信されている時に、エラーのチェックをする方法といったところでしょう。
ここまでならわかります。
ただ、調べてみると以下のような言葉で説明されているのをよくみます。
チェックサムというのはエラー・チェックの方法の1つで、チェック範囲の値をすべて足し算し、その結果の値と足し算するとすべてのビットが1になるような値を設定します(これを1の補数といいます)。受信した側は、同じ演算を行ってみて、結果が正しくなければエラーが発生したと判断するわけです
引用: パケットを正しく届けるために用意されたIPの機能(第7回) | 日経 xTECH(クロステック)
わたしはここで「どういうこと?」ってなりました。
だって、TCP/IP勉強している最中に言われても、完全に理解しているわけじゃないからよく頭に入ってこなinn...ww
けっこう読み飛ばしてしまう原因なのかと思っておりますw
このチェックサムについて、どうやらなにかを計算するということはわかりました。
「なにを計算するの? どうやって計算するの?」
これを引き続きみていきます。
なにを計算するの?
まず、チェックサムといっても、IPヘッダにもTCPヘッダにも含まれているものです。
IP、TCP、UDPにおいて計算方法は同じなので、ここではまとめてチェックサムといいます。
チェックサムの対象は、以下のようになっています。
IPがIPヘッダ部分だけを計算対象とするのは、ルータでIPパケットの分割が行なわれると、 すべてのデータが揃わないので、チェックサムの計算ができなくなるためです。
どうやって計算する?
ここからが頭をよく使って理解していくところです。
さきほども書いたように、どれも計算方法は同じです。
先に具体的な計算方法の解説を書こうと思ったのですが、 先に例として以下の記事(見出し: tcpdumpの出力)でも使用したパケットで計算してみようかと思います。
長くなるので、読み飛ばしたい方は、補数とは?を読んで計算方法だけ読んでください。
実際に計算してみる
ここに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進数についてわからん!という方は、以下の過去記事を参考にしてみてください。
補数とは、
ある基数法において、ある自然数 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が発行している、技術仕様が日本語訳で載っています。