TCP/IP
まずはじめに
本章でのTCP/IPの取り上げるスタンスについて説明します。 本章では、UNIXとTCP/IPの関係性を取り上げ、GNU/Linuxの中でどう扱われているかの議論を試みます。TCP/IP自体の説明、また、インターネット自体の説明は本章の趣旨ではないので本章では扱いません。またTCP/IPに関する基本知識(例えば 3 way handshakeなど)は既に学習しているものとして話を進めます。
GNU/LinuxはTCP/IPだけではなく、Appletalk、DECNETなど多数のプロトコルをサポートしていますが、本章では主要な論点であるTCP/IPのみ取り上げます。
UNIXとTCP/IP
TCP/IPとは1970年代初頭にDRAPAの研究成果として既に出来上がったネットワーク通信用プロトコルで、元々はUNIXにはなかった機能です。米カリフォルニア大学バークレー校の研究グループ[1]がDARPAからの研究資金を得て、4.1bBSD(1982年8月)に対してBBN Technologiesで実装されていたTCP/IPコードをベースとして組入れるという作業が始まり4.2BSDで正式にTCP/IPを含んでリリース(1983年8月)となりました。ここでは「BBNの実装ベースで」とあっさり書いていますが、実際にはもうちょっと複雑な話になっています。 [2]
TCP/IPというとインターネット用プロトコルというイメージがありますが、当時のBSDの実装はイーサーネット接続されたLANやキャンパスネットワークで高速に情報を伝達するのに適したものになっています。一方でオリジナルのBBNの実装は広域なネットワークであるインターネットで使うことを前提としているもので、そのため80年代当時の低速なネットワーク向けに作られています。このようにターゲットとなるネットワークの前提が違うため、BBNの実装をそのままBSDに組み込んだような単純な話ではありません。
この期間でのエポックとしては1983年1月1日にARPANETの公式プロトコルはTCP/IPに切り替わっています。 [3] 当時TCP/IPの実装を持っていたオペレーティング・システムはそんなに多くはなく、BSDの他の名の知れたシステムはDEC社のTOPS-20 system [4] ぐらいです。当時、DARPAの資金で開発した、他に参考となる実装が多くなく、ソースコードが公開されているなどのいくつかの要因により実質BSDがコンピュータにおけるTCP/IPのリファレンス実装となっていました。
リファレンス実装であったというおもしろい傍証は1997-8年頃に問題となった Ping of Death のケースが良いかも知れません。これは規定外サイズのICMPパケットを脆弱性を持つシステムに送るとオペレーティングシステムごとハングアップしてしまうというものです。そもそもの原因はBSDのコードがパケットの分割を考えていなかった実装だったからです。分割されるケースを設計時点で考慮に入れなかったある意味、単純なバグで、そんなに深い意味はありません。もし、IPパケットとICMPパケットの仕様[5]だけで作っていたら設計時に考慮し、この脆弱性はたぶん現れないでしょう。しかし実際にはGNU/LinuxだけではなくMacintoshやWindowsにも同じ脆弱性が現れました。このことから仕様ドキュメントから設計をしたわけではなく、商用のオペレーティングシステムもオープンなコードであるBSDのTCP/IPコードをリファレンスとしていたのではないかといわれています。
Xerox PARCで開発されたイーサーネット、TCP/IP、そしてBSDの組み合わせによって構築されたLANがARPANET経由でエンド・ツー・エンドで接続し広域ネットワークを構築してしまうという当時は技術的にかなり画期的な仕組みであり、これこそ今日私たちがインターネットと呼んでいるものそのものです。ですからUNIXの存在はインターネットの大きな礎としての存在であったといっても決して過言ではありません。
Internet Protocol Suite
BSD由来のTCP/IPの特徴は、クライアントだけでなく、サーバだけでもなく、ルーターだけでもない、TCP/IPの機能一式をもったInternet Protocol Suiteの実装であるということです。つまり、UNIXはサーバとしても使え、クライアントとしても使え、そしてまたルーターとしても使えるということです。もちろんその上で動作するアプリケーションは、TCP/IPの機能をフルに使うことができます。
GNU/Linuxベースのサーバーやクライアントはインターネットにおける日常の風景的な部分ではありますが、形をかえて組み込み系でも市販のルータの中身がGNU/Linuxベースであったりするものよく見かけます。もちろんGNU/Linuxは、Internet Protocol Suiteで一式揃えているので、サーバとクライアントとルータやブリッジを同時に兼ねることも当然可能です。
典型的なのがGNU/Linux上で動作するバーチャルマシン・モニタXenのネットワーク構成です。Xenのドメイン0 (他の仮想マシンを動かすためのベースとなる環境)では、内部的にブリッジの機能を作り、ドメインU(個々の仮想マシン)からはそこに仮想的に接続する形を取りネットワークを形成します。このために特別なプロトコルスタックを用意するわけではありません。既にGNU/Linuxには必要なネットワークの能力が用意されているのです。
一般にTCP/IPのようなプロトコルが重ねられている構造のことをプロトコルスタック ( Protocol Stack ) と呼びます。
そして各々のプロトコルのメカニズムあるいは実装のことを、その層のスタック、たとえばTCPであればTCPスタック、IPであればIPスタックと呼びます。意味は、TCPの層だけではなく、その下層も含めたメカニズムあるいは実装をさしています。
GNU/Linux での TCP/IP
まずEthernetを前提に基本的な部分を説明したいと思います。 GNU/LinuxではEthernetのデバイスは基本的にeth0から順番に割り当てられます。 むかしはNIC (Network Interface Card)に対応するデバイスファイル /dev/eth0 というのがあったのですが、今ではそれが存在せず内部的にeth0、eth1という仮想的なネットワークデバイスになっています。 慣用的にloはローカルホスト、eth0はEternet、wlan0は無線LANの仮想ネットワークデバイスとして命名されていますが、これは任意な名前であり、たとえば、新たにPPP (シリアル経由でpoint-to-point接続するプロトコル)で接続を作る時はppp0や、 easytether (AndroidをUSBで接続しテザリングをするツールを利用)など仮想ネットワークデバイスは自由度の高い名前をつけることができます。 同種類のデバイスが増えるたびに番数が増えるのが普通[6]です ( ex: eth0, eth1, ... )。
- lo ローカルホストのための仮想ネットワークデバイス
- eth0 Ethernetのための仮想ネットワークデバイス
- wlan0 無線LANのための仮想ネットワークデバイス
その仮想ネットワークデバイスにIPアドレスを設定しTCP/IPで利用する形にします。
ifconfig
仮想ネットワークデバイスにTCP/IPに必要なパラメータを設定するのはifconfigコマンドです。 これは米カリフォルニア大学バークレー校でBSDに組み込んだTCP/IPの実装が作られた時に、IPアドレスやネットマスクなどのパラメータを設定する、あるいは参照するのに作られたコマンドですが、そのまま今もスタイルを変えず使われています。 グラフィカルインタフェース〜といっても項目をリストアップする程度のものですが〜を使って設定するツールは存在しますが、システムがブートする段階でIPの制御を行うのがifconfigです。
- 追記 (2015/9)
- RHEL7/CentOS7 移行ではifconfigを標準から外し、ipコマンド(iproute2)がデフォルトで入るようになりました。本セクションもアップデートを予定しています。
低いレベルでコントロールする方法を理解したい、あるいはツールを作成したいと思った時は netdevice のオンラインマニュアルにヒントがあります。
/sbin/ifconfigをオプションなしに実行すると、現在のeth0やloが表示されます。
$ ifconfig eth0 Link encap:Ethernet HWaddr 00:15:c5:41:95:a0 inet addr:192.168.100.18 Bcast:192.168.100.255 Mask:255.255.255.0 inet6 addr: fe80::215:c5ff:fe41:95a0/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:350974 errors:0 dropped:0 overruns:0 frame:0 TX packets:291913 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:329347998 (329.3 MB) TX bytes:36362966 (36.3 MB) Interrupt:18 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:6 errors:0 dropped:0 overruns:0 frame:0 TX packets:6 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:340 (340.0 B) TX bytes:340 (340.0 B)
仮想ネットワークデバイスeth0はEthernetに接続され、IPアドレス192.168.100.18が設定されています。
loはローカルホストです。これは127.0.0.1が割り当てられます。127.0.0.1は特殊なアドレスでこれは自分自身のマシンを指します。
ホスト名はlocalhostとなっています。他のサンプルもこちら(Ifconfig-listed)に載せておきます。
ブート時にはIPアドレスの設定などifconfigコマンドを使っています。デスクトップやノートパソコン、特にモバイルのように色々なネットワーク上で動作させるようなコンピュータはDHCP
[7]
を使ってそのネットワーク上で自動割り当てされるのが一般的ですが、ここではまず固定IPの話から進めましょう。
特定のインターフェースを利用可能するには次のようにします。この場合、eth0のインタフェースを利用可能にします。
# ifconfig eth0 up
固定IPアドレス
# ifconfig eth0 192.168.201.3 netmask 255.255.255.0
こう実行することによってeth0にはIPアドレス192.168.1.3、ブロードキャスト:192.168.1.255、マスク:255.255.255.0が設定されます。
ここではeth0に設定していますが物理的に1つのNICに複数のIPアドレスを付与できます。むかしならNIC(network interface card)に1つのIPアドレスしか付加できませんでしたが、複数のIPアドレスが設定する場合、eth0:1、eth0:2といった形でMACアドレスが同じであるeth0のインタフェースが作られます。
$ sudo ifconfig eth0:1 192.168.100.99 netmask 255.255.255.0 eth0:1 Link encap:イーサネット ハードウェアアドレス 14:da:e9:71:cd:35 inetアドレス:192.168.100.99 ブロードキャスト:192.168.100.255 マスク:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 メトリック:1 割り込み:47 ベースアドレス:0xc000
動的IPアドレス
dhcpサーバがあり動的にネットワークを設定するのは、もちろん事前にGNU/Linuxのディストリビューションの用意しているネットワーク管理環境 (NetworkManagerなどがバックグラウンドで起動されている) に、その処理を委ねますので、ユーザは一々明示的に処理するようなことはありません。GUI環境であれば通常はネットワーク設定のパネルからセットアップを行います。ここでは機能を説明するためにコマンドレベルで説明しています。
eth0にdhcpでアドレスなどを振りたいときはdhclientコマンドでeth0を指定します。
# dhclient eth0
もちろんネットワークデバイスはeth0だけなく仮想ブリッジのインタフェースも同様にIPアドレスを割り当てることができます。ブリッジのインタフェースがbr0として、dhclient br0 が可能で、成功すればbr0に動的にIPアドレスが割り振られます。
DHCPサーバ側で指定できるのはipアドレス、ゲートウェイアドレス、ドメイン名などだけではなく、DNSサーバやNTPサーバの指定などクライアント側で必要な情報を用意することができます。
ブリッジ・ルーター
TCP/IPの機能が一通り使えるようになっているので、ネットワークのブリッジを構築することも、ルータ / NAT を構築することも可能です。 ここでは実際にブリッジやルータをどう作るのかを示すことで、ブリッジやルータの機能が標準的に含まれていることを示したいと思います。
まず、Ethernetのインタフェースが物理的に2つあるハードウェア(eth0とeth1)が使え、ディストリビューションはDebain系という前提で話を進めます。
- 補足
- Raspberry Piを使ってブリッジ/NATルーターを作ることをイメージしています。
ブリッジ
事前の用意としてブリッジを作成するためには、まずbridge-utilsをインストールしておきます。
# apt-get install bridge-utils
インストールが終了したら、作業を行う前に、念のためインタフェースをすべてダウンさせておきましょう。
# ifconfig eth0 down # ifconfig eth1 down
次の手順でブリッジを構築します。
# brctl addbr br0 # brctl addif br0 eth0 # brctl addif br0 eth1 # ifconfig eth0 0.0.0.0 up # ifconfig eth1 0.0.0.0 up
- br0という名前のブリッジのインタフェースを作る。
- br0にインタフェースeth0とeth1を加える。
- eth0とeth1をIPアドレスをつけない形でup(起動)する。
これでeth0側とeth1側のパケットが通過するようになります。
ルータ / NAT
今度はルータを構築してみましょう。 ルータの機能ですが、eth0側はdhcpで自動的にIPアドレスを割り振られることを前提にし、eth1側は192.168.32.0/24のネットワークを構築するとしましょう。 前提としてはeth0側をWAN側/インターネット側、eth1をLAN側/ローカルネットワーク側とします。尚、作業を始めるまえはeth0もeth1もダウンしていることを前提とします。
まずNATに必要なカーネルのモジュールを読み込み、IPパケットをフォワードする設定にします。
# modprobe iptable_nat # echo 1 > /proc/sys/net/ipv4/ip_forward
eth0側でdhclientを使いeth0にIPアドレスを割り当てます。尚、eth0側/WAN側に上位ルータがあるといった運用をされていてDHCPサーバーがあるとします。eth1側/LAN側は192.168.32.0/24のネットワークがあるとします。
# dhclient eth0 # ifconfig eth1 192.168.32.1 netmask 255.255.255.0 # iptables --table nat --append POSTROUTING --out-interface eth0 --jump MASQUERADE # iptables --append FORWARD --in-interface eth1 --jump ACCEPT
- 注意
- 192.168.32.0/24 にはこの段階ではDHCPサーバが存在していないので、dhcpを使いネットワークアドレス取得をする前提の機材を接続しても、そのままではIPアドレスが設定されません。
脚注
- ↑ What, a real UNIX®? http://www.freebsd.org/doc/en/articles/explaining-bsd/what-a-real-unix.html
- ↑ 詳しくは Open Sources: Voices from the Open Source Revolution に納められているMarshall Kirk McKusickが書いたTwenty Years of Berkeley Unix From AT&T-Owned to Freely Redistributable を読むことをお勧めします。
- ↑ 核戦争にも耐えうる軍事ネットワークを作るためにインターネットを作ったという俗説がまことしやかに流れていますが、そのような事実はありません。Internet Society のサイトにある Brief History of the Internet に目を通して正しいインターネットの歴史の全体像を知っておきましょう。
- ↑ http://panda.com/tops-20/ by Mark Crispin
- ↑ RFC791
- ↑ あくまでもコンベンショナル(習慣的)なので、世の中には変な名前の付け方のネットワークのスペシャルデバイスファイルのルールがあるかも知れません。
- ↑ ISC DHCP https://www.isc.org/downloads/dhcp/ ここにDHCPのリファレンス実装があります。
目次へ