プロトコル


Table of Contents

1. プロトコル
通信ってなに?
通信は命令
伝言ゲーム
階層化
パケットしよう
実際のプロトコル
プロトコルとプログラミング
盗聴
なんで盗聴するの?
盗聴の道具立て
実際の盗聴

Chapter 1. プロトコル

通信ってなに?

通信は命令

通信は計算機の基本です。至る所に通信。別にネットワークだけが通信ではないです。計算機の中のバスだって通信だらけ。ハードディスクとマザーボードだって通信。キーボードやマウスだって通信しています。もちろんUSBやIEEE1994も。それどころか、CPU の中身も、演算ユニット、実行ユニット、命令デコーダの間で通信。CPUと外部もメモリコントローラ、メモリコントローラとメモリ、メモリコントローラとIOコントローラの間も通信です。

では通信ってなんでしょう?難しい質問です。まぁ、必要以上に哲学的に考えるの止めておいて、現象だけを見れば、信号(ビット列)が、電線とか光ケーブルとか、単なる空間とかを通して、ある装置から別の装置に送られる事です。ただ、送るだけで良いなら物理現象は自然に伝播します。しかし、物理現象が起こっただけでは実際にとして意味のある現象は起こりません。送り側と受け側で、そのビット列に対する意図を理解しなければなりません。これは、人間の会話と一緒です。

人間の音声による会話では話し手は発生する空気の振動を正確に制御しなければなりませんし、聞き手はその空気の振動を発音として解釈できなければなりません。文字による情報交換では、文字の形や並びなどにルールがあります。音声や文章を解釈をするには、言葉という形式がきまっていなければなりません。 しかし、形式が決まっているだけでは意味がありません。言葉には意味、あるいは、意図がなければなりません。意図があるから、言葉にして相手に伝えようとするのですから。

私は言語学の専門家ではないので、勝手な想像ですが、原始的な言葉(の意味、あるいは意図)というのはみな命令だったはずです。自分の考えを客観化して相手に理解させる、というのは相当高度な精神活動です。言葉がそういう高度な精神活動を目的として生まれたというのは考えにくいです。むしろ、原始的な知性体であった原始の人間が、社会的集団を維持していく上で、人間の間でコミュニケーションをとる必要性が生じて、言葉を使い出したと考えられます。高度な精神活動は、人間どうしのコミュニケーションがとれるようになってから、コミュニケーションのための言葉を流用することでできるようになったと考えられます。さて、原始的な社会における人間どおしのコミュニケーションとはなんでしょうか。それはおそらく命令だったはずです。おい、おまえ、そのバナナをくれ。

電子計算機という知性体はまだ原始的です。だって、自分が何を考えているかということを言語で表記し、他の計算機や人間に理解させるる事ができません(実は、彼らは既に相互に会話をしているのだけれど、人間が知らないだけでもしれません...雪風の読みすぎ)。そんな、原始的な知性体である計算機が使う言葉と言えば命令です。つまり、計算機の通信は全て命令なのです。それに、計算機に言葉を教えているのは人間だし、人間は計算機に命令をしたいのですから。

さて、命令の形式といえば、おいお前、何を、何しろ、です。これだけの要素が必要です。

伝言ゲーム

電線(あるいは光ケーブル)の中をビット列が伝播する速度はすごい速いです。しかし、一本のケーブルをそんなに長く引くことはできません。信号が減衰劣化してしまうからです。となると、遠くまで届けるには中継が必要です。単純に信号の物理的な強度を増強するという中継もできます。でも、あまり効率がよくありません。一旦劣化してしまった信号は、物理的な強度を増強して中継したとしても、オリジナルほど遠くにはとどきません。劣化がどんどん蓄積しているからです。だから、中継器は受信した信号から、オリジナルの信号を再生して、再度送出できなければなりません。アナログ信号に比べると、デジタル信号は信号の再現がしやすいですが。

ところで、単純な中継では、別の問題を解決できません(それどころか、問題をさらに悪くしていまいます)。沢山の所を相互に繋ぐ通信網を構築することを考えます。N箇所のポイントを相互に接続するのに必要なケーブルの本数は? N*(N-1)/2 本です。これで、それぞれのケーブルに中継器をいれたら大変です。

大昔の電話では、交換手というオネーさん(セクハラ発言か!?)がいてくれました。電話をするというのは、まずは、交換局につないで(受話器を上げてフックをカチカチ)、オネーさんに、何処(誰)に繋いでください、とお願いをしました。オネーさんは交換機を操作してあなたの所から交換局までの線と相手の所から交換局までの線を物理的に繋いでくれました。本当にプラグと差し替えていたそうです。これで直接相手と繋がって話ができるようになりました。今の電話はこれを自動化しているだけです。オネーさんと話しする代わりにダイアルを操作しているだけです。自動交換機はダイアルの信号を解釈して、線を繋いでいるのです。交換機をつかえば、ケーブルはN本で済みます。

相手と直接物理的に接続出来ない場合、つまり、N*(N-1)/2 本のケーブルを張るのが不可能な場合は、どうしても何らかの交換機がいります。交換機には命令(オネーさんにはお願い、おいお前なんて言ったら繋いでくれません)しないと、通信ができないのです。つまり、遠くの相手に命令を伝達するには、伝達する為の命令を添えなければならないのです。これは、どちらかと言えば郵便の事を考えると分りやすいです。ちょうど封書で命令を伝達するのと同じです。封書には宛先が書いてあります。封書をポストに入れることは、封書をこの宛先に送れ、とお願いしているわけです。

もし、世界の中心に一局の交換局しかなかったら(世界に一つしか郵便局がなかった)大変です。ケーブルは中継機が必要なくらい長くなってしまいます(郵便配達のオジサンはフレンダー号に乗らなければあなたのお宅から世界の中心の郵便局まで行けない)。それに、世界中の電話が一つの交換局に集中するわけです。ものすごい量の通信を捌かなければなりません。更には、世界の端に住むお隣さんどうしが連絡する時、ものすごい不経済なことになってしまいます。一旦世界の中心まで行って帰ってこなければならないのです。馬鹿みたいです。

この問題を解決するには、適当な地域毎に交換局を設けて、その地域内の通信はそこの局で捌き、他の地域の相手に繋ぐ場合は、相手がいる地域の交換局に繋いで、そこから実際の相手に繋いでもらうようにした方が良いです。

しかし、交換局どうしを直接繋いだらまた、N*(N-1)/2 本になってしまいます。そこで、隣接する交換局を繋ぐことにします。例えば隣接する3局はちょうど三角形をつくるようにつなぎます。これなら、一本一本のケーブルの長さは最低限で良いですし、事故があってケーブルが一本切れても、反対周りの経路が残っていますので、通信は途切れないです。

実際の電話は、交換局どうしを繋ぐ交換局を使っています。地域交換局、地方交換局、中央交換局という階層構造を持っています。電話は災害に弱いのです。通信が集中したり、線が切れたりすると、下手すれば全滅、運が良くても一つの地方が通話不能になります。

インターネットは前述のように隣接した交換局どうしを繋ぐ方式をとっています。これは、初期のインターネットの開発に援助をした米国防総省は、米国本土が核攻撃をうけて電線がずたずたに切られても全滅しないような通信網が欲しかったからです。

階層化

もし、インターネットに使われる通信制御命令が1セット(1系統)しか無かったらどうなるでしょう。全てのインターネットに接続する装置は、全ての命令を理解できなければなりません。しかし、それは大変なことになります。

一番厳しい例として、1GB のファイルを転送しようとしたとします。もし、転送命令に分割して送るという命令が無かったらどうなるでしょう。全ての交換局は 1GB ものデータを一旦受け取って、それから、送り出さなければなりません。その間、少くともケーブル1本は占有されてしまいます。

それでは酷いので、分割して送ることができるように命令を拡張します。これは、あるファイルMバイトの長さで分割したI番目だよ、という感じで。それで大きさの問題はとりあえず解決はできます。しかし、新たな問題が起こります。大きいデータはファイルばかりではありません。メールのメッセージかもしれません。あるいはプログラム間での特殊な通信かもしれません。となると、ファイルを分割して送るという命令、メールを分割して送るという命令、プログラムのデータを分割して送るという命令。命令体系がどんどん膨らんできてしまいます。

そこで、命令体系を機能毎にモジュール化し、分割するという機能と、色々なものを送る、という機能を分けます。命令一覧は1次元から2次元になります。これで一見解決ですが、もっと酷いことになります。命令体系を拡張する度にインターネット中の全ての装置を更新しなければなりません。そんなの不可能です。

これを解決するには、命令体系を階層化という考え方を導入するしかありません。

例えるならば、封筒を幾重にいれて送るという感じ。微妙に違うのは、封筒の中身を勝手に分割して送る場合もあるということ。まぁ、物理的な紙ではなくデジタルデータなので、単純な切断、結合は何の問題もないですが。

命令体系の階層化の必要性は他にも有ります。実際の通信網の中には、光ケーブル、ツイストペア電線、電話線、無線、レーザー、音波、紙(そのうち、亜空間通信)など様々な媒体が使われるでしょう。媒体ごとにその特性は全くことなります。全ての媒体に共通につかえる(言い替えると、全ての媒体に共通の性質に基づいて)命令体系を決めるなんて事はできないか、むりにやったら、相当効率の悪いものになります。

だから、媒体に依存する性質に対応した命令と、媒体に依存しない命令を分けて階層化する必要がでてきます。

パケットしよう

デジタル通信と言ったって、実際にはアナログ通信なのです。1か0と言っているのは、そう解釈しているだけであって、実際には何ボルトなのです。それを、適当なタイミング(クロック)で、適当な値(閾値)で区別して、1か0と言っているだけです。だから、ノイズが乗ったりして波形が崩れてしまうと、それを1/0に変換するときに誤った変換をしてしまいます。

伝言ゲームの節で、交換局網の話しをしました。接続が網の目になっていれば、ケーブルが切れても大丈夫だと。とは言っても、例えば、目的地に向かっているまさにその時に線が切れたら?そういう運の悪い奴だっているでしょう。運が悪いで済ませていいでしょうか?例えばそれが、核ミサイル反撃中止の命令だったら。プロトコルの設計が悪かった為に人類が滅んでしまうかもしれません。

例えば、1GBのファイルを送って、受信が完了してから壊れている事が分ったらどうします?最低ですよね。だから、エラーが見付かったらすぐに再送した方がいいです。でも、エラーが見付かったからといって、その度に最初から再送していたらいつまで経っても転送が完了しません。

大きなデータはパケットに分割して送ります。分割する事のメリットは、分割された単位で、データが壊れているかどうかがチェックできますから、また、パケットに順序の番号を付けておけば、抜けを見付けることもできます。壊れたパケットや、抜けたパケットだけを再送すれば済みます。

プロトコルの階層化は実際には、パケットへの分割という形式をとります。

パケットって小包の事です。宅急便を想像してみてください。送りたいもの(ペイロードと言う)に、伝票を貼ってそれに送り先や送り主、簡単な内容の説明や、注意事項を書きます。インターネットのパケットもみなそんな形をしています。伝票に相当するヘッダというデータをペイロードのデータの前に付けます(前に送ります)。

以上の話が通信プロトコルやインターネットの基礎となる考え方です。

実際のプロトコル

実際にLANやインターネットで使われているプロトコルとしては、TCP/IP/イーサネット、という階層です。TCP より上の層は、HTTPとかFTPとかCIFS(Windows ファイル共有)など、アプリケーションプログラム毎のものです。TCP は OS が理解します。IP は OSとルータというネットワークを接続する為の装置が理解します。イーサネットは OS、ルータ、さらにはスイッチングハブが理解します。リピータハブ(通称、DQNハブ)はイーサネットすら理解しません。単に電気的に流しているだけです。

全てのプロトコルのヘッダには送り元と送り先のアドレスが記録されています。イーサネットでは、イーサネットポートに割り当てられたユニーク(世界的にユニークです)な MAC アドレス。IP には、ご存知 IP アドレス。TCP にはポート番号。

TCP 未満のプロトコルのヘッダには、ペイロードの種類を示す番号が記録されています。TCP/IP/イーサネットで通信をしている時、イーサネットには IP を乗せている事を示す番号が、IP には TCP を乗せている事を示す番号が記録されています。一方、TCP には、TCP にはそれが運んでいるペイロードを示す番号を記録する所がありません。その代用としてポート番号が使われています。

TCP未満のプロトコルのヘッダには、ペイロードの大きさ(長さ)が記録されています。イーサネットにはそれが運んでいるIPの長さ、IPにはそれが運んでいるTCPの長さが、記録されています。一方、TCP にはそういう情報はありません。そのかわり、今まで何バイト送ったか(正確にはちょっと違う)が記録され てます。

プロトコルとプログラミング

TCP/IP/イーサネットのプロトコルについてのハンドリングは OS がやってくれます。なので、よほど特殊な目的(盗聴とか)がないかぎりは、プログラミングをするというのは、TCP の機能を呼び出す事になります。

TCP の機能を呼び出すための API としてはソケットが有名です。というか、事実上これしかありません。

さて、ソケットを利用して通信のプログラムを書くということは、何らかのプロトコルを開発しようとしていると言うことです。言い替えるとHTTPとかRPCと同じレベルのものを作ろうとしていると言うことです。そんなのレベル(3,*)以下の人にできるわけありません。結論「やめろ!」です。少なくとも、実用的なシステムを開発する時、絶対に新しいプロトコルを開発してはいけません。必ず、既存のプロトコルを使い、そのプロトコルを実装した既存のライブラリを使いましょう。

トレーニングとして、ソケットを使った通信プログラムを書きたいというのなら止めませんが、その前にまずはHTTPとかRPCを使いこなして、レベル(2,2)を卒業してからにしてください。

盗聴

盗聴の仕方を伝授しましょう。盗聴の技術を悪用してはいけませんが、この技術はシステムを開発する過程で非常に役に立ちます。善用を目的としているのだから、この技術を教えてもどっかの大学の助手のように手が後ろに回ることはないですよね?

なんで盗聴するの?

悪用が目的では無いことを予め表明しておかないと、後で何を言われるかわらないので、最初に言っておきます。

まず、一番多いケースは、デバッグです。

実用レベルのソフトウェアをデバッグするとき、ソースコードデバッガなんか使えません。そんなものに頼ってると笑われるよ!。

通常はデバッグのためのログを吐くことになります。デバッグログと言います。特にイベント駆動型となるGUIや通信プログラムでは、デバッガに入るとプログラムの実行が中断してしまうので、イベントのタイミングが変わってしまいます。だって、プログラムは止まっても、他のプログラムやOS、そしてユーザは動いているのですから。マウスダウンでデバッガに入ってしまったら、その後のマウスダウンは拾えません。そのマウスアップは何処かに消えているか、他のプログラムは読みだしてしまっています。不確定性原理みたいなもの?観測することにより披観測物は影響をうけてしまう。デバッグログなら影響を最小限にすることができます。

通信プログラムではもっと状況が悪くなります。なぜなら、サーバを開発しているチームとクライアントを開発しているチームが違う(普通は会社も違う)状況でバグがでると、お互いにバグのなすりあいになります。そんなとき、デバッグログは証拠になりません。双方とも正しい動作を示唆しているデバッグログを吐き出しているなら、なんでバグがでているのでしょう?もう、デバッグログは根拠になりません。間違った動きをしているプログラムが吐き出したデバッグログは間違っている可能性があるのですから。

そうなると、実際にネットワークを流れているビット列を分析するしかありません。だから盗聴するのです。

他のケースとしては、通信プログラムのベンチマークを正確に取りたい時です。通信プログラムにログを書かせたら、その分動作が遅くなってしまうからです。

楽しくないケースもあります。通信プロトコルの仕様が文書化されておらず、かつ、開発したチームは既に解散しているようなケースです。かなり厳しい状況ですが、やるしかありません。実際、私自身、某病院に入っている某超大手コンピュータメーカの情報システムを更新するときに、この状況になりました。ほんま辛かった。だから、素人がヘタクソなプロトコルを設計すんなって。

盗聴の道具立て

いわゆる、パケットキャプチャとかスニッファーと呼ばれるプログラムを入手しましょう。LinuxやBSDなどのオープンソースOSには標準的に入っています。tcpdump が一番メジャーです。tcpdump は Windows にも移植されているようです。説明は tcpdump で行います。

盗聴するネットワークに tcpdump を走らせるマシンを繋ぎます。繋ぎ方としては、ルータとしてはさむ、ブリッジとしてはさむ、TCP リレーを走らせる、単に同じネットワークに繋ぐ、ARP Poisoning をする、です。もちろん、サーバあるいはクライアントのマシンの上で tcpdump を走らせても良いです。

一番簡単なのが単に同じネットワークに繋ぐですが、今時のスイッチングハブによるネットワークでは盗聴できません。スイッチングハブはパケットを漏洩してくれないからです。そういう場合は ARP Poisoning を使えばスイッチングハブや盗聴されているマシンの OS をだましてパケットを漏洩させる事ができます(まだ、ARP Poisoning 耐性のあるハブやOSって無いはず)。ARPPoisoning にはツールがいります。ettercap が有名です。これは最強の盗聴の技術です。とてもこれ以上は教えられません。

ルータやブリッジにするには盗聴用PCにNICを2枚刺す必要がありますが、盗聴による通信への干渉が一番小さい、一番素直なやりかたです。もちろん、tcpdump を走らせるマシンをルータやブリッジとして設定(普通はカーネルの再構築)しなければなりません。

現実的には、TCPリレーを走らせるのが、一番簡単です。これなら盗聴用PCのNICは1枚で済みます。ただし、微妙なレベルで通信に影響がでる可能性があります。TCPのパケットの大きさとか送り出されるタイミングとか。それに、一番の影響は、IP アドレスを変えなければならないという事です。クライアントから見がサーバのIPアドレスは、本当のサーバのアドレスではなく盗聴マシンのアドレスに設定しなければなりません。ポート番号も場合によっては変更しなければなりません。本稼働中のシステムではこういう変更は出来ないかもしれません。

実際の盗聴

授業で実演します...