Linux Advanced Routing & Traffic Control HOWTO Bert Hubert Netherlabs BV bert.hubert@netherlabs.nl Thomas Graf (Section Author) tgraf%suug.ch Gregory Maxwell (Section Author) Remco van Mook (Section Author) remco@virtu.nl Martijn van Oosterhout (Section Author) kleptog@cupid.suninternet.com Paul B Schroeder (Section Author) paulsch@us.ibm.com Jasper Spaans (Section Author) jasper@spaans.ds9a.nl Pedro Larroy (Section Author) piotr%member.fsf.org 中野武雄 - 日本語訳 nakano@apm.seikei.ac.jp Revision History Revision Revision: 1.41j1 Date: 2003/10/19 日本語翻訳版 Revision $Revision: 1.41 $ $Date: 2003/10/12 15:51:04 $ DocBook Edition iproute2、トラフィック制御、および多少の netfilter に関する、極めて実践 的なアプローチ。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Table of Contents 1. 献辞 2. はじめに 2.1. 免責およびライセンス 2.2. 事前に必要な知識 2.3. Linux にできること 2.4. この文書の管理についてのメモ 2.5. 取得、CVS およびアップデートの投稿 2.6. メーリングリスト 2.7. この文書の構成 3. iproute2 入門 3.1. なぜ iproute2 なのか? 3.2. iproute2 の概略 3.3. 事前の必要条件 3.4. 現在の設定を調べてみる 3.5. ARP 4. Rules - ルーティングポリシーデータベース 4.1. 簡単なソースポリシールーティング 4.2. 複数のアップリンク/プロバイダに対するルーティング 5. GRE トンネル、その他のトンネル 5.1. トンネルに関する一般的な事柄 5.2. IP in IP トンネリング 5.3. GRE トンネリング 5.4. ユーザランドのトンネル 6. Cisco や 6bone との IPv6 トンネル 6.1. IPv6 トンネリング 7. IPSEC: インターネット越しの安全な IP 7.1. 入門編: 手動での鍵処理 7.2. 自動での鍵処理 7.3. IPSEC トンネル 7.4. 他の IPSEC ソフトウェア 7.5. 他のシステムとの IPSEC の相互運用 8. マルチキャストのルーティング 9. キューイング規則とバンド幅管理 9.1. キューおよびキューイング規則の説明 9.2. シンプルな、クラスレスのキューイング規則 9.3. いつどんなキューを使うべきか 9.4. 用語説明 9.5. クラスフルなキューイング規則 9.6. フィルタによるパケットのクラス選別 9.7. 中間キューイングデバイス (Intermediate queueing device :IMQ) 10. 複数のインターフェースを用いた負荷分散 10.1. 注意 10.2. 他の可能性 11. Netfilter と iproute でパケットに印を付ける 12. 高度なフィルタによるパケットのクラス(再)選別 12.1. u32 クラス選別器 12.2. route クラス選別器 12.3. 監視制限 (policing) フィルタ 12.4. ハッシュフィルタ: 超高速大量フィルタリング 12.5. IPv6 トラフィックのフィルタリング 13. カーネルのネットワークパラメータ 13.1. 戻り経路フィルタ (Reverse Path Filtering) 13.2. あまり知られていない設定 14. 知られざる高度なキューイング規則 14.1. bfifo/pfifo 14.2. Clark-Shenker-Zhang アルゴリズム (CSZ) 14.3. DSMARK 14.4. 入口 (ingress) qdisc 14.5. ランダム初期検知 (Random Early Detection: RED) 14.6. 汎用ランダム初期検知 (Generic Random Early Detection) 14.7. VC/ATM エミュレーション 14.8. 重み付きラウンドロビン (Weighted Round Robin: WRR) 15. クックブック 15.1. SLA の異なる複数のサイトを動作させる 15.2. ホストを SYN フラッドから守る 15.3. ICMP を帯域制限して dDoS を防ぐ 15.4. 対話的トラフィックを優先する 15.5. netfilter, iproute2, ipchains, squid を用いた透過 Web キャッ シュ 15.6. 経路ごとに MTU を設定し Path MTU Discovery 問題を回避する 15.7. MSS クランプによって Path MTU Discovery 問題を回避する (ADSL, ケーブル, PPPoE, PPtP ユーザ向け) 15.8. 究極のトラフィック調整器: 低遅延, 高速アップロード/ダウンロー ド 15.9. 単一のホストまたはネットワークの速度制限 15.10. QoS 付き nat の完全な例 16. ブリッジと、代理 ARP を用いた擬似ブリッジの構築 16.1. ブリッジと iptables の状況 16.2. ブリッジと帯域制限 16.3. 代理 ARP を用いた擬似ブリッジ 17. 動的ルーティング - OSPF と BGP 17.1. Zebra による OSPF の設定 17.2. Zebra による BGP4 の設定 18. その他の候補 19. さらに先に進むために 20. 謝辞 21. 日本語訳について ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 1. 献辞 この文書は多くの人に捧げるもので、著者はこれによって何らかのお返しをし たいと考えています。なかでも何人かの方のお名前を挙げれば: ・ Rusty Russell ・ Alexey N. Kuznetsov ・ The good folks from Google ・ The staff of Casema Internet ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 2. はじめに ようこそ、親愛なる読者のみなさん。 この文書は Linux 2.2/2.4 を用いて、より上級のルーティング (経路操作) を 行う方法について述べようとするものです。ほとんどのユーザには知られてい ないことですが、スペクタクルな作業を行うツールを、あなたは既に実行して いるのです。実は route や ifconfig などは、非常にパワフルな iproute2 と いうインフラストラクチャの、ごく一部のみを使うラッパにすぎないのです。 私はこの HOWTO が、netfilter で (もちろんほかででも) 有名な Rusty Russell の HOWTO と同じくらい読み易いものであるよう、望んでいます。 HOWTO team に送信すれば、いつでも我々とコンタク トを取れます。しかし、その質問がこの HOWTO に直接関連するものでなければ 、できればメーリングリスト (関連する節を見てください) に送ってください 。我々は無料のヘルプデスクではありませんが、メーリングリストにおける質 問には答えることが多いはずです。 この HOWTO で迷子になる前に一言。もし簡単な帯域制限 (traffic shaping) を行いたいだけならば、すべて飛ばしてその他の候補の章に向かってください 。そして CBQ.init に関する記述を読んでください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1. 免責およびライセンス This document is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. In short, if your STM-64 backbone breaks down and distributes pornography to your most esteemed customers - it's never our fault. Sorry. Copyright (c) 2002 by bert hubert, Gregory Maxwell, Martijn van Oosterhout, Remco van Mook, Paul B. Schroeder and others. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/). Please freely copy and distribute (sell or give away) this document in any format. It's requested that corrections and/or comments be forwarded to the document maintainer. It is also requested that if you publish this HOWTO in hardcopy that you send the authors some samples for "review purposes" :-) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1.1. 日本語訳 (参考) この文書は有益であることを願って配布されていますが、一切の保証はありま せん。商用・特定用途への適合に関する、黙示的な保証も一切ありません。 要するに、もしあなたの STM-64 バックボーンが壊れて落ちたり、大事な顧客 にポルノをばらまいてしまったとしても - それは私たちの責任ではありません 。ごめんなさい。 Copyright (c) 2002 by bert hubert, Gregory Maxwell, Martijn van Oosterhout, Remco van Mook, Paul B. Schroeder ほか。この文書は Open Publication License, v1.0 およびそれ以降に示されている項目と条件に従っ た場合に限り、配布できます (OPL の最新版は http://www.opencontent.org/ openpub/ にあります)。 この文書の複写や再配布 (販売・景品) は、どのようなフォーマットでも可能 です。ただし修正やコメントが、この文書の管理者に送られるようにしてくだ さい。 またもしこの HOWTO を書籍として出版する場合は、レビューのために、著者ら に数冊のサンプルを送ってください :-) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.2. 事前に必要な知識 タイトルにあるように、これは "Advanced" な HOWTO です。ロケット科学ほど ではありませんが、ある程度の事前知識はすでにあるものとしています。 必要な知識を得るための参考文献をいくつかあげておきます。 Rusty Russell の networking-concepts-HOWTO 非常に良い導入文書で、ネットワークとは何か、他のネットワークと接続 するにはどうするかを説明しています。 Linux Networking-HOWTO (以前は Net-3 HOWTO) 膨大な分量で、かつまた同時に非常に詳細です。インターネットへの接続 に関する設定 (そのほとんどは既に設定済みなのでしょうが) に関して、 たくさんのことを教えてくれます。 /usr/doc/HOWTO/NET3-4-HOWTO.txt に あることが多いと思いますが、オンライン でも読めます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.3. Linux にできること 実現可能なことを簡単なリストにしてみました。 ・ 特定のコンピュータに対するバンド幅を絞る ・ 特定のコンピュータに「向かう」バンド幅を絞る ・ バンド幅を公平に共有するための手助けをする ・ ネットワークを DoS 攻撃から守る ・ インターネットをあなたの顧客から守る ・ 複数あるサーバをひとつにまとめ、負荷分散や可用性の向上を実現する ・ あなたのコンピュータへのアクセスを制限する ・ あなたのユーザが行う他のホストへのアクセスを制限する ・ 次のような情報に基づいてルーティングを行う: ユーザ id (本当)、MAC アドレス、発信元 IP アドレス、ポート、サービスタイプ、時刻、パケッ トの中味など 現在のところ、これらの先進的な機能を使っている人はあまり多くありません 。これにはいくつかの理由があります。提供されている文書の記述は詳細です が、あまり実践的なものではありません。トラフィック制御はほとんど文書化 されていません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.4. この文書の管理についてのメモ この文書に関して、いくつか気にとめていてほしいことがあります。私はこの 文書の大部分を書きましたが、今後もそのようにし続けたいとは決して思って いません。私はオープンソースの強い信奉者であり、したがって皆さんがフィ ードバック、アップデート、パッチなどを送ってくださることを、つねに歓迎 します。誤字脱字やよくある単純な間違いでも、遠慮せずに知らせてください 。私の英語がちょっとぎごちなく感じられるようでしたら、どうか私がネイテ ィブではないことを思い出してください。気軽に提案を送って下さい。 ある部分のメンテナンスをより良く行う資格が自分にあると感じられたり、あ るいは新しい部分を書いてメンテナンスできると思われたら、大いに歓迎しま す。この HOWTO の SGML は CVS で管理されています。私はもっとたくさんの 人たちがこの文書に関する作業を行うような将来を、強く願っています。 この助けとなるよう、この文書には FIXME という注意書きがたくさん置かれて います。パッチはつねに歓迎します! FIXME を見つけたら、未知の領域を歩い ているのだと思ってください。これは他の部分に間違いがないという意味では ありませんが、そのようなところでは特に注意してください。もしどれかの内 容を確認することができたら、私たちに知らせてください。その FIXME を除去 します。 この HOWTO で、私は勝手な振る舞いをしていることもあります。例えば、私は 10Mbit のインターネット接続を前提としていますが、これがそれほどよくある 状況では無いことはよーくわかっています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.5. 取得、CVS およびアップデートの投稿 この文書の原本の置き場所はこちら です。 現在、全世界からの匿名 CVS アクセスが可能になっています。これにはいろい ろな面で利点があります。この HOWTO のより新しい版への更新が簡単ですし、 パッチの投稿にもまったく面倒がありません。 さらに、著者達がソースに対して独立に作業できるのも良いところです。 ┌──────────────────────────────────┐ │$ export CVSROOT=:pserver:anon@outpost.ds9a.nl:/var/cvsroot │ │$ cvs login │ │CVS password: [enter 'cvs' (without 's)] │ │$ cvs co 2.4routing │ │cvs server: Updating 2.4routing │ │U 2.4routing/lartc.db │ └──────────────────────────────────┘ 変更を行い、それらを提供したいと思われましたら、 cvs -z3 diff -uBb を実 行し、その出力を にメールしてください。こうすると、我々 は楽に反映作業ができます。よろしく。どうか編集は必ず .db ファイルに対し て行ってください。ちなみに他のファイルは、この .db ファイルから生成され たものです。 Makefile を用いると、postscript, dvi, pdf, html, プレインテキストの生成 が楽にできます。これらのフォーマットすべてを生成するには、 docbook, docbook-utils, ghostscript, tetex のインストールが必要です。 2.4routing.sgml は編集しないようにご注意ください! この中味は本 HOWTO の 古い版です。正しいファイルは lartc.db です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.6. メーリングリスト 筆者たちが受け取る、この HOWTO に関するメールは、だんだん増えてきていま す。コミュニティからの興味が明らかになったので、メーリングリストを開始 して、Advanced ルーティングとトラフィック制御に関する議論を皆でできるよ うにしました。リストを購読するのはこちら からできます。 申し訳ありませんが、著者達はリスト以外で尋ねられた質問に対しては、ほと んど回答することがありません。私たちはリストのアーカイブを、ある種の知 識データベースにしたいと考えているのです。質問がありましたら、まずアー カイブを検索し、そしてメーリングリストに投稿してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.7. この文書の構成 我々は自分たちが面白いと思った内容はほぼ直ちに作業しますが、これは逆に 、最初は説明が不十分だったり、完成していない部分があるということでもあ ります。すみませんがそのようなところは目をつぶっていただき、そのうちす べてが明らかになる、と思っておいてください。 ルーティングとフィルタリングは 2 つの別々のことです。フィルタリングは Rusty の HOWTO で非常に良く説明されています。次の場所にあります。 ・ Rusty's Remarkably Unreliable Guides 私たちは、 netfilter と iproute2 を組み合わせて可能になることについて集 中するつもりです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 3. iproute2 入門 3.1. なぜ iproute2 なのか? ほとんどの Linux ディストリビューション (およびほとんどの UNIX) では、 伝統ある arp, ifconfig, route といったコマンドを現在でも用いています。 これらのツールは動作はするものの、 Linux 2.2 以降ではときどき期待通りの 働きをしないことがあります。例えば、GRE トンネルは今日ではルーティング の一部となっていますが、これには完全に別のツールが必要です。 iproute2 を用いれば、トンネルの部分もツールセットに組み込まれます。 2.2 以降の Linux カーネルには、完全に再設計されたネットワークサブシステ ムが含まれています。この新しいネットワークコードによって、 Linux には性 能と機能との (特に機能に関しては一般の OS のなかにほとんど競争者がいな いくらいの) 向上がもたらされました。実際のところ、新しいルーティング・ フィルタリング・クラス付けのコードは、ルータやファイアウォール、帯域制 限などの専用製品の多くに比べても、より高機能になっています。 ネットワーキングに関する新たな概念が発明されるにつれ、それらは既存の OS の既存のフレームワークの上に塗り固められてきました。このようなシステム の多層化が進むことによって、ネットワーキングコードは (人間の言語の多く に非常によく似た) たくさんの奇妙な振る舞いだらけになってしまいました。 過去には、Linux は SunOS の処理の多くをエミュレートしてきましたが、これ は理想的なこととは言えませんでした。 この新しいフレームワークは、以前の Linux が及ばなかった機能を、明確に表 現することを可能としたのです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.2. iproute2 の概略 Linux はバンド幅の供給に関して洗練されたシステムを備えており、これは Traffic Control と呼ばれています。このシステムは、クラス分け・優先付け 、共有化、制限といった様々な手法を、内向き・外向きのトラフィック両方で サポートしています。 では、iproute2 の可能性を手短にご紹介することにしましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.3. 事前の必要条件 まずユーザランドのツールがインストールされていることを確認する必要があ ります。このパッケージは RedHat と Debian の両者では 'iproute' という名 前になっています。それ以外の場合でも ftp://ftp.inr.ac.ru/ip-routing/ iproute2-2.2.4-now-ss??????.tar.gz" から取得できます。 最新版を入手するにはこのリンク も利用できます。 iproute の一部には、適切なカーネルオプションを有効にしなければ使えない ものもあります。なお RedHat のリリース 6.2 以前のカーネルでは、トラフィ ック制御機能がデフォルトのカーネルには組み込まれていません。 RedHat 7.2 には、すべてがデフォルトで組み込まれています。 また自分でカーネルを用意する場合には、 netlink サポートが含まれているこ とを確認してください。 iproute2 ではこれが必要です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.4. 現在の設定を調べてみる 驚かれるかもしれませんが、iproute2 は既に設定済みなのです! 現状のコマン ドである ifconfig と route も、既に advanced なシステムコールを用いてい るのです。ただし非常に基本的な (退屈な) 設定のみですが。 中心となるコマンドは ip です。ではこのコマンドに、我々のインターフェー スの状態を表示させてみましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.4.1. ip にリンクを表示させる ┌────────────────────────────────────────┐ │[ahu@home ahu]$ ip link list │ │1: lo: mtu 3924 qdisc noqueue │ │ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 │ │2: dummy: mtu 1500 qdisc noop │ │ link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff │ │3: eth0: mtu 1400 qdisc pfifo_fast qlen 100 │ │ link/ether 48:54:e8:2a:47:16 brd ff:ff:ff:ff:ff:ff │ │4: eth1: mtu 1500 qdisc pfifo_fast qlen 100 │ │ link/ether 00:e0:4c:39:24:78 brd ff:ff:ff:ff:ff:ff │ │3764: ppp0: mtu 1492 qdisc pfifo_fast qlen 10 │ │ link/ppp │ └────────────────────────────────────────┘ 環境によって表示は異なりますが、私の自宅の NAT ルータで ip が表示したの はこのような内容でした。ここではすべてが直接関係しているわけではないの で、一部のみを説明します。 まず loopback インターフェースがあります。これがなくても動作しているコ ンピュータもあるかもしれませんが、そのような状況はまずい、というのが私 のアドバイスです。 MTU サイズ (最大転送単位) は 3924 オクテットで、キュ ーイングは行いません。 loopback インターフェースはカーネルによる一種の 虚構ですから、これは当然です。 いまは dummy インターフェースは飛ばします。これはあなたのコンピュータに はないでしょうから。続いて物理的なネットワークインターフェースが 2 つあ ります。ひとつはケーブルモデムの側に、もう一つは自宅イーサネットセグメ ントの側にあります。さらに、ppp0 インターフェースがあるのもわかります。 IP アドレスが表示されていないことに気づかれたでしょうか。 iproute は「 リンク」と「IP アドレス」という二つの概念を切り離しています。 IP エイリ アスを用いると、「特定の」IP アドレスという概念は、どのみち極めて不適切 なものになってしまいます。 そのかわり MAC アドレスは表示されています。これはイーサネットインターフ ェースのハードウェア識別子です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.4.2. ip に IP アドレスを表示させる ┌────────────────────────────────────────┐ │[ahu@home ahu]$ ip address show │ │1: lo: mtu 3924 qdisc noqueue │ │ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 │ │ inet 127.0.0.1/8 brd 127.255.255.255 scope host lo │ │2: dummy: mtu 1500 qdisc noop │ │ link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff │ │3: eth0: mtu 1400 qdisc pfifo_fast qlen 100 │ │ link/ether 48:54:e8:2a:47:16 brd ff:ff:ff:ff:ff:ff │ │ inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0 │ │4: eth1: mtu 1500 qdisc pfifo_fast qlen 100 │ │ link/ether 00:e0:4c:39:24:78 brd ff:ff:ff:ff:ff:ff │ │3764: ppp0: mtu 1492 qdisc pfifo_fast qlen 10 │ │ link/ppp │ │ inet 212.64.94.251 peer 212.64.94.1/32 scope global ppp0 │ └────────────────────────────────────────┘ 今回は情報が多くなりました。すべてのアドレスと、それがどのカードに属し ているかが表示されています。'inet' は Internet (IPv4) の意味です。ほか にもたくさんのアドレスファミリーがありますが、現在のところはまだ出てき ていません。 eth0 をもうちょっと詳しく調べてみましょう。これには inet アドレス '10.0.0.1/8' が関連付けられています。これは何を意味するのでしょう? /8 はネットワークアドレス部のビット数を表しています。全部で 32 ビットあり ますから、残りの 24 ビットがネットワークの内部で使えるわけです。 10.0.0.1 の最初の 8 ビットは 10.0.0.0 となりますので、これが我々のネッ トワークアドレスです。また我々のネットマスクは 255.0.0.0 です。 他のビットはこのインターフェースと接続しています。例えば 10.250.3.13 に は、 (現在 10.0.0.1 である) eth0 から直接に接続できます。 ppp0 にも同じ考えが適用できますが、数値が違います。 ppp0 のアドレスは 212.64.94.251 で、サブネットマスクはありません。これは point-to-point 接続であることを意味しており、 212.64.94.251 を除いては、すべてのアドレ スがリモートです。ただし、他にも情報があります。この表示によれば、接続 の他端には、 (またもや) 単独のアドレス 212.64.94.1 のみがあります。この /32 は、ネットワークビットが一切存在しないことを意味しています。 これらの概念を把握することは、決定的に重要です。もし問題があるようなら 、この HOWTO の最初のほうで紹介した文書を参照して下さい。 さらに 'qdisc' にも気づいたでしょうか。これは Queueing Discipline を表 しています。これも後ほど非常に重要となります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.4.3. ip に経路を表示させる さて、これで 10.x.y.z というアドレスの探し方はわかりました。また 212.64.94.1 に到達することもできます。しかしこれだけでは十分ではありま せん。全世界へと到達するための方法を知る必要があります。インターネット には ppp 接続経由でつながっており、よって 212.64.94.1 は我々から世界に 向けたパケットを送る先で、またその結果を私たちのところに戻してくる場所 であることがわかります。 ┌──────────────────────────────────┐ │[ahu@home ahu]$ ip route show │ │212.64.94.1 dev ppp0 proto kernel scope link src 212.64.94.251 │ │10.0.0.0/8 dev eth0 proto kernel scope link src 10.0.0.1 │ │127.0.0.0/8 dev lo scope link │ │default via 212.64.94.1 dev ppp0 │ └──────────────────────────────────┘ これはほぼ見ただけでわかるでしょう。最初の 3 行は、既に ip address show によって示された状態を表示しています。最後の行は、残りの世界が 212.64.94.1 の先に見つかること、これが我々のデフォルトゲートウェイであ ることを示しています。これがまさに「玄関口」であることは、言葉からもわ かります。すなわち我々は 212.64.94.1 にパケットを送れば、あとの残りはそ ちらで面倒を見てもらえるのです。 参照のために、古い route ユーティリティの結果もお見せしておきましょう。 ┌───────────────────────────────────────┐ │[ahu@home ahu]$ route -n │ │Kernel IP routing table │ │Destination Gateway Genmask Flags Metric Ref Use │ │Iface │ │212.64.94.1 0.0.0.0 255.255.255.255 UH 0 0 0 ppp0 │ │10.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 eth0 │ │127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo │ │0.0.0.0 212.64.94.1 0.0.0.0 UG 0 0 0 ppp0 │ └───────────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.5. ARP ARP はアドレス解決プロトコル (Address Resolution Protocol) の意味で、 RFC 826 に記述されています。ARP はネットワークにあるマシンが、同じローカルネットワークにある他のマシン のハードウェア位置 (アドレス) を解決するために用います。インターネット にあるマシンは、一般に名前で知られており、この名前が IP アドレスへと解 決されます。これが、foo.com ネットワークにあるマシンが bar.net ネットワ ークにある他のマシンと通信できる理由です。しかし IP アドレスは、マシン の物理的な位置を伝えてはくれません。そこで ARP が登場するわけです。 非常に簡単な例を取り上げましょう。いくつかのマシンからなるネットワーク を想像してください。いま私のネットワークにある 2 台のマシンが、 IP アド レス 10.0.0.1 の foo と IP アドレス 10.0.0.2 の bar であるとします。い ま foo が、bar が生きているかどうか ping で確かめたいと思いました。しか し残念なことに、foo はどこに bar がいるかがわかりません。よって foo は bar に ping するまえに、ARP 要求を送る必要があります。この ARP 要求は、 「bar (10.0.0.2)! どこにいるんだ?」と foo が叫ぶようなものです。この結 果ネットワークのすべてのマシンは foo が叫んでいるのを聞きますが、bar (10.0.0.2) のみがこれに応えます。 bar はこのとき ARP 返信を直接 foo に 返します。これは「foo (10.0.0.1)、私はここ 00:60:94:E9:08:12 にいるよ」 と bar が言うのに似ています。この簡単な、友達がネットワークのどこにいる かを知るためのやり取りの後に、 foo は bar と通信できるようになります。 これは foo (foo の arp キャッシュ) が bar の場所を忘れるまで (通常 Unix では 15 分) 有効です。 では、実際にどのように動作するかを見てみましょう。あなたのマシンの現在 の arp/近隣キャッシュ/テーブルは、次のようにしてみることができます。 ┌──────────────────────────────────┐ │[root@espa041 /home/src/iputils]# ip neigh show │ │9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable │ │9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud reachable │ └──────────────────────────────────┘ みてわかるとおり私のマシン espa041 (9.3.76.41) は、 espa042 (9.3.76.42) および espagate (9.3.76.1) の位置を知っています。ここで別のマシンを arp キャッシュに追加してみましょう。 ┌────────────────────────────────────────┐ │[root@espa041 /home/paulsch/.gnome-desktop]# ping -c 1 espa043 │ │PING espa043.austin.ibm.com (9.3.76.43) from 9.3.76.41 : 56(84) bytes of data. │ │64 bytes from 9.3.76.43: icmp_seq=0 ttl=255 time=0.9 ms │ │ │ │--- espa043.austin.ibm.com ping statistics --- │ │1 packets transmitted, 1 packets received, 0% packet loss │ │round-trip min/avg/max = 0.9/0.9/0.9 ms │ │ │ │[root@espa041 /home/src/iputils]# ip neigh show │ │9.3.76.43 dev eth0 lladdr 00:06:29:21:80:20 nud reachable │ │9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable │ │9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud reachable │ └────────────────────────────────────────┘ espa041 が espa043 に接続を試みた結果、 espa043 のハードウェアアドレス/ 位置が arp/近隣キャッシュに追加されました。よって espa043 のエントリが (その間 2 台の間に通信がなく) 時間切れになるまで、 espa041 は espa043 の場所を知っており、ARP 要求を送る必要はありません。 では espa043 を arp キャッシュから削除してみましょう。 ┌───────────────────────────────────┐ │[root@espa041 /home/src/iputils]# ip neigh delete 9.3.76.43 dev eth0 │ │[root@espa041 /home/src/iputils]# ip neigh show │ │9.3.76.43 dev eth0 nud failed │ │9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable │ │9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud stale │ └───────────────────────────────────┘ これで espa041 は再び espa043 がどこにあるかを忘れてしまったので、次に espa043 と通信しようと思ったときには再度 ARP 要求を送る必要があります。 上記の出力には、espagate (9.3.76.1) が "stale" 状態に変わったことも示さ れています。これが意味するのは、位置情報はまだ有効であるものの、そのマ シンと最初にやり取りをする際には確認が必要である、ということです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 4. Rules - ルーティングポリシーデータベース 大規模なルータを管理していると、利用者それぞれに応じて、違ったやり方で サービスを提供しなければならないことがよくあります。ルーティングポリシ ーデータベースで複数のルーティングテーブルセットを用いると、これが可能 となります。 この機能を利用したい場合は、カーネルのコンパイルの際に "IP: advanced router" と "IP: policy routing" の各機能を有効にする必要があります。 カーネルはルーティングの判断をする際に、どのテーブルにあたるべきかをま ず決定します。デフォルトでは 3 つのテーブルがあります。古い 'route' ツ ールは、main テーブルと local テーブルを変更します。デフォルトでは ip ツールも同様です。 デフォルトのルールは: ┌──────────────────────────────────┐ │[ahu@home ahu]$ ip rule list │ │0: from all lookup local │ │32766: from all lookup main │ │32767: from all lookup default │ └──────────────────────────────────┘ これはすべてのルールの優先度の一覧です。すべてのルールがすべてのパケッ ト ('from all') に適用されることがわかります。以前にも 'main' テーブル は見ています (ip route ls で出力されたものです)。しかし 'local' テーブ ルと 'default' テーブルは今回が初めてです。 凝ったことをするには、別のテーブルを指すルールを生成します。これによっ てシステムワイドのルーティング規則を上書きできるのです。 複数のマッチルールがあるときにカーネルがどのように動作するかに関する正 確な仕組みについては、Alexey の ip-cref 文書を見てください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.1. 簡単なソースポリシールーティング ここでまた実際の例を見てみることにしましょう。私は 2 台 (実際にはそろそ ろ返さないといけないのも含めると 3 台) のケーブルモデムを持っていて、そ れらは Linux の NAT (マスカレード) ルータにつながっています。ここに住ん でいる人達は、インターネット接続の対価を私に支払っています。私の同居人 の一人が、hotmail しか見に行かないので額を減らしてほしい、と言ってきた としましょう。私に異存はありませんが、彼らにはローエンドの方のケーブル モデムを使ってもらうことにします。 「速い」ケーブルモデムは 212.64.94.251 で、 212.64.94.1 に PPP 接続され ています。「遅い」ケーブルモデムはいろいろな IP アドレスを取りますが、 この例では 212.64.78.148 とし、195.96.98.253 に接続されているとします。 local テーブルは次の通り: ┌──────────────────────────────────────┐ │[ahu@home ahu]$ ip route list table local │ │broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 │ │local 10.0.0.1 dev eth0 proto kernel scope host src 10.0.0.1 │ │broadcast 10.0.0.0 dev eth0 proto kernel scope link src 10.0.0.1 │ │local 212.64.94.251 dev ppp0 proto kernel scope host src 212.64.94.251 │ │broadcast 10.255.255.255 dev eth0 proto kernel scope link src 10.0.0.1 │ │broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 │ │local 212.64.78.148 dev ppp2 proto kernel scope host src 212.64.78.148 │ │local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 │ │local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 │ └──────────────────────────────────────┘ 自明ですが、どこかで指定しておかなければならない内容です。で、それがこ れらです。default テーブルは空です。 では 'main' テーブルをみましょう: ┌───────────────────────────────────┐ │[ahu@home ahu]$ ip route list table main │ │195.96.98.253 dev ppp2 proto kernel scope link src 212.64.78.148 │ │212.64.94.1 dev ppp0 proto kernel scope link src 212.64.94.251 │ │10.0.0.0/8 dev eth0 proto kernel scope link src 10.0.0.1 │ │127.0.0.0/8 dev lo scope link │ │default via 212.64.94.1 dev ppp0 │ └───────────────────────────────────┘ ここで 'John' という新しいテーブルを、さきほど仮定した同居人のために作 ります。数値だけで設定を行うこともできますが、このテーブルを /etc/ iproute2/rt_tables に追加するほうがずっと楽になります。 ┌──────────────────────────────────┐ │# echo 200 John >> /etc/iproute2/rt_tables │ │# ip rule add from 10.0.0.10 table John │ │# ip rule ls │ │0: from all lookup local │ │32765: from 10.0.0.10 lookup John │ │32766: from all lookup main │ │32767: from all lookup default │ └──────────────────────────────────┘ これであと必要なのは John のテーブルを作って、ルートキャッシュをフラッ シュ (クリア) するだけです。 ┌──────────────────────────────────┐ │# ip route add default via 195.96.98.253 dev ppp2 table John │ │# ip route flush cache │ └──────────────────────────────────┘ これで完了です。これを ip-up に対して実装するのは、読者の課題としておき ましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2. 複数のアップリンク/プロバイダに対するルーティング 以下のようなよくある場合を考えてみましょう。ローカルなネットワーク (1 台のマシンかもしれませんが) をインターネットへと接続しているプロバイダ が 2 つあるような場合です。 ┌─────────────────────────────────────┐ │ ________ │ │ +------------+ / │ │ | | | │ │ +-------------+ Provider 1 +------- │ │ __ | | | / │ │ ___/ \_ +------+-------+ +------------+ | │ │ _/ \__ | if1 | / │ │ / \ | | | │ │| Local network -----+ Linux router | | Internet │ │ \_ __/ | | | │ │ \__ __/ | if2 | \ │ │ \___/ +------+-------+ +------------+ | │ │ | | | \ │ │ +-------------+ Provider 2 +------- │ │ | | | │ │ +------------+ \________ │ └─────────────────────────────────────┘ この設定においては、通常 2 つの問題点があります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2.1. アクセス分割 最初の問題点は、特定のプロバイダからやって来たパケットに対して、応答を どのようにルーティングするかです。例えば Provider 1 からのパケットに対 して、返信を再び同じプロバイダに戻すにはどうすればいいでしょう。 まず記号的な名前を設定しましょう。 $IF1 を第一インターフェース (上記の 絵の if1)、 $IF2 を第二インターフェースとします。次に $IP1 は $IF1 に関 連付けられた IP アドレス、 $IP2 は $IF2 に関連付けられた IP アドレスと します。さらに、 $P1 を Provider 1 のゲートウェイの IP アドレス、 $P2 を Provider 2 のゲートウェイの IP アドレスとします。最後に、$P1_NET を $P1 の所属する IP ネットワーク、 $P2_NET を $P2 の所属する IP ネットワ ークとします。 2 つのルーティングテーブルを追加します。ここでは T1 および T2 とします 。これらは /etc/iproute2/rt_tables に追加します。そしてこれらのテーブル でのルーティングを次のように設定します。 ┌──────────────────────────────────┐ │ ip route add $P1_NET dev $IF1 src $IP1 table T1 │ │ ip route add default via $P1 table T1 │ │ ip route add $P2_NET dev $IF2 src $IP2 table T2 │ │ ip route add default via $P2 table T2 │ │ │ └──────────────────────────────────┘ 特別なことは何もしておらず、ゲートウェイへの経路と、そのゲートウェイを 経由する default 経路を作っただけです。これは上流のプロバイダが 1 つだ けの場合と同じですが、経路をプロバイダごとに別々のテーブルに置いていま す。ここではネットワークの経路だけで十分です。なぜならネットワークを指 定すれば、そこにあるすべてのホストに到達でき、前述のようにゲートウェイ もそのうちのひとつだからです。 次に main ルーティングテーブルを設定します。パケットを直接の近隣に送る 際には、その近隣に接続しているインターフェースからにするのが良い考えで す。ここで `src' 引数によって、確実に正しい起点 IP アドレスが選択されて いることにも注目してください。 ┌──────────────────────────────────┐ │ ip route add $P1_NET dev $IF1 src $IP1 │ │ ip route add $P2_NET dev $IF2 src $IP2 │ │ │ └──────────────────────────────────┘ ここで default ルートを好きな方に設定します。 ┌──────────────────────────────────┐ │ ip route add default via $P1 │ │ │ └──────────────────────────────────┘ 次にルーティングの rule を設定します。これらは実際には、どのルーティン グテーブルを使うかの選択です。既にソースアドレスが付けられている場合に は、そのインターフェースから出ていくように経路を選びたいはずです。 ┌──────────────────────────────────┐ │ ip rule add from $IP1 table T1 │ │ ip rule add from $IP2 table T2 │ │ │ └──────────────────────────────────┘ この一連のコマンドによって、特定のインターフェースからやって来たトラフ ィックが、必ずそのインターフェースを通して応答されることが確実となりま した。 ┌──────────────────────────────────────────┐ │ Warning │ ├──────────────────────────────────────────┤ │読者である Rod Roark によると: 「$P0_NET がローカルネットワークで $IF0 │ │をそのインターフェースとすると、次のようなエントリも追加しておくほうが │ │良いでしょう │ │┌────────────────────────────────────────┐│ ││ip route add $P0_NET dev $IF0 table T1 ││ ││ip route add $P2_NET dev $IF2 table T1 ││ ││ip route add 127.0.0.0/8 dev lo table T1 ││ ││ip route add $P0_NET dev $IF0 table T2 ││ ││ip route add $P1_NET dev $IF1 table T2 ││ ││ip route add 127.0.0.0/8 dev lo table T2 ││ │└────────────────────────────────────────┘│ │」 │ └──────────────────────────────────────────┘ さて、これは非常に基本的な設定にすぎません。これはルータで動作している プロセスと、マスカレードされている場合にはローカルネットワークに対して も動作するでしょう。それ以外としては、両方のプロバイダから IP 空間をも らっている場合と、片方のプロバイダに対してのみマスカレードをしている場 合とが考えられます。このいずれにおいても、どちらのプロバイダを通して発 信するかを、ローカルネットワークのマシンの IP アドレスから決めるような ルールを追加することになるでしょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2.2. 負荷分散 二番目の問題点は、二つのプロバイダを通して出て行くトラフィックをバラン スさせるやり方です。これは実際には、前述のようなアクセス分割を既に行っ ていれば、難しくありません。 default の経路として片方のプロバイダを選ぶ代わりに、 default 経路を multipath 経路として設定します。デフォルトのカーネルでは、これで両者の プロバイダへの経路がバランスされます。これは次のようにして行います (こ こでもアクセス分割の節における例での場合を考えます)。 ┌─────────────────────────────────────────┐ │ ip route add default scope global nexthop via $P1 dev $IF1 weight 1 \ │ │ nexthop via $P2 dev $IF2 weight 1 │ │ │ └─────────────────────────────────────────┘ これで両方のプロバイダへの経路がバランスされます。 weight パラメータは 、一方のプロバイダをもう一方より優先するよう調節します。 このバランス化は経路に基づいたもので、経路はキャッシュされるため、完全 とは言えません。なぜかというと、良く利用されるサイトに対する経路は、つ ねに同じプロバイダから出て行くことになるからです。 さらに、本当にこれを実行した場合は、 Julian Anastasov のページ にあるパッチをみるとよいでしょう。これらを用い ると、より良好な動作が期待できます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 5. GRE トンネル、その他のトンネル Linux では 3 種類のトンネルを利用できます。 IP in IP トンネル、GRE トン ネル、そしてカーネル外でのトンネル (例えば PPTP のようなもの) です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.1. トンネルに関する一般的な事柄 トンネルを用いると、非常に例外的な、また非常にスマートな状況を実現でき ます。またトンネルは、設定を正しく行わないと、状況をメチャメチャにする こともあります。意図していることを「正確に」理解していない限り、 default 経路をトンネルデバイスに向けてはいけません :-) また、トンネルを 通すとオーバーヘッドが増えます。なぜならトンネルでは IP ヘッダに余分な データセットを必要とするからです。通常これはパケットあたり 20 バイトな ので、仮にネットワークの通常のパケットサイズ (MTU) を 1500 バイトとすれ ば、トンネルを通して送られるパケットは 1480 バイトの大きさしか持たない ことになります。これは必ずしも問題になるとは限りませんが、大きなネット ワークをトンネルで接続しようとする際には、 IP パケットのフラグメント化/ 再構成について、よく勉強しておくことです。あ、それからもちろん、トンネ ルを掘る最速の方法は、両側から掘り進めることです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.2. IP in IP トンネリング Linux では、このトンネルの利用はかなり昔から可能でした。 2 つのカーネル モジュール、ipip.o と new_tunnel.o が必要です。 いま 3 つのネットワークがあるとしましょう。内部ネットワーク A と B、そ してその中間にあるネットワーク C (あるいはインターネット) です。ネット ワーク A は: ┌──────────────────────────────────┐ │network 10.0.1.0 │ │netmask 255.255.255.0 │ │router 10.0.1.1 │ └──────────────────────────────────┘ このルータのネットワーク C 側のアドレスは 172.16.17.18 です。 ネットワーク B では: ┌──────────────────────────────────┐ │network 10.0.2.0 │ │netmask 255.255.255.0 │ │router 10.0.2.1 │ └──────────────────────────────────┘ このルータのネットワーク C 側のアドレスは 172.19.20.21 です。 ここではネットワーク C は、 A から B へのパケット (およびその逆) をすべ て通すとします。ここにはインターネットを利用することもできます。 では作業に入ります: まず最初にモジュールをインストールします。 ┌──────────────────────────────────┐ │modprobe ipip.o │ │modprobe new_tunnel.o │ └──────────────────────────────────┘ 続いてネットワーク A のルータで次の内容を実行します: ┌──────────────────────────────────┐ │ifconfig tunl0 10.0.1.1 pointopoint 172.19.20.21 │ │route add -net 10.0.2.0 netmask 255.255.255.0 dev tunl0 │ └──────────────────────────────────┘ そしてネットワーク B のルータでは: ┌──────────────────────────────────┐ │ifconfig tunl0 10.0.2.1 pointopoint 172.16.17.18 │ │route add -net 10.0.1.0 netmask 255.255.255.0 dev tunl0 │ └──────────────────────────────────┘ トンネルの利用を終わるには: ┌──────────────────────────────────┐ │ifconfig tunl0 down │ └──────────────────────────────────┘ あっという間に終わりました。ただし、ブロードキャストや IPv6 のトラフィ ックを IP-in-IP トンネルを通してフォワードすることはできません。単に通 常では通信ができない 2 つの IPv4 ネットワークを接続しただけ、です。互換 性に関して言いますと、このコードは長いこと存在しているため、ずっと昔の 1.3 カーネルくらいまでは互換です。しかし私が知る限り、 Linux の IP-in-IP トンネルは他の OS やルータと一緒には動作しません。これはシンプ ルで、ちゃんと働きます。これでなければならない理由があるなら使えばいい ですし、そうでなければ GRE を使うといいでしょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.3. GRE トンネリング GRE は元々 Cisco によって開発されたトンネリングプロトコルで、 IP-in-IP トンネリングよりはもうちょっと高機能です。例えばマルチキャストトラフィ ックや IPv6 も、GRE トンネルを通して転送できます。 Linux では、ip_gre.o モジュールが必要です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.3.1. IPv4 トンネリング まず先に IPv4 のトンネリングから行きましょう。 いま 3 つのネットワークがあるとしましょう。内部ネットワーク A と B、そ してその中間にあるネットワーク C (あるいはインターネット) です。 ネットワーク A は: ┌──────────────────────────────────┐ │network 10.0.1.0 │ │netmask 255.255.255.0 │ │router 10.0.1.1 │ └──────────────────────────────────┘ このルータのネットワーク C 側のアドレスは 172.16.17.18 です。このネット ワークを neta と呼びましょう (ええ、独創的な名前でないのはわかってます) 。 そしてネットワーク B は: ┌──────────────────────────────────┐ │network 10.0.2.0 │ │netmask 255.255.255.0 │ │router 10.0.2.1 │ └──────────────────────────────────┘ このルータのネットワーク C 側のアドレスは 172.19.20.21 です。このネット ワークを netb と呼びましょう (これも独創的ではありませんな)。 ネットワーク C は、 A から B へのパケット (およびその逆) はすべて通すと します。ここではその方法・理由に関しては気にしないことにします。 ネットワーク A のルータで、次を実行します。 ┌──────────────────────────────────────┐ │ip tunnel add netb mode gre remote 172.19.20.21 local 172.16.17.18 ttl 255 │ │ip link set netb up │ │ip addr add 10.0.1.1 dev netb │ │ip route add 10.0.2.0/24 dev netb │ └──────────────────────────────────────┘ この内容について少々説明しましょう。1 行目ではトンネルデバイスを追加し 、それを netb と呼ぶことにしました (理由は明らかでしょうが、行き先の名 前だからです)。さらにここでは、 GRE プロトコルを用いること (mode gre)、 リモートアドレスは 172.19.20.21 であること (他端のルータ)、トンネリング パケットの発信元は 172.16.17.18 であること (これによってルータにはネッ トワーク C の複数の IP アドレスを与えることができ、そのうちのどれをトン ネルに用いるかを決定できます)、そしてパケットの TTL フィールドを 255 に すること (ttl 255)、などを指定しています。 2 行目ではデバイスを有効にしています。 3 行目では、新たに生まれたインターフェース netb に、アドレス 10.0.1.1 を与えています。小さいネットワークならこれで OK ですが、いまから鉱山探 検 (たくさんのトンネル掘り) をはじめようとしている場合は、トンネリング インターフェースには別の領域の IP アドレスを与えるほうが良いかもしれま せん (この例なら 10.0.3.0 が使えます)。 4 行目では、ネットワーク B への経路を設定しています。ネットワークの指定 が異なっていることに注意してください。この書式に慣れていない人に、簡単 な原理を説明します: ネットワークを 2 進形式で書き、1 の数を数えてくださ い。やり方がわからなければ、255.0.0.0 が /8、 255.255.0.0 が /16、 255.255.255.0 が /24 であると暗記しましょう。あ、それから 255.255.254.0 は /23 です。念のため。 ここはこれで十分でしょう。ではネットワーク B のルータに行きましょう。 ┌──────────────────────────────────────┐ │ip tunnel add neta mode gre remote 172.16.17.18 local 172.19.20.21 ttl 255 │ │ip link set neta up │ │ip addr add 10.0.2.1 dev neta │ │ip route add 10.0.1.0/24 dev neta │ └──────────────────────────────────────┘ そしてトンネルを削除したくなったら、ルータ A で: ┌──────────────────────────────────┐ │ip link set netb down │ │ip tunnel del netb │ └──────────────────────────────────┘ もちろんルータ B では netb を neta に置き換えてください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.3.2. IPv6 トンネリング IPv6 アドレスに関する説明は Section 6 を見てください。 ではトンネルについて: 次のような IPv6 ネットワークがあるとします。そしてこれを 6bone や友達の ネットワークに接続したいとしましょう。 ┌──────────────────────────────────┐ │Network 3ffe:406:5:1:5:a:2:1/96 │ └──────────────────────────────────┘ IPv4 アドレスは 172.16.17.18 で、6bone ルータの IPv4 アドレスは 172.22.23.24 とします。 ┌───────────────────────────────────────┐ │ip tunnel add sixbone mode sit remote 172.22.23.24 local 172.16.17.18 ttl 255 │ │ip link set sixbone up │ │ip addr add 3ffe:406:5:1:5:a:2:1/96 dev sixbone │ │ip route add 3ffe::/15 dev sixbone │ └───────────────────────────────────────┘ 説明しましょう。1 行目では、 sixbone という名前のトンネルデバイスを生成 しています。これには mode sit (IPv6 を IPv4 にトンネリングする) を与え 、行き先 (remote) と発信元 (local) を指定しています。 TTL は最大の 255 にしています。次に、このデバイスをアクティブにしています (up)。その後、 我々のネットワークのアドレスを追加し、 3ffe::/15 (現在のところ 6bone の 全体) への経路をこのトンネルにしています。 GRE トンネルは現在のところ好んで用いられているトンネリング形式です。こ れは Linux コミュニティ以外でも広く採用されている標準で、よって良いもの であると言えます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.4. ユーザランドのトンネル カーネル外でのトンネリングの実装も、文字通りあまた存在しています。最も よく知られているのはもちろん PPP と PPTP でしょうが、他にもたくさんあり ます (商用のもの、安全なもの、IP を使わないものさえあります)。ただしこ れらはこの HOWTO の範囲から大きく外れています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 6. Cisco や 6bone との IPv6 トンネル By Marco Davids メンテナへのメモ: わたしの知る限り、この IPv6-IPv4 トンネリングは GRE トンネリングの定義 には当てはまりません。 GRE トンネルデバイスを用いて IPv6 を IPv4 にトン ネルすることはできます (GRE は「なんでも」IPv4 に通せます) が、ここで用 いているデバイス ("sit") は IPv6 のみを IPv4 に通しますので、ちょっと違 います。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.1. IPv6 トンネリング これは Linux のトンネリング機能の、また別の利用例です。これは IPv6 を初 期に導入した人 (あるいは「先駆者」) たちに人気があります。以降で記述さ れている「実践的」な説明は、もちろん IPv6 トンネリングを実現する唯一の 方法というわけではありません。しかしこれは、IPv6 機能を持った Cisco ル ータと Linux とをトンネルするのによく用いられる方法ですし、我々の経験に よれば、多くの人々が従っているやり方でもあります。これがあなたにも当て はまるであろうことは、まず間違いないと思いますね ;-) IPv6 アドレスに関して少々: IPv6 アドレスは、IPv4 のアドレスに比べると、非常に巨大 (128 ビット対 32 ビット) です。これによって、我々の望むものが提供されます。すなわち、た くさんの、たくさんの IP アドレス、正確に言えば 340,2822,6692,0938,4634,6337,4607,4317,6821,1465 個のアドレスです。これ 以外にも、IPv6 (あるいは IPng: IP Next Generation) では、インターネット のバックボーンルータにおける経路テーブルが小さくなり、機器の設定がより 単純になり、IP レベルでのセキュリティがより堅固になると期待されています 。 例: 2002:836b:9820:0000:0000:0000:836b:9886 IPv6 をすべて書き下すのは、かなりの重荷です。よって、生活を楽にするため の規則がいくつかあります。 ・ 先頭から続く 0 は使いません。IPv4 と同じです。 ・ 16 ビットごと (2 バイトごと) にコロンで区切ります。 ・ 0 がたくさん連続するところは :: のように書けます。ただしこれが行え るのはアドレスのうちの一箇所だけで、また 16 ビットの単位に対しての み使えます。 アドレス 2002:836b:9820:0000:0000:0000:836b:9886 は、 2002:836b:9820:: 836b:9886 のようにも書けます。後者のほうがちょっとは親しみやすいでしょ う。 別の例、アドレス 3ffe:0000:0000:0000:0000:0020:34A1:F32C は 3ffe::20: 34A1:F32C とも書けます。ずっと短いですね。 IPv6 は現在の IPv4 の後継者となるべく開発されました。しかし IPv6 はやや 新しい技術なので、まだ世界規模のネイティブな IPv6 ネットワークはありま せん。 6bone は、この移行を速やかにするために導入されました。 ネイティブな IPv6 ネットワーク同士を接続するのに、 IPv6 プロトコルを IPv4 パケットにカプセルし、既存の IPv4 インフラを通して、相手の IPv6 サ イトに送るのです。 ここでまさにトンネルを利用することになるわけです。 IPv6 を有効にするには、カーネルのサポートが必要です。これには多くの良質 な文書が存在していますが、結局のところはいくつかの段階に分割できます。 ・ 適切な glibc を備えた、最近の Linux ディストリビューションを入手し ます。 ・ 最新のカーネルソースを入手します。 準備が整ったら、IPv6 機能を持つカーネルをコンパイルします。 ・ /usr/src/linux に移動し、 "make menuconfig" と入力します。 ・ "Networking Options" を選びます。 ・ "The IPv6 protocol", "IPv6: enable EUI-64 token format", "IPv6: disable provider based addresses" を選択します。 ヒント: モジュールにはしないほうがいいでしょう。うまくいかないことがよ くあります。 別の言い方をすれば、IPv6 をカーネルの「組み込み」にしてください。続いて 通常のように設定を保存し、カーネルのコンパイルに移ってください。 ヒント: その前に、Makefile を編集して EXTRAVERSION = -x ; --> ; EXTRAVERSION = -x-IPv6 のようにしておくといいかもしれません。 カーネルのコンパイルとインストールにあたっては良い文書がたくさんありま すが、この文書はそこまでは書きません。もしこの段階でトラブルが生じたら 、自分で設定した Linux カーネルのコンパイルに関する文書を探して読んでく ださい。最初は /usr/src/linux/README ファイルから見てみるといいでしょう 。 これらがすんだら、新しいカーネルで再起動してください。ここで '/sbin/ ifconfig -a' とすると、'sit0' という新しいデバイスができていることに気 づくでしょう。 SIT は Simple Internet Transition を意味します。自分を誉 めてあげましょう。あなたは次世代の IP へと向かう、大きな一歩をいま踏み 出したのです。 では次の一歩に向かいましょう。自分のホスト (あるいは LAN 全体) を IPv6 機能を持った別のネットワークに接続します。この相手は、この目的のために 特別に設定された "6bone" となるでしょう。 あなたの IPv6 ネットワークが 3ffe:604:6:8::/64 で、 6bone (あるいは友 人) に接続することを意図しているとします。ここで /64 というサブネット表 記は、通常の IP アドレスの場合とちょうど同じような意味を持ちます。 あなたの持っている IPv4 アドレスは 145.100.24.181 で、 6bone ルータの IPv4 アドレスは 145.100.1.5 だとしましょう。 ┌──────────────────────────────────────────┐ │# ip tunnel add sixbone mode sit remote 145.100.1.5 [local 145.100.24.181 ttl 255] │ │# ip link set sixbone up │ │# ip addr add 3FFE:604:6:7::2/126 dev sixbone │ │# ip route add 3ffe::0/16 dev sixbone │ └──────────────────────────────────────────┘ 説明しましょう。最初の行では sixbone という名前のトンネルデバイスを生成 しています。これには mode sit (IPv6 を IPv4 にトンネリングする) を与え 、行き先 (remote) と発信元 (local) を指定しています。 TTL は最大の 255 にしています。 次に、このデバイスをアクティブにしています (up)。その後、我々のネットワ ークのアドレスを追加し、 3ffe::/15 (現在のところ 6bone の全体) への経路 をこのトンネルにしています。もしこれを実行したマシンが、あなたの IPv6 ゲートウェイなら、次の内容を実行するといいでしょう。 ┌──────────────────────────────────┐ │# echo 1 >/proc/sys/net/ipv6/conf/all/forwarding │ │# /usr/local/sbin/radvd │ └──────────────────────────────────┘ 二番目の行の radvd は (zebra のような) ルータ広報デーモンで、 IPv6 の自 動設定機能をサポートしています。必要なら、お好みの検索エンジンで調べて みてください。では次のようにしてチェックを行いましょう。 ┌──────────────────────────────────┐ │# /sbin/ip -f inet6 addr │ └──────────────────────────────────┘ radvd が IPv6 ゲートウェイで動作しており、 IPv6 機能を持った Linux が LAN 上のマシンで起動していれば、 IPv6 自動設定の恩恵を楽しめるはずです 。 ┌───────────────────────────────────────┐ │# /sbin/ip -f inet6 addr │ │1: lo: mtu 3924 qdisc noqueue inet6 ::1/128 scope host │ │ │ │3: eth0: mtu 1500 qdisc pfifo_fast qlen 100 │ │inet6 3ffe:604:6:8:5054:4cff:fe01:e3d6/64 scope global dynamic │ │valid_lft forever preferred_lft 604646sec inet6 fe80::5054:4cff:fe01:e3d6/10 │ │scope link │ └───────────────────────────────────────┘ では引き続き IPv6 アドレスに対して bind の設定を行いましょう。 A タイプ は IPv6 では AAAA となります。 in-addr.arpa は ip.int となります。この 話題に関しては、たくさんの情報が得られるでしょう。 IPv6 が使えるアプリケーションの数は増え続けています。 secure shell, telnet, inetd, Mozilla ブラウザ, webserver Apache, ほかにもたくさんあり ます。しかしそれは、この「経路」に関する文書の範疇を超えています :-) Cisco 側での設定は、次のような感じになります。 ┌──────────────────────────────────┐ │! │ │interface Tunnel1 │ │description IPv6 tunnel │ │no ip address │ │no ip directed-broadcast │ │ipv6 address 3FFE:604:6:7::1/126 │ │tunnel source Serial0 │ │tunnel destination 145.100.24.181 │ │tunnel mode ipv6ip │ │! │ │ipv6 route 3FFE:604:6:8::/64 Tunnel1 │ └──────────────────────────────────┘ もし思い通りになる Cisco がなければ、インターネットにたくさんある IPv6 トンネルブローカーのどれかを試してみましょう。きっと Cisco の設定を行い 、あなた用のトンネルを掘ってくれるでしょう。たいていの場合は、使いやす い web インターフェースがあるはずです。お好みの検索エンジンで、"ipv6 tunnel broker" をキーワードにして探してみてください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 7. IPSEC: インターネット越しの安全な IP 最近の Linux には 2 種類の IPSEC が存在します。 2.2 と 2.4 には FreeS/ WAN があり、これが最初のメジャーな実装でした。公式サイト と非公式サイト があり、活発 にメンテナンスされています。FreeS/WAN は様々な理由から、これまでメイン のカーネルにはマージされてきませんでした。最も良く言及されてきたのはア メリカ人開発者の「政治的」な理由で、暗号化に関して作業すると輸出規制が かかってしまうからでした。さらに FreeS/WAN は Linux カーネルにあまりう まく統合することができず、実際のマージにあたって、良い候補にはなりませ んでした。 また、コードの品質に関しても多くの 団体から懸念の声 があげられました。 FreeS/ WAN の設定に関しては、多くの文書 が利用できます 。 Linux 2.5.47 の頃には、カーネルのネイティブな IPSEC 実装が存在していま した。これは Alexey Kuznetsov と Dave Miller によるもので、 USAGI IPv6 グループの成果から刺激を受けたものでした。このマージにあたっては、James Morris の CryptoAPI もカーネルの一部となりました (これが実際の暗号化を 行います)。 この HOWTO では、2.5+ 版の IPSEC のみを扱います。 Linux 2.4 のユーザに は、現在のところは FreeS/WAN をお奨めしますが、この設定はネイティブの IPSEC のものとは異なります。関連ニュースですが、いまでは FreeS/WAN のユ ーザ空間のコードを、ネイティブな Linux IPSEC と動作させるためのパッチ があるそうです。 2.5.49 からは、IPSEC はパッチを必要せずに動作できるようになりました。 Note: ユーザ空間のツールはこちら から入手できます。複数のプログラムが使えますが、ここで リンクしたのは Racoon ベースのものです。 カーネルのコンパイルにあたっては、'PF_KEY', 'AH', 'ESP' その他、 CryptoAPI のすべてを有効にしてください! ┌──────────────────────────────────┐ │ Warning │ ├──────────────────────────────────┤ │この章の著者は、IPSEC に関してはど素人です! 必ずあるであろうミスをも│ │し見つけたら、どうか bert hubert にメールしてください │ │。 │ └──────────────────────────────────┘ まず最初に、安全な通信を 2 つのホストの間で行う方法を示します。この処理 の大部分は自動化できますが、ここでは手動で行ってみて、「箱の中で」なに が行われているのかについて、よく理解することにしましょう。 自動での鍵処理 (keying) にだけ興味があるのでしたら、次の節は飛ばしてく ださい。しかし手動での鍵処理に関する理解は、きっと有益です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.1. 入門編: 手動での鍵処理 IPSEC は複雑な題材です。たくさんの情報がネットワーク上にありますが、こ の HOWTO では動作できるようにすること、基本的な原理を説明することに集中 したいと考えています。すべての例は、上記のリンクにある Racoon をもとに しています。 Note: iptables のたいていの設定では、IPSEC パケットを落としてしまい ます! IPSEC を通すには、'iptables -A xxx -p 50 -j ACCEPT' と 'iptables -A xxx -p 51 -j ACCEPT' を使ってください。 IPSEC はセキュリティに優れたインターネットプロトコルです。ここでの「セ キュリティ」には、2 つの意味があります。暗号化および認証です。わかって ない人は、セキュリティすなわち暗号化であると考えがちですが、それだけで は十分でないことは簡単に示せます。通信は暗号化していても、通信先が自分 の期待している通りかどうかの保証が一切ないとしたらどうでしょうか? IPSEC は暗号化には 'Encapsulated Security Payload (ESP)' を、そしてリモ ートの通信先の認証には 'Authentication Header (AH)' を用います。両方を 使うようにも設定できますし、片方だけを使うこともできます。 ESP も AH も、security assosiation (SA) に依存しています。 SA は、発信 元・送信先・手続きからなります。例えば認証の SA は次のようになります。 ┌──────────────────────────────────────┐ │ add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456"; │ │ │ └──────────────────────────────────────┘ これは「10.0.0.11 から 10.0.0.216 に向かうトラフィックのうち AH を必要 とするものは、秘密鍵 123456789123456 を用いた HMAC-MD5 で署名できる」と いう意味です。この手続きには SPI ('Security Parameter Index') の id 15700 が付けられています。これについては後ほど詳しく説明します。 SA の 興味深い点は、対称的だという点にあります。通話を共有している両端は、ま ったく同じ SA を共有し、他端の鏡映にはなりません。しかしながら、ここで は 'autoreserve' ルールがないことに注意しましょう。つまりこの SA は、 10.0.0.11 から 10.0.0.216 への認証が可能であることだけを示しています。 双方向のトラフィックに対しては、2 つの SA が必要となります。 ESP SA の例を挙げましょう。 ┌──────────────────────────────────────┐ │add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "123456789012123456789012"; │ │ │ └──────────────────────────────────────┘ これは「10.0.0.11 から 10.0.0.216 に向かうトラフィックのうち暗号化を必 要とするものは、鍵 123456789012123456789012 を用いて暗号化できる」とい う意味です。この SPI id は '15701' です。 ここまでのところ、SA が可能な手続きを記述することを見てきましたが、しか し実は SA は、いつそれが必要になるかに関するポリシーを記述していません 。実際のところ、SPI id のみが異なるほとんど同じ SA を、好きなだけ置くこ ともできるのです (先ほども述べましたが SPI は Security Parameter Index の意味です)。実際の暗号化を行うには、ポリシーを記述しなければなりません 。このポリシーには、「可能なら ipsec を使う」とか「ipsec が使えなければ トラフィックを捨てる」とかいった内容を記述できます。 典型的かつ簡単な Security Policy (SP) は次のようなものです。 ┌──────────────────────────────────┐ │spdadd 10.0.0.216 10.0.0.11 any -P out ipsec │ │ esp/transport//require │ │ ah/transport//require; │ │ │ └──────────────────────────────────┘ ホスト 10.0.0.216 でこれが入力されると、 10.0.0.11 に向かうすべてのトラ フィックは暗号化されなければならず、かつ AH (authenticating header: 認 証ヘッダ) でラップされなければなりません。ただしここではどの SA を使う べきかは記述されていません。これを決定するのはカーネルに残された仕事な のです。 別の言い方をすれば、Security Policy は我々が「何を」必要とするかを指定 するのです。Security Association はそれを「どのように」必要とするかを記 述します。 送り出されるパケットは、SA SPI (「どのように」) でラベル付けされます。 カーネルはこれを使って暗号化と認証を行い、リモートからはそれに対応する 検証と復号化の手続きを調べることができます。 以降に示すのは、ホスト 10.0.0.216 から 10.0.0.11 に対して暗号化と認証を 用いて通信する場合の、非常に簡単な設定です。ただし最初の版では逆向きは 平文ですので、これは実際に用いてはなりません。 ホスト 10.0.0.216 では: ┌──────────────────────────────────────┐ │#!/sbin/setkey -f │ │add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456"; │ │add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012"; │ │ │ │spdadd 10.0.0.216 10.0.0.11 any -P out ipsec │ │ esp/transport//require │ │ ah/transport//require; │ │ │ └──────────────────────────────────────┘ ホスト 10.0.0.11 では同じ Security Association を用い、 Security Policy は指定しません。 ┌──────────────────────────────────────┐ │#!/sbin/setkey -f │ │add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456"; │ │add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012"; │ │ │ └──────────────────────────────────────┘ 以上の設定を行えば (これらのファイルは 'setkey' が /sbin にインストール されていれば実行できます)、 10.0.0.126 から行った 'ping 10.0.0.11' は、 tcpdump では次のように見えます。 ┌───────────────────────────────────────────────┐ │22:37:52 10.0.0.216 > 10.0.0.11: AH(spi=0x00005fb4,seq=0xa): ESP(spi=0x00005fb5,seq=0xa) (DF) │ │22:37:52 10.0.0.11 > 10.0.0.216: icmp: echo reply │ │ │ └───────────────────────────────────────────────┘ 10.0.0.11 からの ping の戻りが、実際に平文で見えることに注目。フォワー ドの ping は、もちろん tcpdump では見えません。しかし 10.0.0.11 に伝え ている AH と ESP の Security Parameter Index、すなわちパケットの認証確 認と複号化の方法、は見えています。 しかし、ここでいくつかの点に触れておく必要があります。上記の設定は、多 くの IPSEC の例で示されているものですが、非常に危険なのです。問題は次の 点にあります。上記におけるポリシーでは、 10.0.0.216 が 10.0.0.11 に向か うパケットをどう取り扱うかと、 10.0.0.11 がこれらのパケットをどう扱うか は定めていますが、しかし 10.0.0.11 が認証できない/複号化できないトラフ ィックをどのように捨てるかを示していないのです! これでは、誰かが途中でパケットを盗み、完全に複号化されたパケットで置き 換えると、 10.0.0.11 はそれを受け入れてしまいます。これを避けるには、到 着パケットに対する Security Policy が 10.0.0.11 に必要です。次のように します。 ┌──────────────────────────────────┐ │#!/sbin/setkey -f │ │spdadd 10.0.0.216 10.0.0.11 any -P IN ipsec │ │ esp/transport//require │ │ ah/transport//require; │ │ │ └──────────────────────────────────┘ これは 10.0.0.11 に対して、 10.0.0.216 から来たトラフィックに対して、正 しい ESP と AH が必要である、と指定しています。 さて、設定を完了するには、もちろん帰りのトラフィックにも同様な暗号化・ 認証が必要です。 10.0.0.216 における完全な設定は: ┌──────────────────────────────────────┐ │#!/sbin/setkey -f │ │flush; │ │spdflush; │ │ │ │# AH │ │add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456"; │ │add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456"; │ │ │ │# ESP │ │add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "123456789012123456789012"; │ │add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012"; │ │ │ │spdadd 10.0.0.216 10.0.0.11 any -P out ipsec │ │ esp/transport//require │ │ ah/transport//require; │ │ │ │spdadd 10.0.0.11 10.0.0.216 any -P in ipsec │ │ esp/transport//require │ │ ah/transport//require; │ │ │ │ │ └──────────────────────────────────────┘ そして 10.0.0.11 では: ┌──────────────────────────────────────┐ │#!/sbin/setkey -f │ │flush; │ │spdflush; │ │ │ │# AH │ │add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456"; │ │add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456"; │ │ │ │# ESP │ │add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "123456789012123456789012"; │ │add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012"; │ │ │ │ │ │spdadd 10.0.0.11 10.0.0.216 any -P out ipsec │ │ esp/transport//require │ │ ah/transport//require; │ │ │ │spdadd 10.0.0.216 10.0.0.11 any -P in ipsec │ │ esp/transport//require │ │ ah/transport//require; │ │ │ │ │ └──────────────────────────────────────┘ この例では、同一の鍵を両方向のトラフィックに用いています。しかしこれは 、必要条件ではまったくありません。 いま行った設定を確認するには、 setkey -D を実行して Security Association を表示するか、 setkey -DP を実行して設定されたポリシーを表 示してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2. 自動での鍵処理 一つ前の節では、暗号化は単純な共有鍵によって行われました。したがって、 安全を確保するには、この暗号化設定を信頼できる経路で転送しなければいけ ません。リモートのホストを telnet で設定しなければならないような場合に は、第三者にこの共有鍵が覗けてしまうので、その設定が安全であるとは言え ません。 さらに、鍵が共有されているということは、秘匿性がないということです。リ モートはこの鍵を用いてもたいした悪さはできませんが、しかし別々の相手と 通信する際には、必ずそれぞれ別々の鍵を使う必要があります。これには鍵の 数が非常にたくさん必要です。もし 10 組のパートナーを組むなら、最低 50 種類の鍵が必要です (訳注 10_C_2 なら 45 だと思いますが…)。 対称鍵の問題に加え、鍵の更新にも問題があります。第三者が十分な量のトラ フィックを盗聴すると、鍵をリバースエンジニアリングすることが不可能では なくなります。これは時々鍵を新しくすることによって避けられますが、これ は自動化が必要な処理です。 もうひとつの問題として、前述したような手動の鍵処理では、アルゴリズムと 鍵の長さを正確に定義しなければならず、リモートとの調整の手間がかかりま す。もっと幅広い鍵ポリシーを記述できるほうが望ましいです。例えば「こち らでは 3DES と Blowfish が使えて、鍵の長さには次のようなのが使えます」 のように。 これらの問題を解決するために、IPSEC では Internet Key Exchange が使えま す。これはアルゴリズムの詳細をネゴシエートして、ランダムに生成された鍵 を自動的に交換し、非対称暗号化技術を用いて鍵を転送します。 Linux 2.5 の IPSEC 実装では、KAME の 'racoon' IKE デーモンが動作します 。 11 月 9 日 (訳注: 2002 年) の段階では、Alexey の iptools 配布に入っ ている racoon は、コンパイルはできますが、2 つのファイルで #include を削除する必要があります。または、私もコンパイル済バイナ リ を提供しています。 Note: IKE は UDP ポート 500 にアクセスできる必要があります。 iptables でブロックしていないことを確認しましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2.1. 理論 先に説明した通り、自動鍵処理では多くの作業をしてくれます。特に、 Security Associations を動作の途中で生成してくれます。しかしポリシーの 設定は、希望するようには行ってくれません。 よって、IKE の恩恵を享受するには、ポリシーは設定して、 SA は設定しない 、ということになります。カーネルは SA の無い IPSEC ポリシーを発見すると それを IKE デーモンに知らせ、そしてデーモンがネゴシエーションの作業を開 始します。 繰り返しますが、Security Policy は我々が「何を」必要とするかの指定です 。 Security Association は、それを「どのように」実現するかを記述します 。自動鍵処理を用いれば、我々は何をしたいかだけを指定すればよくなるので す。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2.2. 例 Kame の racoon には非常にたくさんのオプションがありますが、そのほとんど は、デフォルトが非常に良く考えられた値になっています。よってほとんどは 変更する必要がありません。前述のように、オペレータは Security Policy を 定義すればよく、 Security Associations は必要ありません。ネゴシエーショ ンは IKE デーモンに任せます。 この例では、再び 10.0.0.11 と 10.0.0.216 が安全な通信を確立しようとして います。しかし今回は racoon の助けを借ります。簡単のために、この設定に は事前に共有した、嫌われ者の「共有暗号鍵」があるとしましょう。 X.509 認 証は別の節で議論します。 Section 7.2.3 をみてください。 ほぼデフォルトの設定でいきましょう。両方のホストで同じです。 ┌──────────────────────────────────┐ │path pre_shared_key "/usr/local/etc/racoon/psk.txt"; │ │ │ │remote anonymous │ │{ │ │ exchange_mode aggressive,main; │ │ doi ipsec_doi; │ │ situation identity_only; │ │ │ │ my_identifier address; │ │ │ │ lifetime time 2 min; # sec,min,hour │ │ initial_contact on; │ │ proposal_check obey; # obey, strict or claim │ │ │ │ proposal { │ │ encryption_algorithm 3des; │ │ hash_algorithm sha1; │ │ authentication_method pre_shared_key; │ │ dh_group 2 ; │ │ } │ │} │ │ │ │sainfo anonymous │ │{ │ │ pfs_group 1; │ │ lifetime time 2 min; │ │ encryption_algorithm 3des ; │ │ authentication_algorithm hmac_sha1; │ │ compression_algorithm deflate ; │ │} │ └──────────────────────────────────┘ たくさんの設定がありますね。デフォルトの設定にさらに近づければ、もうち ょっと減らせると思いますが。いくつか注目すべき点を。 anonymous (匿名) 設定を 2 つ行っていますが、これはすべてのリモートに適用され、以降の設定 が簡単になります。ホストごとの記述は、特に必要としなければ、書かなくて かまいません。 さらに、この設定では自分自身の指定は IP アドレスによって行う ('my_identifier address') ように指定しました。またこちらで行えるのは 3des, sha1 で、 psk.txt にある事前共有鍵を使う、と宣言しています。 psk.txt には 2 つのエントリを設定しておきます。これは各ホストで異なりま す。10.0.0.11 では: ┌──────────────────────────────────┐ │10.0.0.216 password2 │ └──────────────────────────────────┘ 10.0.0.216 では: ┌──────────────────────────────────┐ │10.0.0.11 password2 │ └──────────────────────────────────┘ これらのファイルは root の所有にし、モードを 0600 にすることを忘れずに 。さもないと racoon はこのファイルの内容を信用しません。これらのファイ ルはそれぞれの鏡映対称になっていますね。 これで望むポリシーを設定する準備ができました。これも簡単で、10.0.0.216 では: ┌──────────────────────────────────┐ │#!/sbin/setkey -f │ │flush; │ │spdflush; │ │ │ │spdadd 10.0.0.216 10.0.0.11 any -P out ipsec │ │ esp/transport//require; │ │ │ │spdadd 10.0.0.11 10.0.0.216 any -P in ipsec │ │ esp/transport//require; │ └──────────────────────────────────┘ そして 10.0.0.11 では: ┌──────────────────────────────────┐ │#!/sbin/setkey -f │ │flush; │ │spdflush; │ │ │ │spdadd 10.0.0.11 10.0.0.216 any -P out ipsec │ │ esp/transport//require; │ │ │ │spdadd 10.0.0.216 10.0.0.11 any -P in ipsec │ │ esp/transport//require; │ └──────────────────────────────────┘ ここも、これらのポリシーは鏡映になっています。 これで racoon を起動する準備ができました! 一度起動すれば、10.0.0.11 か ら 10.0.0.216 に telnet しようとしたとき、あるいは他の接続を試みたとき 、racoon はネゴシエーションを開始します: ┌──────────────────────────────────────────┐ │12:18:44: INFO: isakmp.c:1689:isakmp_post_acquire(): IPsec-SA │ │ request for 10.0.0.11 queued due to no phase1 found. │ │12:18:44: INFO: isakmp.c:794:isakmp_ph1begin_i(): initiate new │ │ phase 1 negotiation: 10.0.0.216[500]<=>10.0.0.11[500] │ │12:18:44: INFO: isakmp.c:799:isakmp_ph1begin_i(): begin Aggressive mode. │ │12:18:44: INFO: vendorid.c:128:check_vendorid(): received Vendor ID: │ │ KAME/racoon │ │12:18:44: NOTIFY: oakley.c:2037:oakley_skeyid(): couldn't find │ │ the proper pskey, try to get one by the peer's address. │ │12:18:44: INFO: isakmp.c:2417:log_ph1established(): ISAKMP-SA │ │ established 10.0.0.216[500]-10.0.0.11[500] spi:044d25dede78a4d1:ff01e5b4804f0680 │ │12:18:45: INFO: isakmp.c:938:isakmp_ph2begin_i(): initiate new phase 2 │ │ negotiation: 10.0.0.216[0]<=>10.0.0.11[0] │ │12:18:45: INFO: pfkey.c:1106:pk_recvupdate(): IPsec-SA established: │ │ ESP/Transport 10.0.0.11->10.0.0.216 spi=44556347(0x2a7e03b) │ │12:18:45: INFO: pfkey.c:1318:pk_recvadd(): IPsec-SA established: │ │ ESP/Transport 10.0.0.216->10.0.0.11 spi=15863890(0xf21052) │ └──────────────────────────────────────────┘ ここで setkey -D を実行して Security Associations を表示すると、実際に 存在していることがわかります: ┌──────────────────────────────────────┐ │10.0.0.216 10.0.0.11 │ │ esp mode=transport spi=224162611(0x0d5c7333) reqid=0(0x00000000) │ │ E: 3des-cbc 5d421c1b d33b2a9f 4e9055e3 857db9fc 211d9c95 ebaead04 │ │ A: hmac-sha1 c5537d66 f3c5d869 bd736ae2 08d22133 27f7aa99 │ │ seq=0x00000000 replay=4 flags=0x00000000 state=mature │ │ created: Nov 11 12:28:45 2002 current: Nov 11 12:29:16 2002 │ │ diff: 31(s) hard: 600(s) soft: 480(s) │ │ last: Nov 11 12:29:12 2002 hard: 0(s) soft: 0(s) │ │ current: 304(bytes) hard: 0(bytes) soft: 0(bytes) │ │ allocated: 3 hard: 0 soft: 0 │ │ sadb_seq=1 pid=17112 refcnt=0 │ │10.0.0.11 10.0.0.216 │ │ esp mode=transport spi=165123736(0x09d79698) reqid=0(0x00000000) │ │ E: 3des-cbc d7af8466 acd4f14c 872c5443 ec45a719 d4b3fde1 8d239d6a │ │ A: hmac-sha1 41ccc388 4568ac49 19e4e024 628e240c 141ffe2f │ │ seq=0x00000000 replay=4 flags=0x00000000 state=mature │ │ created: Nov 11 12:28:45 2002 current: Nov 11 12:29:16 2002 │ │ diff: 31(s) hard: 600(s) soft: 480(s) │ │ last: hard: 0(s) soft: 0(s) │ │ current: 231(bytes) hard: 0(bytes) soft: 0(bytes) │ │ allocated: 2 hard: 0 soft: 0 │ │ sadb_seq=0 pid=17112 refcnt=0 │ └──────────────────────────────────────┘ Security Policy は設定したとおりです: ┌──────────────────────────────────┐ │10.0.0.11[any] 10.0.0.216[any] tcp │ │ in ipsec │ │ esp/transport//require │ │ created:Nov 11 12:28:28 2002 lastused:Nov 11 12:29:12 2002 │ │ lifetime:0(s) validtime:0(s) │ │ spid=3616 seq=5 pid=17134 │ │ refcnt=3 │ │10.0.0.216[any] 10.0.0.11[any] tcp │ │ out ipsec │ │ esp/transport//require │ │ created:Nov 11 12:28:28 2002 lastused:Nov 11 12:28:44 2002 │ │ lifetime:0(s) validtime:0(s) │ │ spid=3609 seq=4 pid=17134 │ │ refcnt=3 │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2.2.1. 問題と既知の欠陥 これが動作しないときは、設定ファイルがすべて root の所有であるか、 root からしか読めないようになっているかを確認してください。 racoon をフォア グラウンドで起動するには '-F' を用います。コンパイル時に設定されたファ イルの代わりに特定の設定ファイルを読み込ませるには、'-f' を用います。詳 細についてこと細かに知りたければ、racoon.conf に 'log debug' という行を 追加してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2.3. X.509 証明書を用いた自動での鍵処理 先に説明した通り、共有鍵の利用には難しい点があります。共有は面倒で、そ れに一度共有されてしまうとそれはもう秘密ではないからです。ありがたいこ とに、非対称暗号技術を利用すればこの問題を解決できます。 IPSEC への各参加者が公開鍵と秘密鍵を作れば、通信の両者が公開鍵を発行し 、ポリシーを設定すれば、安全な通信を確立できます。 鍵の生成は割に簡単ですが、しかし多少の作業が必要です。以下では 'openssl' のツールをもとにした作業を説明します。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2.3.1. 自分のホストの X.509 証明書を作る OpenSSL には鍵に関する膨大なインフラストラクチャがあり、 CA による署名 を行うことも行わないこともできます。ここではインフラの大部分は回避し、 CA 無しで済ませる、お馴染みのいんちきセキュリティを適用しましょう。 まず私たちのホストの「証明請求書 (certificate request)」を発行します。 名前は 'laptop' とします。 ┌────────────────────────────────────┐ │$ openssl req -new -nodes -newkey rsa:1024 -sha1 -keyform PEM -keyout \ │ │ laptop.private -outform PEM -out request.pem │ └────────────────────────────────────┘ するといくつか質問されます。 ┌─────────────────────────────────────┐ │Country Name (2 letter code) [AU]:NL │ │State or Province Name (full name) [Some-State]:. │ │Locality Name (eg, city) []:Delft │ │Organization Name (eg, company) [Internet Widgits Pty Ltd]:Linux Advanced │ │Routing & Traffic Control │ │Organizational Unit Name (eg, section) []:laptop │ │Common Name (eg, YOUR name) []:bert hubert │ │Email Address []:ahu@ds9a.nl │ │ │ │Please enter the following 'extra' attributes │ │to be sent with your certificate request │ │A challenge password []: │ │An optional company name []: │ └─────────────────────────────────────┘ どのくらいまじめに答えるかは、あなたの判断です。セキュリティの必要に応 じて、ここにホスト名を入れることを望む人も、望まない人もいるでしょう。 この例では入れてあります。 ここでこの請求書に、「自己署名」します: ┌──────────────────────────────────┐ │$ openssl x509 -req -in request.pem -signkey laptop.private -out \ │ │ laptop.public │ │Signature ok │ │subject=/C=NL/L=Delft/O=Linux Advanced Routing & Traffic \ │ │ Control/OU=laptop/CN=bert hubert/Email=ahu@ds9a.nl │ │Getting Private key │ └──────────────────────────────────┘ これで 'request.pem' ファイルは捨ててかまいません。 この手続きを鍵の必要なホストのすべてで繰り返します。この '.public' 鍵は 問題なく配布できますが、'.private' 鍵の方は秘匿しておくように! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2.3.2. 設定と起動 ホストの公開鍵・秘密鍵ができたら、これらを使うよう racoon に伝えます。 先ほどの設定に戻りましょう。 2 つのホストを 10.0.0.11 ('upstairs') と 10.0.0.216 ('laptop') とします。 10.0.0.11 の racoon.conf ファイルに、以下を追加します: ┌──────────────────────────────────┐ │path certificate "/usr/local/etc/racoon/certs"; │ │ │ │remote 10.0.0.216 │ │{ │ │ exchange_mode aggressive,main; │ │ my_identifier asn1dn; │ │ peers_identifier asn1dn; │ │ │ │ certificate_type x509 "upstairs.public" "upstairs.private"; │ │ │ │ peers_certfile "laptop.public"; │ │ proposal { │ │ encryption_algorithm 3des; │ │ hash_algorithm sha1; │ │ authentication_method rsasig; │ │ dh_group 2 ; │ │ } │ │} │ └──────────────────────────────────┘ これは racoon に /usr/local/etc/racoon/certs/ にある証明を用いるよう指 示しています。さらに 10.0.0.216 用の設定項目も含まれています。 ここの 'asn1dn' の行は、ローカル・リモート両側の識別子としては、公開鍵 から取り出されたものを用いるよう、racoon に伝えています。これは前述の 'subject=/C=NL/L=Delft/O=Linux Advanced Routing & Traffic Control/OU= laptop/CN=bert hubert/Email=ahu@ds9a.nl' という出力です。 certificate_type の行は、ローカルの公開鍵・秘密鍵を設定しています。 peers_certfile 文は、リモートの接続先の公開鍵を laptop.public というフ ァイルから読み込むよう racoon に伝えています。 proposal ブロックは先ほどのものと変わっていません。ただし今回は authentication_method が rsasig になっており、認証に RSA 公開鍵/秘密鍵 を用いることが示してあります。 10.0.0.216 の設定への追加もだいたい同じで、ただしいつもの通り鏡映の関係 にあります。 ┌──────────────────────────────────┐ │path certificate "/usr/local/etc/racoon/certs"; │ │ │ │remote 10.0.0.11 │ │{ │ │ exchange_mode aggressive,main; │ │ my_identifier asn1dn; │ │ peers_identifier asn1dn; │ │ │ │ certificate_type x509 "laptop.public" "laptop.private"; │ │ │ │ peers_certfile "upstairs.public"; │ │ │ │ proposal { │ │ encryption_algorithm 3des; │ │ hash_algorithm sha1; │ │ authentication_method rsasig; │ │ dh_group 2 ; │ │ } │ │} │ └──────────────────────────────────┘ 両方のホストにこれらの文を追加したら、あとは鍵をその場所に置くだけです 。'upstairs' マシンでは、 /usr/local/etc/racoon/certs に upstairs.private, upstairs.public, laptop.public が必要です。このディレ クトリが root の保有になっており、モードが 0700 になっていることを確認 のこと。さもないと racoon は読み込みを行いません! マシン 'laptop' では、 /usr/local/etc/racoon/certs に laptop.private, laptop.public, upstairs.public が必要です。要するに、各ホストでは自分の 公開鍵と秘密鍵、さらにリモートの公開鍵を必要とします。 Security Policy が設定されていることを確認します (Section 7.2.2 の 'spdadd' 行を実行)。続いて racoon を起動すれば、すべて動作するはずです 。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2.3.3. トンネルを安全に設定するには リモートの接続先との間で安全な通信を行うには、公開鍵を交換しなければな りません。公開鍵そのものは秘匿しておかなくて良いのですが、一方、その鍵 が本当に改竄されていない鍵である保証が非常に大切です。要するに、「途中 の奴」がいないことが確かでなければなりません。 この作業を簡単にするため、OpenSSL には 'digest' コマンドがあります。 ┌──────────────────────────────────┐ │$ openssl dgst upstairs.public │ │MD5(upstairs.public)= 78a3bddafb4d681c1ca8ed4d23da4ff1 │ └──────────────────────────────────┘ これで、リモートのパートナーとの間で、ダイジェストが同じであることを確 認するだけですみます。これを行うには実際に会うか、あるいは電話をすれば いいでしょう。リモート側でのこの番号が、実は鍵と同じメールで送られて来 た、というのではダメですよ! これを行うもう一つの方法は、CA を行っている信頼できる第三者 (Trusted Third Party) を用いることです。この CA に、(先ほどは自分たちで行った) 署名をしてもらうのです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.3. IPSEC トンネル これまでのところは、通信の両端が直接 IPSEC を理解している、いわゆる IPSEC の「転送 (transport)」モードのみを見てきました。しかし通常は IPSEC を理解するマシンばかりではないので、ルータのみに IPSEC を喋らせ、 その後ろにあるネットワークの作業を肩代わりさせることが必要となります。 こちらは「トンネルモード」と呼ばれています。 この設定はとても簡単です。10.0.0.216 から 130.161.0.0/16 へ向かうトラフ ィックをすべて 10.0.0.11 経由でトンネルさせるには、以下を 10.0.0.216 で 実行するだけです。 ┌──────────────────────────────────┐ │#!/sbin/setkey -f │ │flush; │ │spdflush; │ │ │ │add 10.0.0.216 10.0.0.11 esp 34501 │ │ -m tunnel │ │ -E 3des-cbc "123456789012123456789012"; │ │ │ │spdadd 10.0.0.0/24 130.161.0.0/16 any -P out ipsec │ │ esp/tunnel/10.0.0.216-10.0.0.11/require; │ └──────────────────────────────────┘ ここでは '-m tunnel' に注目してください。これが最重要の部分です。これは 最初に ESP 暗号化された SA をトンネルの両端で設定します。 次に実際のトンネルを設定します。これはカーネルに対し、 10.0.0.24 から 130.161.0.0 に向かう経路のトラフィックを暗号化するよう指定しています。 さらに、続いてこのトラフィックは 10.0.0.11 に出荷されるのです。 10.0.0.11 でも少々設定が必要です。 ┌──────────────────────────────────┐ │#!/sbin/setkey -f │ │flush; │ │spdflush; │ │ │ │add 10.0.0.216 10.0.0.11 esp 34501 │ │ -m tunnel │ │ -E 3des-cbc "123456789012123456789012"; │ │ │ │spdadd 10.0.0.0/24 130.161.0.0/16 any -P in ipsec │ │ esp/tunnel/10.0.0.216-10.0.0.11/require; │ └──────────────────────────────────┘ これは、'-P out' が '-P in' に変わったことを除けば、まったく同じもので す。これまでの例と同様、ここでの例も一方向のトラフィックのみを設定しま した。トンネルの反対方向を完成させるのは、読者の課題としておきます。 この設定は別名「代理 ESP」とも呼ばれています。こちらのほうが少々わかり やすいでしょうか。 Note: IPSEC トンネルには、カーネルで IP Forwarding を有効にしておく 必要があります! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.4. 他の IPSEC ソフトウェア Thomas Walpuski は、OpenBSD の isakpmd を Linux 2.5 IPSEC で動作するよ うにしたパッチを書いたと報告してくれました。さらに、現在は isakpmd のメ インの CVS リポジトリにこのコードが入っています。彼のページ にも記述がありま す。 isakpmd は前述の racoon とはかなり違いますが、こちらを好む人もたくさん います。こちら から入手できます。より詳しくは、 OpenBSD CVS を見てください。 Thomas は CVS やパッチが面倒な人向けに tarball も用意し てくれています。 さらに、FreeS/WAN のユーザ空間のツールを Linux 2.5 のネイティブな IPSEC で使えるようにするパッチもあります。こちら にあります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.5. 他のシステムとの IPSEC の相互運用 FIXME: 書いてください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.5.1. Windows Andreas Jellinghaus のレポートによれば: 「win2k: 動作します。ip アドレスと事前共有鍵を組み合わせた認証が使えます (windows は fqdn や userfqdn 文字列はサポートしていないと思います)。証 明書もつかえると思いますが、試していません」 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 8. マルチキャストのルーティング FIXME: 編集者がいません! Multicast-HOWTO は (あえて言えば) 古代のもので、そのため各所に不正確な 部分や間違いがあります。 どのようなものであれ、マルチキャストルーティングを行う前には、カーネル を設定して行いたいルーティングのタイプをサポートさせる必要があります。 これは逆に、どのようなマルチキャストルーティングを使うのかは、自分で決 めなければならない、ということでもあります。「通常」のタイプは、基本的 に 4 つあります。 DVMRP (RIP ユニキャストプロトコルのマルチキャスト版) 、 MOSPF (同じく OSPF のマルチキャスト版)、 PIM-SM ("Protocol Independent Multicasting - Sparse Mode": マルチキャストグループのユーザ が集中しておらず、分散していると仮定しています)、 PIM-DM (上記の "Dense Mode" で、同じマルチキャストグループに属するユーザは、ある程度まとまっ ていると仮定しています)、です。 Linux カーネルには、これらの選択肢は登場しません。プロトコルそのものは 、Zebra, mrouted, pimd などのルーティングアプリケーションが扱うからです 。しかしそれでも、どれを用いるかはしっかり決めておかないと、カーネルで 正しいオプションを選ぶことができません。 すべてのマルチキャストルーティングにおいて、まず何はおいても "multicasting" と "multicast routing" は必ず有効にしておかなければなり ません。 DVMRP と MOSPF では、これだけで OK です。 PIM を用いるつもりな ら、接続しているネットワークにおける PIM のバージョンに応じて、PIMv1 か PIMv2 かのどちらかも有効にしておかなければなりません。 前述の選択を行ったら、新しい Linux カーネルをコンパイルします。するとブ ート時に IP プロトコルがリストされ、ここでは IGMP が含まれているはずで す。これはマルチキャストグループを管理するプロトコルです。これを書いて いる現在では、Linux は IGMP のバージョン 1 と 2 だけしかサポートしてい ませんが、バージョン 3 も既に存在し、文書化されています。しかしバージョ ン 3 はかなり新しく、 IGMPv3 の拡張機能はそれほど利用されていないので、 これはあまり大きな問題にはなりません。 IGMP はグループを扱うので、グル ープ全体のうち、最もシンプルなバージョンの IGMP における機能だけが用い られることになります。ほとんどの場合これは IGMPv2 になるでしょうが、 IGMPv1 になることもまだあるでしょう。 ここまではいいですね。マルチキャスティングを有効にできました。さて、ル ーティングを開始するにはマルチキャストで実際に何らかの作業を行うよう Linux カーネルに伝える必要があります。これは、マルチキャスト仮想ネット ワークを経路テーブルに追加することを意味します。 ┌──────────────────────────────────┐ │ip route add 224.0.0.0/4 dev eth0 │ └──────────────────────────────────┘ (当然ですが、ここではマルチキャスティングを eth0 で行っているとしていま す! ほかのデバイスを使いたかったらここを置き換えてください。) では、パケットをフォワードするよう指示しましょう… ┌──────────────────────────────────┐ │echo 1 > /proc/sys/net/ipv4/ip_forward │ └──────────────────────────────────┘ ここまでのところで、これがいったい何を行うのか、不思議に思っているかも しれません。そこで、接続テストとして、デフォルトのグループ 224.0.0.1 に ping して、誰かが生きているか見てみることにしましょう。マルチキャストを 有効にした LAN のマシンはすべて応答するはずですが、ほかには何も起こりま せん。どのマシンも 224.0.0.1 という IP アドレスは持っていません。なんと 不思議なことでしょう! :) これはグループアドレス (購読者への「ブロードキ ャスト」)であって、グループのすべてのマシンは、グループアドレスではなく 、すべて自分自身のアドレスで応答するのです。 ┌──────────────────────────────────┐ │ping -c 2 224.0.0.1 │ └──────────────────────────────────┘ これで、実際のマルチキャストルーティングを行う準備ができました。さて、2 つのネットワークがあり、この間でルーティングを行いたいとしましょう。 (続く!) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 9. キューイング規則とバンド幅管理 さて、わたしがこれに気づいたときには、ほんとうにたまげました。 Linux 2.2/2.4 には、ハイエンドの専用システムに比肩するほどの、様々なバンド幅 管理機能が含まれているのです。 Linux はフレームリレーや ATM よりもずっと進んだ処理を行えます。 混乱を避けるため、tc におけるバンド幅の指定では、以下の規則を用いること にします。 mbps = 1024 kbps = 1024 * 1024 bps => byte/s mbit = 1024 kbit => kilo bit/s. mb = 1024 kb = 1024 * 1024 b => byte mbit = 1024 kbit => kilo bit. 内部では、数値は bps および b で保持されています。 しかし tc が速度を表示するときには、以下を用います。 1Mbit = 1024 Kbit = 1024 * 1024 bps => byte/s ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.1. キューおよびキューイング規則の説明 キューイングを用いると、データをどのように「送る」かを決定できます。大 切なのは、我々が変更できるのは送信データだけ、という認識です。 インターネットの動作原理からして、他の人が送ってくる内容を直接制御する ことはできません。これはあなたの自宅の (物理的な!) 郵便箱にちょっと似て います。世の中を変更して、送られてくる郵便物の量に影響を与えることはで きません (全員に連絡を取れば別ですが)。 しかしながら、Internet の大部分は TCP/IP で動作しているので、このいくつ かの機能が利用できます。 TCP/IP は 2 つのホスト間のネットワークの容量を 知る方法を持たないので、単に開始時にデータをだんだん速く送ろうとし ('slow start')、送信できる限界を越えてパケットがロストし始めたときに、 減速します。実際はこの説明よりもう少し賢いのですが、それらについてはま た後ほど触れます。 これはつまり、到着する郵便の半分を読まずに捨て、それ以上送ってこないよ う祈るのと同じです。インターネットではうまくいく、というところが違いま すが :-) 自分のネットワークのあるホストに対して、あまり高速にダウンロードできな いようにしたい場合を考えましょう (そのネットワークのルータもあるとしま す)。この場合はルータの「内側」のインターフェース、すなわちコンピュータ にデータを送るところ、で帯域制限を行います。 接続のボトルネックの部分で制御を行うようにすることも重要です。 NIC が 100Mbit で、ルータの接続が 256kbit だったら、ルータが処理できる以上のデ ータは送らないようにしなければなりません。さもないと、接続を制御しバン ド幅を絞るのはそのルータ、ということになります。我々は、言うなれば「自 前のキュー」が必要なわけで、それは連鎖のなかで最も遅い回線に存在しなけ ればいけません。ありがたいことに、これは簡単に可能となります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2. シンプルな、クラスレスのキューイング規則 既に述べたように、キューイング規則を用いると、データの送信のされ方を変 更できます。クラスのない (クラスレスな) キューイング規則とは、おおむね データを受信し、それを再スケジュール・遅延・破棄するようなものです。 これを用いると、再分割 (subdivision) を用いずに、インターフェース全体の トラフィックを絞れます。クラスのある (クラスフルな) 「qdisc を含む qdisc」に進む前に、この部分を理解しておくことはとても大切です。 これまでのところ、最も広く用いられている規則は pfifo_fast qdisc です (これがデフォルトです)。これは、なぜこれらの先進的な機能の信頼性がこれ ほど高いのかも説明してくれます。これらは「ひとつ余分のキュー」以上のも のではないのです。 これらのキューには、それぞれ長所と短所があります。これらのすべてが、完 全にテストされているというわけではありません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2.1. pfifo_fast このキューは、名前からわかるとおり、「先入れ先出し (First In, First Out)」で、つまりどの受信パケットも特殊な扱いは (少なくともそれほどは) されません。このキューには 3 つのいわゆる「バンド」があります。各バンド には FIFO のルールが適用されます。しかし、バンド 0 に待ちパケットがある と、バンド 1 は処理されません。バンド 1 とバンド 2 も同じ関係にあります 。 カーネルは、パケットのいわゆる「サービスのタイプ (Type of Service)」フ ラグを尊重し、「最小遅延 (minimum delay)」パケットをバンド 0 に入れるよ う計らいます。 このクラスレスかつシンプルな qdisc を、クラスフルな PRIO qdisc と混同し ないこと。両者は同じように動作しますが、pfifo_fast はクラスレスですから 、 tc コマンドで他の qdisc を追加することはできません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2.1.1. パラメータと使い方 pfifo_fast qdisc は出来合いのデフォルトですから、ユーザによる設定はでき ません。ここではデフォルトでどのように設定されているかを示します。 priomap パケットの優先具合が、カーネルによって、どのバンドに対応付けられる かを決めます。対応付けはパケットの TOS に従って決まります。TOS は以 下のようなものです。 ┌────────────────────────────┐ │ 0 1 2 3 4 5 6 7 │ │+-----+-----+-----+-----+-----+-----+-----+-----+ │ │| | | | │ │| PRECEDENCE | TOS | MBZ | │ │| | | | │ │+-----+-----+-----+-----+-----+-----+-----+-----+ │ └────────────────────────────┘ TOS の 4 ビット (「TOS フィールド」) は次のように定義されています。 ┌────────────────────────────┐ │Binary Decimcal Meaning │ │----------------------------------------- │ │1000 8 Minimize delay (md) │ │0100 4 Maximize throughput (mt) │ │0010 2 Maximize reliability (mr) │ │0001 1 Minimize monetary cost (mmc) │ │0000 0 Normal Service │ └────────────────────────────┘ この 4 ビットの右にはもう 1 ビットありますから、 TOS フィールドの実 際の値はこれらの TOS ビットから決まる値の 2 倍になります。 tcpdump -v -v としたときは、この 4 ビット列の値ではなく、 TOS フィールド全 体の値を表示します。これは、次の表の第一列にある値になります。 ┌───────────────────────────────┐ │TOS Bits Means Linux Priority Band │ │------------------------------------------------------------ │ │0x0 0 Normal Service 0 Best Effort 1 │ │0x2 1 Minimize Monetary Cost 1 Filler 2 │ │0x4 2 Maximize Reliability 0 Best Effort 1 │ │0x6 3 mmc+mr 0 Best Effort 1 │ │0x8 4 Maximize Throughput 2 Bulk 2 │ │0xa 5 mmc+mt 2 Bulk 2 │ │0xc 6 mr+mt 2 Bulk 2 │ │0xe 7 mmc+mr+mt 2 Bulk 2 │ │0x10 8 Minimize Delay 6 Interactive 0 │ │0x12 9 mmc+md 6 Interactive 0 │ │0x14 10 mr+md 6 Interactive 0 │ │0x16 11 mmc+mr+md 6 Interactive 0 │ │0x18 12 mt+md 4 Int. Bulk 1 │ │0x1a 13 mmc+mt+md 4 Int. Bulk 1 │ │0x1c 14 mr+mt+md 4 Int. Bulk 1 │ │0x1e 15 mmc+mr+mt+md 4 Int. Bulk 1 │ └───────────────────────────────┘ たくさん数字が並んでいますね。第二列は、これらに対応する 4 つの TOS ビット列の値です。続いてそれらの意味する内容が示されています。例え ば 15 は、このパケットは Minimal Monetary Cost, Maximum Reliability, Maximum Throughput, Minimum Delay のすべてを期待してい るパケットを意味しています。私はこれを「強欲なパケット (Dutch Packet)」と呼びたいと思います。 [訳注: 著者の Hubert さんもオランダ 人 :-)] 第 4 列は Linux カーネルによる TOS ビット列の解釈です。どの優先度に マップされるかを示しています。 最後の列は、デフォルトの優先度マップ (priomap) の結果です。コマンド ラインでは、デフォルトの優先度マップは次のようになります。 ┌────────────────────────────┐ │1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 │ └────────────────────────────┘ つまり例えば優先度 4 のパケットは、バンド番号 1 へとマップされます (訳注: 優先度は 0 から始まることに注意)。優先度マップでは高い優先度 (> 7) をリストすることもできます。これらは TOS マッピングには対応し ていませんが、他の方法によって設定できます。 この表は RFC1349 (詳細は直接こちらを) から取ったもので、各アプリケ ーションがどのように TOS ビットを設定すると良いかについて示してあり ます。 ┌─────────────────────────────────┐ │TELNET 1000 (minimize delay) │ │FTP │ │ Control 1000 (minimize delay) │ │ Data 0100 (maximize throughput) │ │ │ │TFTP 1000 (minimize delay) │ │ │ │SMTP │ │ Command phase 1000 (minimize delay) │ │ DATA phase 0100 (maximize throughput) │ │ │ │Domain Name Service │ │ UDP Query 1000 (minimize delay) │ │ TCP Query 0000 │ │ Zone Transfer 0100 (maximize throughput) │ │ │ │NNTP 0001 (minimize monetary cost) │ │ │ │ICMP │ │ Errors 0000 │ │ Requests 0000 (mostly) │ │ Responses (mostly) │ └─────────────────────────────────┘ txqueuelen このキューの長さはインターフェースの設定から決まります。インターフ ェース設定は ifconfig や ip で閲覧・設定できます。キューの長さを 10 にするには、 "ifconfig eth0 txqueuelen 10" と実行します。 このパラメータは tc では設定できません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2.2. トークンバケツフィルタ (Token Bucket Filter) トークンバケツフィルタ (Token Bucket Filter: TBF) は単純な qdisc で、管 理者が設定した速度を越えない範囲で到着パケットを通します。ただし短い時 間の突発的なものなら、この値を越えることを許す可能性があります。 TBF は非常に正確で、ネットワークとプロセッサへの負荷も軽いです。単純に インターフェースの速度を落としたいと思ったときには、まず最初にこの利用 を考えてみるべきです。 TBF の実装はバッファ (バケツ) であり、これは定期的に仮想的な情報の断片 (トークンと呼ばれます) によって、一定の割合 (トークン速度) で満たされて いきます。バケツの最も重要なパラメータはサイズで、保持できるトークンの 数を意味します。 バケツに入った各トークンは、それぞれひとつの受信データパケットをデータ キューから拾い、そしてバケツからは削除されます。この 2 つの流れ (トーク ンとデータ) からなるアルゴリズムには、 3 つのシナリオが考えられます。 ・ トークンと「同じ」割合で、TBF にデータが到着する。この場合各受信パ ケットは、それぞれ対応するトークンがあるので、遅延することなしにキ ューを通過する。 ・ トークンの速度よりも「遅い」割合で、TBF にデータが到着する。キュー に入った受信データパケットの出力に応じて削除されるトークンは一部分 のみなので、トークンはバケツサイズ一杯にまで溜まっていく。使われな かったトークンは、突発的なデータのバーストが起こったような場合に利 用でき、トークンの標準流入速度を越えたデータが送信できる。 ・ トークンの速度よりも「大きい」割合で、TBF にデータが到着する。この 場合、バケツのトークンはすぐに空になってしまい、 TBF はしばらくの間 入力を絞る。これは「過負荷状態 (overlimit situation)」と呼ばれる。 パケットがそのまま入り続けてくる場合には、パケットは破棄され始める 。 後ろの 2 シナリオがとても重要です。これらは、このフィルタを通過するデー タのバンド幅を、管理者が制御できることを意味しているからです。 トークンが溜まっていくと、制限を越えたデータバーストも、短いものならロ スなしに通過できますが、過負荷状態が続くとパケットはだんだん遅延してい き、ついには破棄されます。 ただし実際の実装では、トークンが対応しているのはバイトであり、パケット ではありません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2.2.1. パラメータと使い方 ほとんど変更の必要はないでしょうが、 TBF にも調整つまみはついています。 まず、つねに指定できるパラメータです: limit または latency limit はトークン待ち状態でキューに入れるバイト数の制限値です。これ は latency パラメータ (パケットが TBF に留まれる時間の最大値) を設 定することによっても指定できます。こちらを計算する際には、バケツの サイズ、トークンの追加速度、および (指定されていれば) ピーク速度を 考慮します。 burst/buffer/maxburst バイト単位のバケツのサイズです。これはある瞬間に利用できるトークン の最大値 (バイト数) です。一般に、絞りたい通信速度が大きい場合には 、大きなバッファがいります。 Intel で 10mbit/s を使う場合、この設定 速度にするには少なくとも 10kbyte のバッファが必要です。 バッファが小さすぎると、時間単位あたりに到着するトークンだけでバケ ツが溢れてしまうので、パケットが破棄されてしまうかもしれません。 mpu サイズが 0 のパケットも、使うバンド幅は 0 ではありません。イーサネ ットでは、64 バイト以下のパケットはありません。最小パケット単位 (Minimum Packet Unit) は、ひとつのパケットが用いるトークンの最小値 を定めます。 rate スピードつまみです。制限については上記を参照のこと。 バケツにトークンが入っていて、これを空にすることが許されるとき、デフォ ルトではこの作業は無限の速度で行われます。これがまずい場合には、以下の パラメータを使って下さい。 peakrate トークンが使える状態でパケットが到着すると、デフォルトではそのパケ ットはすぐさま、いうならば「光速」で送り出されます。これは、特に大 きなバケツを使っている場合には、望ましいことではないかもしれません 。 peakrate は、バケツを空ける際に許される速さを決めるために用います。 すべてが本に書いてある通りなら、この動作は「パケットを送って、十分 長い間待って、そして次のパケットを送る」ことによって実現されていま す。待ち時間は、ちょうど peakrate で送られるように計算してあります 。 しかしながら、Unix でのデフォルトの時間単位は 10ms なので、パケット サイズの平均を 10.000 ビットとすると、 peakrate は 1mbit/s にしかで きません! mtu/minburst 1mbit/s の peakrate は、これが通常の速度より遅ければ、あまり便利と は言えません。時間単位ごとに送れるパケットの数を増やせば、より peakrate は大きくできます。これは実質的には 2 番目のバケツを作るの と同じことです。 この 2 番目のバケツは、デフォルトでは 1 つのパケットでしかなく、つ まり実際にはバケツではありません。 peakrate の最大許容値を計算するには、 mtu の設定値に 100 (あるいは 正確には HZ。これは Intel では 100, Alpha では 1024) をかけてくださ い。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2.2.2. 設定例 シンプルですが、とっても便利な設定を示します。 ┌───────────────────────────────────┐ │# tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1540 │ └───────────────────────────────────┘ はい、なぜ便利なのか説明しましょう。 DSL モデムやケーブルモデムのように 、キューの大きなネットワークデバイスがあるとして、そのデバイスには (イ ーサネットインターフェースのような) 高速なデバイスで通信しているとしま しょう。するとアップロードの際に、対話通信が完全にダメになってしまいま す。 この理由は、アップロードがモデムのキューを一杯にしてしまうからです。こ のキューはおそらく非常に大きく、実際これによってアップロードのスループ ットが向上しています。しかしこれは望ましいことではないでしょう。キュー をあまり大きくせずにおいて、データの送信時にも他のことができるよう、対 話通信を可能としておきたいことが多いはずです。 上記の実行例では、モデムのキューが必要ない程度にまで、送信速度を落とし ています。これでキューは Linux 上にあり、これに対して制限を課すことがで きます。 220kbit は、あなたの環境の「実際の」速度 (マイナス数パーセント) にして ください。手持ちのモデムが非常に高速なら、'burst' を少々増やしてくださ い。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2.3. 確率的不偏キューイング (Stochastic Fairness Queueing) 確率的不偏キューイング (Stochastic Fairness Queueing: SFQ) は、不偏キュ ーイングのアルゴリズムをシンプルに実装したものです。これは他のものほど 正確ではありませんが、あまり計算量が必要なく、それでいて非常に公平です 。 SFQ でのキーワードは「会話 (または流れ)」で、これは TCP セッションや UDP ストリームとほとんど同じ意味です。トラフィックはかなり多い数の FIFO キューに分割され、各キューがそれぞれの会話に対応します。そしてトラフィ ックはラウンドロビン的に送られます。すなわちデータを送る機会が各セッシ ョンに順番に与えられます。 これは非常に公平な動作につながり、ある特定の会話が残りをかき消してしま うようなことはなくなります。 SFQ には「確率的」と名前がついていますが、 これは SFQ が実際には各セッションごとにキューを割り当てるのではなく、ト ラフィックをある限られた数のキューに、ハッシュアルゴリズムを使って分割 するからです。 このハッシュのため、複数のセッションが同じバケツに入ることもあり得ます 。各セッションがパケットを送信する機会を握っているのはこのバケツなので 、実効的な速度は半分になってしまいます。この状況が目立つのを防ぐために 、 SFQ はハッシュアルゴリズムを極めて頻繁に変更し、あるセッション同士の 衝突が、短い時間しか続かないようにしています。 これは重要なポイントですが、 SFQ が有効なのは、実際の出力インターフェー スが、本当に一杯一杯になっている場合に限られます。そうでなければ、あな たの linux マシンのキューはほぼ空っぽで、よって何の効果も持ちません。 SFQ を他の qdisc と組み合わせ、「両方にいい顔をしたい」状況を実現するた めの方法については後ほど議論します。 特に、ケーブルモデムや DSL ルータに向かうイーサネットに対して SFQ を設 定するのは、他の制限を行わないならば的外れです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2.3.1. パラメータと使い方 SFQ はほとんどチューニングの必要がありません。 perturb ハッシュコードの再設定を行う時間間隔。設定しないとハッシュは変更さ れませんが、これはお勧めできません。たぶん 10 秒で良いでしょう。 quantum 次のキューに順番を回すまえに、あるストリームのキューから吐き出すデ ータのバイト量。デフォルトは最も大きなサイズ (MTU サイズ) のパケッ ト 1 つ分です。 MTU よりも小さな値にはしないように! limit この SFQ がキューイングするパケットの総数 (これを越えるとパケットを 落とし始めます)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2.3.2. 設定例 接続速度と実効速度とが同じデバイス (電話線のモデム) に対しては、この設 定を行うと公平性の向上が期待できます: ┌────────────────────────────────────────┐ │# tc qdisc add dev ppp0 root sfq perturb 10 │ │# tc -s -d qdisc ls │ │qdisc sfq 800c: dev ppp0 quantum 1514b limit 128p flows 128/1024 perturb 10sec │ │ Sent 4812 bytes 62 pkts (dropped 0, overlimits 0) │ └────────────────────────────────────────┘ 800c: というのは、自動的に割り当てられるハンドル番号です。 limit は、こ のキューでは 128 個のパケットが待機できることを意味しています。 1024 個 のハッシュバケツが割り振りに利用でき、そのうち同時にアクティブになれる のは 128 個です (これ以上の数はキューに入れません!)。 10 秒ごとにハッシ ュが再設定されます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.3. いつどんなキューを使うべきか まとめると、これらは単純なキューで、実際にパケットを並び換えたり、遅延 ・破棄したりしてトラフィックを管理するものでした。 以下に、どのキューを用いるべきかの選択にあたって助けとなるコツを示しま す。ここでは、 Chapter 14 の章で解説されている qdisc も含まれています。 ・ 単に送信されるトラフィックを遅くしたければ、トークンバケツフィルタ を用いましょう。バケツの大きさを調節すれば、非常に大きなバンド幅に 対してまで使えます。 ・ 現在の接続がほぼ一杯で、特定のセッションが出力バンド幅を支配しない ようにしたい場合は、確率的不偏キューイングを用いましょう。 ・ バックボーンが大きく、目的がはっきりしているなら、ランダム初期検知 (高度なフィルタの章を見てください) を検討しましょう。 ・ フォワードしない到着トラフィックを「絞り」たければ、入口監視制限 (Ingress Policer) を使いましょう。なお、到着トラフィックを絞る場合 には「監視制限 (policing)」という言葉が使われます。 ・ パケットをフォワードする場合は、データのフォワード先インターフェー スで TBF を使いましょう。ただし複数のインターフェースに向かいうるト ラフィックを絞りたい (その場合共通要素は到着インターフェースだけに なります) 場合は別で、その際には入口監視制限を使いましょう。 ・ 帯域制限は行いたくないが、インターフェースの負荷が高いか (キューが 必要かどうか) を監視したい場合は、pfifo キュー (pfifo_fast ではあり ません) を使いましょう。これには内部のバンドはありませんが、パケッ トのサイズを記録します。 ・ 最後に - 「社会的帯域制限」も行えるはずです。つねに望みの状態を実現 できる技術が利用できるとは限りません。またユーザは技術的な制限を敵 視するものです。親切な言葉は、バンド幅の適切な分配にも役立つでしょ う! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.4. 用語説明 より複雑な設定を正しく理解するには、いくつかの概念を先に説明しておく必 要があるでしょう。このテーマは込み入っていて、また比較的新しいため、実 は同じ概念に異なる言葉が用いられることが多々あります。 以下では An Informal Management Model for Diffserv Routers (draft-ietf-diffserv-model-06.txt) を少々参考にしています。こちら にあります。 用語の正確な定義を知りたい場合は、この原本も読んでください。 キューイング規則 (Queueing Discipline: qdisc) 入力側 (ingress: 入口)、出力側 (egress: 出口) いずれかのデバイスの キューを管理するアルゴリズムのこと。 root qdisc デバイスに直接割り当てられた qdisc のこと。 クラスレスな qdisc (classless qdisc) 設定可能な内部の再分配ルールが存在しない qdisc のこと。 クラスフルな qdisc (classful qdisc) クラスフルな qdisc には複数のクラスが含まれます。これらのクラスのい くつかはさらに qdisc を保持し、それがまたクラスフルであることもあり ます (そうでないこともあります)。厳密な定義では、pfifo_fast はクラ スフルです。なぜなら 3 つのバンドが含まれ、これらは実はクラスだから です。しかし、ユーザ設定という面から見ると、これらのクラスは tc ツ ールで変更できないのでクラスレスです。 クラス (class) クラスフルな qdisc は多くのクラスを持つことができ、それらのクラスは その qdisc の内部に属します。またクラスも、自らに属する複数のクラス を持つことがあります。したがってクラスは親として qdisc か別のクラス かを持ちます。 葉クラス (leaf class) は子のクラスを持たないクラスです。このクラス には qdisc がひとつ所属します。この qdisc はそのクラスからのデータ 送信を決定します。クラスを作ると、fifo qdisc が所属します。子クラス を追加すると、この qdisc は削除されます。葉クラスのこの fifo qdisc は、より適切な他の qdisc と置き換え可能です。この qdisc をクラスフ ルな qdisc と置き換えれば、さらにクラスを追加することもできます。 クラス選別器 (classifier) クラスフルな qdisc では、どのクラスにパケットを送るかを決める必要が あります。これはクラス選別器を用いて行われます。 フィルタ (filter) クラス選別はフィルタを用いて行うこともできます。フィルタには多数の 条件が含まれ、これらのどれかにマッチすると、フィルタがマッチしたこ とになります。 スケジューリング (scheduling) クラス選別器を用いると、 qdisc は特定のパケットを他より先に送信する ような決定を下すことができます。この処理はスケジューリングと呼ばれ 、例えば前に紹介した pfifo_fast qdisc で行われていたものです。スケ ジューリングは「並び換え (reordering)」と呼ばれることもありますが、 これはやや紛らわしいです。 帯域制限 (shaping) パケットを送信前に遅延させ、トラフィックが設定された最高速度を越え ないようにする処理のことです。帯域制限は出口で行われます。話し言葉 では、パケットを破棄してトラフィックを遅くすることも、帯域制限と呼 ばれることが多いです。 監視制限 (policing) パケットを遅延または破棄して、トラフィックを設定したバンド幅に収め ることです。 Linux における監視制限ではパケットの破棄のみが可能で、 遅延はできません。「入口キュー」は存在しないのです。 処理保存的 (Work-Conserving) 処理保存的な qdisc では、可能な場合にはつねにパケットを配送します。 つまり、ネットワークアダプタが送信可能 (出力 qdisc の場合) な状態に あれば、決してパケットを遅延させません。 処理非保存的 (non-Work-Conserving) 例えばトークンバケツフィルタのように、パケットをある時間保持してバ ンド幅を制限するようなキューもあります。これはつまり、可能な場合で あってもパケットの引き渡しを拒否するようなこともある、ということで す。 ではここでこれらの用語を整理するため、これらがどの場所にあるのかを見て みることにしましょう。 ┌──────────────────────────────────┐ │ Userspace programs │ │ ^ │ │ | │ │ +---------------+-----------------------------------------+ │ │ | Y | │ │ | -------> IP Stack | │ │ | | | | │ │ | | Y | │ │ | | Y | │ │ | ^ | | │ │ | | / ----------> Forwarding -> | │ │ | ^ / | | │ │ | |/ Y | │ │ | | | | │ │ | ^ Y /-qdisc1-\ | │ │ | | Egress /--qdisc2--\ | │ │ --->->Ingress Classifier ---qdisc3---- | -> │ │ | Qdisc \__qdisc4__/ | │ │ | \-qdiscN_/ | │ │ | | │ │ +----------------------------------------------------------+ │ └──────────────────────────────────┘ この ASCII 図は Jamal Hadi Salim によるものです。 大きなブロックはカーネルを表しています。一番左にある矢印は、ネットワー クからこのマシンに入ってきたトラフィックです。これはまず入口 (ingress) の qdisc に入ります。ここではパケットにフィルタを適用し、破棄するかどう かを決定できます。これは「監視制限」と呼ばれます。 これは非常に初期の段階で、カーネルの大部分がパケットを見る前です。です ので、CPU パワーをあまり使わずにトラフィックをごく初期に捨てたければ、 ここが非常に良い場所です。 継続処理を許されたパケットには、ローカルなアプリケーションに向かうもの があります。この場合そのパケットは処理のために IP スタックに入り、ユー ザ空間のプログラムに渡されます。パケットはアプリケーションには入らずに フォワードされることもあり、この場合は出口 (egress) に向かいます。ユー ザー空間のプログラムがデータを配送することもありますが、このデータは検 査され、出口のクラス選別器へとフォワードされます。 ここでパケットは調査され、たくさんある qdisc のいずれかへキューされます 。設定されていないデフォルトでは、ひとつの出口 qdisc だけがインストール されています。これは pfifo_fast で、これが常にパケットを受信します。こ れは「エンキューイング (enqueueing)」と呼ばれます。 ここでパケットは qdisc に収まり、ネットワークインターフェースを通して送 信するようカーネルが指令するのを待ちます。このように送信されることは「 デキューイング (dequeueing)」と呼ばれます。 この絵はネットワークアダプタがひとつしかない場合でも成立します。カーネ ルを出入りしている矢印は、そのまま絵の通りには取らないで下さい。各ネッ トワークアダプタには、入口と出口両方のフックがあるのです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5. クラスフルなキューイング規則 クラスフルな qdisc は、異なった種類のトラフィックに対して異なる取り扱い が必要な場合に非常に便利です。クラスフルな qdisc のひとつに、'CBQ (Class Based Queueing)' というものがあります。これは非常に広く紹介され ているので、クラスのあるキューイングと言えば CBQ だと思っている人も多い のですが、これは真実ではありません。 CBQ は単に最も古くからあったものにすぎません (そして、最も複雑なもので もあるのですが)。つねにこれが望みの動作をするとは限りません。これは「 sendmail 現象」にだまされ、文書化されていない複雑な技術が入手できる一番 良いものだ、と思っている人たちにはショックかもしれません。 CBQ とその代替品について、もうちょっと説明していきましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.1. クラスフルな qdiscs とクラスにおける流れ クラスフルな qdisc に入ったトラフィックは、その内部にあるクラスのいずれ かに送らなければなりません。よってトラフィックに「クラス選別」を適用す る必要があります。パケットに対して何を行うかを決める際には、いわゆる「 フィルタ」に問い合わせがいきます。ここで大事なのは、フィルタは qdisc の 内部から呼ばれるのであって、他から呼ばれることはありえない、ということ です。 その qdisc に属しているフィルタは、決定を返します。すると qdisc はそれ を用いてパケットをクラスのどれかひとつにエンキューします。サブクラスが あると、さらに動作を適用するかどうか見るために、他のフィルタが適用され ることもあります。サブクラスがなければ、そのクラスはパケットを自分の持 つ qdisc にエンキューします。 多くのクラスフルな qdisc は、他のクラスを持つだけでなく、帯域制限も行い ます。これはパケットのスケジューリング (例えば SFQ) と速度制御の両方を 行うのに便利です。高速のインターフェース (例えばイーサネット) の先に低 速なデバイス (ケーブルモデム) をつなぐ場合などには、これが必要になりま す。 SFQ だけを使うだけでは、何も起こりません。パケットはそのルータに入り、 遅延せずに出て行くだけだからです (出力インターフェースが実際のリンク速 度よりずっと速いからです)。この場合スケジュール対象となるキューは存在し ません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.2. qdisc ファミリ: ルート・ハンドル・兄弟・親 各インターフェースは出口「ルート (root) qdisc」をひとつ持ちます。これは デフォルトでは、先に紹介した pfifo_fast キューイング規則です。各 qdisc とクラスにはハンドル (handle) が割り当てられ、後で行う設定文からこの qdisc を参照する際にはこのハンドルが用いられます。インターフェースには 出口 qdisc だけでなく入口 qdisc もあり、ここでは到着トラフィックの監視 制限ができます。 これらの qdisc のハンドルは、メジャー (major) 番号とマイナー (minor) 番 号の 2 つからなり、: のように記述します。習慣的にルート qdisc には '1:' という名前が付くことになっています。これは '1:0' と同じ です。qdisc のマイナー番号はつねに 0 です。 クラスは、親と同じメジャー番号を持たなければなりません。メジャー番号は 、出口・入口の設定の内部で、他と重なってはいけません。マイナー番号は、 qdisc およびそのクラスの内部で、他と重なってはいけません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.2.1. フィルタによるトラフィックのクラス選別 まとめると、よくある階層図は次のようになります: ┌──────────────────────────────────┐ │ 1: root qdisc │ │ | │ │ 1:1 child class │ │ / | \ │ │ / | \ │ │ / | \ │ │ / | \ │ │ 1:10 1:11 1:12 child classes │ │ | | | │ │ | 11: | leaf class │ │ | | │ │ 10: 12: qdisc │ │ / \ / \ │ │ 10:1 10:2 12:1 12:2 leaf classes │ └──────────────────────────────────┘ しかし、だまされないでください。カーネルがこのような逆向きの木構造 (あ るいは後ほど出てくる網目構造) のもとにあると考えてはいけません。それは 正しくありません。パケットはルート qdisc にエンキューされ、デキューされ るのであって、カーネルはこの部分としか対話しないのです。 パケットは例えば次のようなチェインに沿ってクラス選別されます。 1: -> 1:1 -> 1:12 -> 12: -> 12:2 これでこのパケットは、クラス 12:2 に属する qdisc に収まりました。この例 では、フィルタは木のそれぞれの「節 (node)」に属しており、これらのフィル タが次にどちらの枝に向かうかを決定します。これはわかりやすいですね。し かしながら、次のようなものも可能です: 1: -> 12:2 この場合は、ルートに属するフィルタが、パケットを直接 12:2 に送るように 決めたのです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.2.2. ハードウェアへパケットをデキューする カーネルがパケットを取り出してインターフェースに送ることを決めると、ル ート qdisc である 1: はデキュー要求を受け取り、これは 1:1 に渡されます 。これは続いて 10:, 11:, 12: へと渡されます。これらはそれぞれ兄弟に問い 合わせを行い、自分から dequeue() を行おうとします。この場合パケットは 12:2 にしかないので、木の全体を辿っていかなければなりません。 要するに、ネストされたクラスは親の qdisc と通信するだけで、インターフェ ースとは通信しないのです。カーネルによってデキューされるのはルート qdisc だけなのです! 結果的に、あるクラスがその親より先にデキューすることはありえません。こ れは我々の望むことそのものです: このようにすれば SFQ を内側のクラスとし 、帯域制限は行わずにスケジューリングだけをさせ、制限用の qdisc をその外 に置くことが可能となります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.3. PRIO qdisc PRIO qdisc は実際の帯域制限は行わず、フィルタの設定に従ってトラフィック の分割のみを行います。 PRIO qdisc は、ある種の大きな pfifo_fast だと考 えることができます。ただし各バンドは単なる FIFO ではなく、別々のクラス になります。 パケットが PRIO qdisc にエンキューされると、与えたフィルタコマンドに基 づいて、あるクラスが選別されます。デフォルトでは 3 つのクラスが生成され ます。これらのクラスはデフォルトでは純粋な FIFO qdisc のみを含み、内部 構造は持ちません。しかしこれらは別の qdisc と置き換え可能です。 パケットのデキューを要求されると、クラス :1 が最初に試されます。数値の 大きなクラスは、それより小さなバンドのどれからもパケットを取り出すこと ができなかった場合にのみ用いられます。 TOS フラグだけでなく、tc フィルタの能力すべてを用いてトラフィックの種類 を優先付けしたい場合に、この qdisc はとても便利です。デフォルトで定義さ れている 3 つのクラスにさらに別の qdisc を追加することもできます (pfifo_fast は単純な fifo qdisc しか扱えません)。 これは実際の帯域制限は行わないので、 SFQ に対するものと同じ注意が該当し ます。これを用いるのは、物理的なリンクが一杯になっている場合か、または 実際の帯域制限を行うクラスフルな qdisc の内部でにすべきです。後者はほと んどのケーブルモデムや DSL デバイスに該当します。 公式の言葉で言うと、PRIO qdisc は処理保存的なスケジューラなのです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.3.1. PRIO のパラメータと使い方 tc は以下のパラメータを認識します: bands 生成するバンドのクラス。各バンドは実際にはクラスです。この番号を変 更する場合には、次の項目も変更する必要があります: priomap トラフィックを選別する tc フィルタを与えないと、 PRIO qdisc は TP_PRIO 優先度を参照して、どのようにトラフィックをエンキューするか を決定します。 これは以前に紹介した pfifo_fast qdisc とまったく同じように動作しま す。詳細はそちらを見てください。 各バンドはクラスで、デフォルトでは major:1 から major:3 までの名前が付 きます。よって PRIO qdisc の名前が 12: なら、tc は 12:1 へのトラフィッ クに最も高い優先度を与えるようにフィルタします。 繰り返しますが、バンド 0 のマイナー番号が 1 です! 同様に、バンド 1 はマ イナー番号 2 となります (以下同様)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.3.2. 設定例 この木を作ることを考えましょう: ┌──────────────────────────────────┐ │ 1: root qdisc │ │ / | \ │ │ / | \ │ │ / | \ │ │ 1:1 1:2 1:3 classes │ │ | | | │ │ 10: 20: 30: qdiscs qdiscs │ │ sfq tbf sfq │ │band 0 1 2 │ └──────────────────────────────────┘ バルク転送のトラフィックは 30: へ送り、対話的なトラフィックは 20: また は 10: へ送ります。 コマンドラインです: ┌───────────────────────────────────────────┐ │# tc qdisc add dev eth0 root handle 1: prio │ │## これによって直ちにクラス 1:1, 1:2, 1:3 ができます。 │ │ │ │# tc qdisc add dev eth0 parent 1:1 handle 10: sfq │ │# tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000 │ │# tc qdisc add dev eth0 parent 1:3 handle 30: sfq │ └───────────────────────────────────────────┘ では、いま作った内容を見てみましょう: ┌──────────────────────────────────┐ │# tc -s qdisc ls dev eth0 │ │qdisc sfq 30: quantum 1514b │ │ Sent 0 bytes 0 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms │ │ Sent 0 bytes 0 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc sfq 10: quantum 1514b │ │ Sent 132 bytes 2 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 │ │ Sent 174 bytes 3 pkts (dropped 0, overlimits 0) │ └──────────────────────────────────┘ おわかりのように、バンド 0 には既にトラフィックが入っています。そしてこ のコマンドの実行中に、パケットがひとつ送られました! ここで TOS フラグを適切に設定するようなツールを用いてバルク転送トラフィ ックを流し、再び見てみましょう。 ┌────────────────────────────────────────┐ │# scp tc ahu@10.0.0.11:./ │ │ahu@10.0.0.11's password: │ │tc 100% |*****************************| 353 KB 00:00 │ │# tc -s qdisc ls dev eth0 │ │qdisc sfq 30: quantum 1514b │ │ Sent 384228 bytes 274 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms │ │ Sent 2640 bytes 20 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc sfq 10: quantum 1514b │ │ Sent 2230 bytes 31 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 │ │ Sent 389140 bytes 326 pkts (dropped 0, overlimits 0) │ └────────────────────────────────────────┘ おわかりのように、すべてのトラフィックはハンドル 30: に向かいました。意 図したとおり、これが最も優先度の低いバンドです。では、対話的なトラフィ ックがより優先されるか確認するために、そのようなトラフィックを生成して みましょう: ┌──────────────────────────────────┐ │# tc -s qdisc ls dev eth0 │ │qdisc sfq 30: quantum 1514b │ │ Sent 384228 bytes 274 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms │ │ Sent 2640 bytes 20 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc sfq 10: quantum 1514b │ │ Sent 14926 bytes 193 pkts (dropped 0, overlimits 0) │ │ │ │ qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 │ │ Sent 401836 bytes 488 pkts (dropped 0, overlimits 0) │ └──────────────────────────────────┘ うまくいっています。追加したトラフィックはすべて 10: へ、つまり最も優先 度の高い qdisc へ行っています。先に scp を受けとった、優先度の低いバン ドへは一切向かっていません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.4. (有名な) CBQ qdisc 既に述べたように、CBQ は現在ある中で最も複雑な、最もすごい、そして最も 理解されておらず、おそらくは正しく動作させるのに最も手がかかる qdisc で す。これは作者が悪辣だったり無能だったわけでは全然なく、単に CBQ アルゴ リズムが几帳面なものでなく、また Linux の動作との相性が悪いからなのです 。 クラスフルであるという以外に、CBQ は帯域制限の動作も行います。そしてう まく動かないのは、まさにこの点にあるのです。例えば 10mbit/s の接続を 1mbit/s に絞りたい場合、全時間の 90% のあいだ回線はアイドルになるはずで す。そうでなければ、そうなるように絞る必要があります。 これを測定するのはかなり難しいので、代わりに CBQ は、ハードウェア層から のデータ要求の間隔をマイクロ秒単位で測定し、そこからアイドル時間を導出 しようとします。これを用いると、接続がどのくらい一杯か、空いているかを 概算できます。 これはどちらかというともってまわったやり方で、つねに正しい結果になると は限りません。例えば、インターフェースの実際の速度が、 (おそらくはドラ イバの実装が悪いために) 100mbit/s のデータをフルには通せないとしたらど うでしょう? PCMCIA のネットワークカードも、バスの設計上 100mbit/s に達 することはあり得ません。これらの場合、アイドル時間はどのように考えれば いいのでしょうか? PPP over Ethernet, PPTP over TCP/IP のような、実際のデバイスではないネ ットワークデバイスを考えると、さらに状況は悪くなります。これらのような 場合の実効的なバンド幅は、おそらくユーザ空間のパイプの効率によって決ま ります。これは非常に大きくなるでしょう。 測定を行った人たちは、CBQ が必ずしも常に正確であるとは限らず、ときには 指定とまったく異なる結果となることに気づいています。 しかし、多くの環境では CBQ はうまく動作します。ここに提供した記述を用い れば、ほとんどの場合、うまく動作するような設定が可能だと思います。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.4.1. CBQ による帯域制限の詳細 先に述べたように、CBQ の動作では、ちょうど実際のバンド幅が設定した速度 に落ちるよう、接続を適切な時間アイドルにします。これを行うため、CBQ で は平均的なパケットを渡す際の時間間隔を計算します。 動作中、実効的なアイドル時間は、指数重み付け移動平均 (Exponential Weighted Moving Average: EWMA) を用いて測定されます。これは最近のパケッ トを、過去のパケットに比べて、指数関数の重みでより重要だと考えるもので す。UNIX の平均負荷も、この方法で計算されています。 計算したアイドル時間を、この EWMA 測定値から差し引いた、その結果の数値 を 'avgidle' と呼びます。負荷で一杯の接続における avgidle はゼロです。 このときパケットは、まさに計算された間隔につきひとつずつ到着しています 。 過負荷の接続では avgidle は負の値になります。負になりすぎると CBQ はし ばらく遮断し、'overlimit' 状態になります。 逆に、アイドルな接続では大量の avgidle が蓄積され、数時間沈黙した後のバ ンド幅は無限大になってしまいます。これを防ぐため、avgidle には maxidle という上限が設けられています。 overlimit になると、原理的には、 CBQ は自分自身をちょうどパケット送信間 隔の計算値の間だけ絞ります。そしてパケットをひとつだけ送り、再び絞りま す。ただし以下の 'minburst' パラメータも参照してください。 以降に、帯域制限を設定するために指定できるパラメータを示します: avpkt パケットの平均サイズ (バイト単位)。 maxidle を maxburst から計算す る際に必要です (maxburst はパケット単位で指定されます)。 bandwidth デバイスの物理的なバンド幅。アイドル時間の計算に必要です。 cell パケットをデバイスを通して送信するのにかかる時間は、パケットのサイ ズに応じていくつか異なる値になることがあります。例えばサイズが 800 と 806 のパケットを送るのにかかる時間がちょうど同じだったとすると、 これが粒度を決めます。通常は 8 を指定します。2 の整数べきで指定しな ければなりません。 maxburst maxidle の計算時に、このパケット数が用いられます。 avgidle が maxidle から 0 になるとき、平均的なサイズのパケットをこの数だけバー スト送信できる、というのが条件になります。この数を大きくすると、よ りバーストに対する耐性が高まります。 maxidle を直接設定することはで きず、このパラメータを通して調整するしかありません。 minburst 前述の通り、CBQ は overlimit の際に絞り動作が必要です。この際の理想 的な動作は、ちょうど計算されたアイドル時間だけ絞り、そして 1 つのパ ケットを通すことです。しかし UNIX カーネルは、10ms より短い間隔でイ ベントをスケジュールすることは難しいので、少々長い間隔絞り、そして minburst 分のパケットを一度に通し、そして minburst をかけた時間だけ スリープする方がより良く動作します。 この待ち時間は offtime と呼ばれています。 minburst の値を大きくする と、長い目では正確な帯域制限につながりますが、ミリ秒のタイムスケー ルではより大きなバーストが起きることになります。 minidle avgidle が 0 以下になると overlimit 状態となり、 avgidle が 1 つの パケットの送信を許す値にまで大きくなるのを待たなければなりません。 接続を長い事遮断したあとに、突然大きなバーストが生じるのを防ぐには 、 avgidle が小さくなりすぎたとき、これを minidle にリセットしなけ ればなりません。 minidle は負のマイクロ秒で指定します。したがって 10 を指定すると、 avgidle の下限を -10us としたことになります。 mpu 最小パケットサイズ (Minimum Packet Size) です。データサイズが 0 の パケットでもイーサネットでは 64 バイトとなり、送信にある時間がかか るので、この値が必要です。 CBQ がアイドル時間を正確に計算するには、 この値を知っている必要があります。 rate この qdisc から送信されるトラフィックの速度の設定値。これは「スピー ドつまみ」です。 内部的には、CBQ にはたくさんの微調整をしています。例えばエンキューされ たデータを持っていないことがわかっているクラスには、問い合わせは行きま せん。overlimit のクラスにはペナルティがつき、実効的な優先度が下がりま す。非常に賢くかつ複雑になっています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.4.2. CBQ のクラスフル動作 CBQ は帯域制限だけでなく、先に紹介したアイドル時間の近似を用いて PRIO キューのような動作もできます。つまりクラスに異なる優先度を与え、優先度 の数値が小さい方を、大きな方より先にポーリングできるのです。 ハードウェア層からパケットをネットワークに送信するよう要求されると、重 み付きラウンドロビン (Weighted Round Robin: WRR) プロセスが、優先度の数 値が小さいクラスから開始されます。 これらはグループ化されており、データがあるか問い合わせを受け、データが あればそれを返します。あるクラスがあるバイト数のデキューを許されると、 その優先度にある別のクラスが試行を受けます。 この WRR プロセスは、以降のパラメータによって制御されます。 allot 外部の CBQ がパケットをインターフェースに送るよう依頼されると、この 依頼は (クラスに属する) 内部の qdisc へ、'priority' パラメータの順 に送られます。自分の順番が来たクラスは、ある制限された量のデータを 送信できます。'Allot' はこの量の単位となる量です。より詳しくは 'weight' パラメータを見てください。 prio CBQ は PRIO デバイスのように動作できます。優先度の高い内部クラスが 先に試行され、これらがトラフィックを保持していれば、他のクラスはポ ーリングされません。 weight weight は重み付けラウンドロビンプロセスの補助をします。各クラスは、 送信の機会を順に与えられます。あるクラスが、他に比べて明らかに大き なバンド幅を持つ場合、このクラスには、1 回あたりにより多くのデータ 送信を許すのが妥当でしょう。 CBQ はクラス以下の weight を全て足してこれらを正規化します。したが って任意の数を用いることができ、比率だけが問題になります。たいてい の人は、おおよそ 'rate/10' を目安にしているようで、これでだいたいう まく行っているようです。正規化された weight には 'allot' パラメータ がかけられ、 1 ラウンドあたりに送信できるデータ量が決定されます。 ある CBQ 階層に属するクラスは、すべて同じメジャー番号を持たなければなら ないことに注意してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.4.3. 接続共有・貸借を決める CBQ パラメータ ある種のトラフィックを純粋に制御するだけでなく、どのクラスが他のクラス から回線容量を借りられるか、また逆にバンド幅を貸せるか、ということも CBQ では指定できます。 isolated/sharing クラスを 'isolated' と設定すると、そのクラスは兄弟クラスへのバンド 幅の貸し出しができなくなります。ある回線を、ライバル関係にある (あ るいは互いに仲の悪い) 組織が使用していて、余剰を互いに融通させたく ないようであれば、これを用いてください。 制御プログラムである tc では、'isolated' の逆意である 'sharing' も 使えます。 bounded/borrow クラスは 'bounded' にすることもできます。こうすると兄弟クラスからバ ンド幅を借りようとはしなくなります。 tc では 'bounded' の逆意である 'borrow' も使えます。 典型的な状況は、回線を 2 つの組織が使用していて、その両方が 'isolated' かつ 'bounded' であるような場合でしょう。この場合は両者ともそれぞれに割 り当てられた速度に留まり、お互いに貸し借りも行いません。 このような組織クラスの内部には、バンド幅を貸し借りするような他のクラス 群が置かれることもあるでしょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.4.4. 設定例 ┌──────────────────────────────────┐ │ 1: root qdisc │ │ | │ │ 1:1 child class │ │ / \ │ │ / \ │ │ 1:3 1:4 leaf classes │ │ | | │ │ 30: 40: qdiscs │ │ (sfq) (sfq) │ └──────────────────────────────────┘ この設定では web サーバのトラフィックを 5mbit に、 SMTP トラフィックを 3mbit に制限します。両者の合計は 6mbit を越えないものとします。ハードウ ェアは 100mbit NIC で、これらのクラスは互いにバンド幅を貸し借りできるも のとします。 ┌────────────────────────────────────┐ │# tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 100Mbit \ │ │ avpkt 1000 cell 8 │ │# tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 100Mbit \ │ │ rate 6Mbit weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20 \ │ │ avpkt 1000 bounded │ └────────────────────────────────────┘ この部分では root およびいつもの 1:1 クラスをインストールしています。こ の 1:1 クラスは bounded なので、合計のバンド幅は 6mbit を越えません。 前述の通り、CBQ では非常にたくさんのつまみを調整しなければなりません。 しかしこれらのパラメータは、すべてこれまでに説明してあります。これに対 応する HTB での設定 (後述) は、もっとずっと単純になります。 ┌────────────────────────────────────┐ │# tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 100Mbit \ │ │ rate 5Mbit weight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20 \ │ │ avpkt 1000 │ │# tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 100Mbit \ │ │ rate 3Mbit weight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20 \ │ │ avpkt 1000 │ └────────────────────────────────────┘ これらは 2 つの葉クラスになります。設定したい速度に対して、重みをどのよ うにかけているかに注目してください。これら両クラスは bounded ではありま せんが、bounded であるクラス 1:1 に接続しています。よってこれら 2 クラ スの合計バンド幅は、決して 6mbit を越えません。ところで、クラス id のメ ジャー番号は、親の qdisc と同じでなければなりません! ┌──────────────────────────────────┐ │# tc qdisc add dev eth0 parent 1:3 handle 30: sfq │ │# tc qdisc add dev eth0 parent 1:4 handle 40: sfq │ └──────────────────────────────────┘ 両クラスは、デフォルトでは FIFO qdisc を持っています。しかしここでは、 これらを SFQ で置き換え、両データフローが同じように扱われるようにします 。 ┌───────────────────────────────────┐ │# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \ │ │ sport 80 0xffff flowid 1:3 │ │# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \ │ │ sport 25 0xffff flowid 1:4 │ └───────────────────────────────────┘ これらのコマンドでは、直接 root に追加しており、トラフィックを正しい qdisc に送ります。 ここでは 'tc class add' を、qdisc 内部にクラスを「生成」するために用い ています。一方 'tc qdisc add' は、これらのクラスに qdisc を文字通り追加 するのに用いています。 これら 2 つの規則でクラス選別できないトラフィックが来たらどうなるでしょ うか。この場合は、データは 1:0 の内部で処理されるので、制限を受けません 。 web と SMTP が両方合わせて 6mbit/s を越えそうになったときは、バンド幅は weight パラメータに比例するかたちで分割されます。つまり 5/8 が web サー バに、3/8 がメールサーバに向かいます。 またこの設定からは、web サーバのトラフィックとして、最低 5/8 * 6 mbit = 3.75 mbit が常に保証されることもわかります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.4.5. 他の CBQ パラメータ: split と defmap 先に述べたとおり、クラスフルな qdisc では、どのクラスにパケットをエンキ ューするかを決めるためにフィルタを呼ぶ必要があります。 フィルタを呼ぶほかに、CBQ には他の選択肢もあります。それが defmap と split です。これは極めて理解するのが面倒で、またそれほど重要でもありま せん。しかし他に defmap と split を適切に説明しているところを知りません ので、著者なりに最善を尽くしてみたいと思います。 Type of Service フィールドのみでフィルタを行いたい局面が多いため、特殊 な文法が提供されています。CBQ はパケットのエンキュー先を決めるとき、こ のノードが 'split' ノードであるかどうかをチェックします。もしそのような 場合には、サブの qdisc のひとつは、ある設定された優先度 (これは TOS フ ィールドから決まるでしょう) や、あるいはアプリケーションから設定された ソケットオプションを持つ、すべてのパケットを受信するよう指示されていま す。 そのパケットの優先度ビット列は defmap フィールドと and 演算され、マッチ したかどうかが判断されます。つまりこれは、ある優先度にだけマッチする、 非常に高速なフィルタを手軽に作る方法なのです。 ff (16 進) という defmap はすべてにマッチし、0 は何にもマッチしません。設定例を見れば、少しはわ かりやすいでしょう: ┌─────────────────────────────────────┐ │# tc qdisc add dev eth1 root handle 1: cbq bandwidth 10Mbit allot 1514 \ │ │ cell 8 avpkt 1000 mpu 64 │ │ │ │# tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit \ │ │ rate 10Mbit allot 1514 cell 8 weight 1Mbit prio 8 maxburst 20 \ │ │ avpkt 1000 │ └─────────────────────────────────────┘ 標準的な CBQ の初期段階設定です。しかしこの設定パラメータの多さには、ど うあっても慣れることはありませんね! defmap は TCP_PRIO ビット列を参照します。これは以下のように定義されてい ます。 ┌──────────────────────────────────┐ │TC_PRIO.. Num Corresponds to TOS │ │------------------------------------------------- │ │BESTEFFORT 0 Maximize Reliablity │ │FILLER 1 Minimize Cost │ │BULK 2 Maximize Throughput (0x8) │ │INTERACTIVE_BULK 4 │ │INTERACTIVE 6 Minimize Delay (0x10) │ │CONTROL 7 │ └──────────────────────────────────┘ TC_PRIO.. の数値は右から数えたビット列に対応しています。 TOS ビットの優 先度への変換に関する詳細は、 pfifo_fast の節を見てください。 では対話的なクラスとバルク転送のクラスです: ┌─────────────────────────────────────┐ │# tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit \ │ │ rate 1Mbit allot 1514 cell 8 weight 100Kbit prio 3 maxburst 20 \ │ │ avpkt 1000 split 1:0 defmap c0 │ │ │ │# tc class add dev eth1 parent 1:1 classid 1:3 cbq bandwidth 10Mbit \ │ │ rate 8Mbit allot 1514 cell 8 weight 800Kbit prio 7 maxburst 20 \ │ │ avpkt 1000 split 1:0 defmap 3f │ └─────────────────────────────────────┘ この split qdisc は 1:0 で、ここで選別がなされます。 C0 は 2 進で 11000000 で、3F は 00111111 です。よってこれら 2 つをあわせると、すべて にマッチします。最初のクラスはビット 7 と 6 にマッチし、よって「対話的 」かつ「制御」トラフィックにマッチします。二番目のクラスは残りにマッチ します。 これでノード 1:0 は、次のようなテーブルを持つことになります。 ┌──────────────────────────────────┐ │priority send to │ │0 1:3 │ │1 1:3 │ │2 1:3 │ │3 1:3 │ │4 1:3 │ │5 1:3 │ │6 1:2 │ │7 1:2 │ └──────────────────────────────────┘ もうちょっと遊びたい人は、'change mask' を渡すこともできます。これは変 更したい優先度を厳密に指定するものです。これを使う必要があるのは、'tc class change' を実行する場合だけです。例えば best effort トラフィックを 1:2 に追加するには、次のようにすればできます。 ┌──────────────────────────────────┐ │# tc class change dev eth1 classid 1:2 cbq defmap 01/01 │ └──────────────────────────────────┘ これで 1:0 の優先度マップは次のようになります: ┌──────────────────────────────────┐ │priority send to │ │0 1:2 │ │1 1:3 │ │2 1:3 │ │3 1:3 │ │4 1:3 │ │5 1:3 │ │6 1:2 │ │7 1:2 │ └──────────────────────────────────┘ FIXME: 'tc class change' はソースを見ただけで試してません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.5. 階層的トークンバケツ (Hierarchical Token Bucket: HTB) Martin Devera () は正しくも、 CBQ が複雑で、多くの状況において最 適なものではなさそうだ、という認識に至りました。彼の階層的なアプローチ は、固定されたバンド幅があり、それを別々の目的に分割し、各目的にバンド 幅を保証し、またどのくらいのバンド幅を借りることを定義できるようにした い、というような場合に適しています。 HTB は CBQ と同じように動作しますが、帯域制限にアイドル時間の計算を必要 としません。そのかわり、HTB はクラスフルなトークンバケツフィルタになっ ています (すなわちこれが名前の由来)。パラメータは数個で、彼のサイト で良く文書化されています。 設定を複雑にしたいときも、HTB はちゃんと対応してくれます。 CBQ では、シ ンプルなクラスの設定も既に複雑でした! HTB3 (HTB のバージョンについては HTB のホームページ を見てくださ い) は、公式のカーネルソースの一部になっています (2.4.20-pre1 及び 2.5.31 以降)。しかし、HTB3 パッチの当たった 'tc' を入手する必要があるか もしれません。 HTB のカーネルとユーザ空間の部分とは、同じメジャー番号で なければなりません。さもないと 'tc' は HTB に対して動作しません。 最近のカーネルを持っている人、あるいはカーネルにパッチできる立場の人は 、ぜひとも HTB の利用を考えましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.5.5.1. 設定例 前述の CBQ の設定例と、機能的にはほぼ同一のものです: ┌──────────────────────────────────────────┐ │# tc qdisc add dev eth0 root handle 1: htb default 30 │ │ │ │# tc class add dev eth0 parent 1: classid 1:1 htb rate 6mbit burst 15k │ │ │ │# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit burst 15k │ │# tc class add dev eth0 parent 1:1 classid 1:20 htb rate 3mbit ceil 6mbit burst 15k │ │# tc class add dev eth0 parent 1:1 classid 1:30 htb rate 1kbit ceil 6mbit burst 15k │ └──────────────────────────────────────────┘ 続いて、これらのクラスの下に SFQ を置くと良いでしょう。 ┌──────────────────────────────────┐ │# tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10 │ │# tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10 │ │# tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10 │ └──────────────────────────────────┘ トラフィックを適切なクラスに向けるフィルタを追加します: ┌──────────────────────────────────┐ │# U32="tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32" │ │# $U32 match ip dport 80 0xffff flowid 1:10 │ │# $U32 match ip sport 25 0xffff flowid 1:20 │ └──────────────────────────────────┘ これでおしまい。わかりにくく、説明されていない数値もなければ、文書化さ れていないパラメータもありません。 HTB は実にすばらしく見えます。10: と 20: の両方が保証されたバンド幅を取 り、そしてまだ残りがあれば、これらのクラスはその残りを 5:3 の比率で借り ることになります。これは期待した通りの動作でしょう。 クラス付けされなかったトラフィックは 30: に向かいます。このクラスは自分 自身のバンド幅はほとんど持っていませんが、余っている分はすべて借りるこ とができます。内部で SFQ を利用するようにしたので、公平性もタダで実現で きています! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.6. フィルタによるパケットのクラス選別 あるパケットをどのクラスが処理すべきかを決めるには、いわゆる「クラス選 別チェイン」がその度ごとに呼び出されます。このチェインは、その決断を行 うクラスフルな qdisc に属するすべてのフィルタからなります。 ではまた木を。実際には木ではないのですが。 ┌──────────────────────────────────┐ │ root 1: │ │ | │ │ _1:1_ │ │ / | \ │ │ / | \ │ │ / | \ │ │ 10: 11: 12: │ │ / \ / \ │ │ 10:1 10:2 12:1 12:2 │ └──────────────────────────────────┘ パケットのエンキューイングをする際、それぞれの枝でどのような命令をすべ きか、フィルタチェインに問い合わせがいきます。よくある設定は、1:1 のフ ィルタがパケットを 12: に向け、 12: のフィルタがそのパケットを 12:2 に 送る、といったようなものです。 この後者の規則を直接 1:1 に所属させることもできますが、より特殊なテスト はチェインの先に置く方が、効率的になります。 なお、パケットを「上向き」にフィルタすることはできません。また HTB では 、すべてのフィルタは root に所属させなければなりません。 もう一度。パケットのエンキューは下に向かってのみ行われます。それらのパ ケットがデキューされる際には、インターフェースのある上の方に上がってい くわけです。枝の先の方に行ってもネットワークアダプタはありません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.6.1. シンプルなフィルタの例 クラス選別器の章で説明する通り、非常に複雑な文法が利用でき、文字通りあ らゆるものにマッチが可能です。まずは、わかりやすい例から始めましょう。 幸いこれらは簡単です。 ここでは '10:' という PRIO qdisc があるといます。ここには 3 つのクラス があり、ポート 22 からのトラフィックを最も優先度の高いバンドに割り当て たいとしましょう。このときのフィルタは次のようになります。 ┌──────────────────────────────────┐ │# tc filter add dev eth0 protocol ip parent 10: prio 1 u32 match \ │ │ ip dport 22 0xffff flowid 10:1 │ │# tc filter add dev eth0 protocol ip parent 10: prio 1 u32 match \ │ │ ip sport 80 0xffff flowid 10:1 │ │# tc filter add dev eth0 protocol ip parent 10: prio 2 flowid 10:2 │ └──────────────────────────────────┘ 何をやってるのでしょうか? ここでは: eth0 の node 10: に、優先度 1 の u32 フィルタを加えています。このフィルタは IP 送信先ポートが 22 (だけ) にマッチし、バンド 10:1 へ送ります。次の行では発信元ポートが 80 のパケ ットについて同じことをやっています。最後の行では、マッチしなかったもの はすべて、次に優先度の高いバンド 10:2 に送っています。 (訳注: 0xffff は マッチの前に取る AND マスクです) ここには 'eth0' またはそのインターフェースの名前が必要です。各インター フェースは、それぞれ他と重ならないかたちで割り当てられた、ハンドルの名 前空間を保持しているからです。 IP アドレスで選別するには、次のようにします。 ┌──────────────────────────────────┐ │# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 \ │ │ match ip dst 4.3.2.1/32 flowid 10:1 │ │# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 \ │ │ match ip src 1.2.3.4/32 flowid 10:1 │ │# tc filter add dev eth0 protocol ip parent 10: prio 2 \ │ │ flowid 10:2 │ └──────────────────────────────────┘ これは 4.3.2.1 へのトラフィックと、1.2.3.4 からのトラフィックに最高の優 先度のキューに入れ、その他を 2 番目のキューに入れています。 マッチ条件は連結できます。1.2.3.4 のポート 80 から来たトラフィックにマ ッチさせるには、次のようにします。 ┌───────────────────────────────────────────┐ │# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 match ip src 4.3.2.1/32 \ │ │ match ip sport 80 0xffff flowid 10:1 │ └───────────────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.6.2. 通常必要なフィルタコマンドすべて ここで説明する帯域制限コマンドは、たいていこの前置きから始まっています: ┌──────────────────────────────────┐ │# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 .. │ └──────────────────────────────────┘ これらはいわゆる 'u32' マッチで、パケットのどの部分にもマッチします。 発信元/送信先アドレスによるマッチ 発信元マスクは 'match ip src 1.2.3.0/24'、送信先マスクは 'match ip src 4.3.2.0/24' です。単一のホストにマッチさせるには、/32 を用いる かマスクを省略します。 発信元/送信先ポートによるマッチ (ip プロトコルは問わない) 発信元は 'match ip sport 80 0xffff'、送信先は 'match ip dport 0xffff'。 ip プロトコル (tcp, udp, icmp, gre, ipsec) /etc/protocols の番号を用います。例えば icmp は 1 ですから、'match ip protocol 1 0xff'。 fwmark によるマッチ ipchains か iptables でパケットに印を付けて、その印をインターフェー ス間のルーティングで利用することもできます。これは例えば、eth0 から 入ってきたパケットだけを eth1 で絞る場合などにとても便利です。文法 は次の通り: ┌───────────────────────────────────────┐ │# tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 6 fw flowid 1:1 │ └───────────────────────────────────────┘ これは u32 マッチではないことに注意。 印を付けるには次のようにします。 ┌────────────────────────────────┐ │# iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 6 │ └────────────────────────────────┘ この番号 6 は任意に変更できます。 tc フィルタの文法全部を理解するのが面倒なら、 iptables を使うことに して、fwmark による選別だけを覚えてください。 TOS フィールドによるマッチ 対話的な、minimum delay のトラフィックを選別するには: ┌───────────────────────────────┐ │# tc filter add dev ppp0 parent 1:0 protocol ip prio 10 u32 \ │ │ match ip tos 0x10 0xff \ │ │ flowid 1:4 │ └───────────────────────────────┘ バルクトラフィックには 0x08 0xff を用います。 フィルタコマンドについてもっと知りたい人は、高度なフィルタの章を見てく ださい。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.7. 中間キューイングデバイス (Intermediate queueing device :IMQ) 中間キューデバイスは qdisc ではないのですが、その利用法は qdisc と深く 関連しています。 linux では qdisc はネットワークデバイスに属しており、 デバイスにキューされたものは、すべてこの qdisc にキューされます。このコ ンセプトには、2 つの限界があります。 1. 出口での帯域制限しかできない (入口 qdisc もありますが、クラスフルな qdisc に比べると非常に機能が限られています)。 2. ある qdisc はひとつのインターフェースにおけるトラフィックしか見ない ので、グローバルな制限を置くことはできません。 IMQ はこれら 2 つの制限を解決するためにあるのです。簡単にいうと、qdisc で選んだものすべてを IMQ に置けるのです。専用の印がついたパケットは netfilter の NF_IP_PRE_ROUTING と NF_IP_POST_ROUTING 各フックで割り込み を受け、 imq デバイスに属する qdisc へと渡されます。パケットに印を付け るには、iptables のターゲットを用います。 これによって、つまりある場所から来たパケットに印を付けて入口での帯域制 限もできますし、また複数のインターフェースをクラスとみなしてグローバル な制限を設定できます。他にも例えば http トラフィックを qdisc に入れたり 、新しい接続要求を qdisc に入れたり、などいろいろなことができます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.7.1. 設定例 最初に思い浮かぶのは、入口での帯域制限を用いて確実に保証されたバンド幅 を使えるようにすることでしょう ;) 設定は他のインターフェースでのものと 似ています。 ┌───────────────────────────────────┐ │tc qdisc add dev imq0 root handle 1: htb default 20 │ │ │ │tc class add dev imq0 parent 1: classid 1:1 htb rate 2mbit burst 15k │ │ │ │tc class add dev imq0 parent 1:1 classid 1:10 htb rate 1mbit │ │tc class add dev imq0 parent 1:1 classid 1:20 htb rate 1mbit │ │ │ │tc qdisc add dev imq0 parent 1:10 handle 10: pfifo │ │tc qdisc add dev imq0 parent 1:20 handle 20: sfq │ │ │ │tc filter add dev imq0 parent 10:0 protocol ip prio 1 u32 match \ │ │ ip dst 10.0.0.230/32 flowid 1:10 │ └───────────────────────────────────┘ この例では u32 をクラス選別に用いています。他のクラス選別器も、期待通り に動作します。次に、トラフィックを選別して imq0 にエンキューされるよう 印を付けます。 ┌──────────────────────────────────┐ │iptables -t mangle -A PREROUTING -i eth0 -j IMQ --todev 0 │ │ │ │ip link set imq0 up │ └──────────────────────────────────┘ iptables の IMQ ターゲットは、mangle テーブルの PREROUTING チェインと POSTROUTING チェインで使えます。文法は以下の通り: ┌──────────────────────────────────┐ │IMQ [ --todev n ] n : imq デバイスの番号 │ └──────────────────────────────────┘ ip6tables ターゲットも提供されています。 トラフィックがエンキューされるのは、ターゲットに当たったときではなく、 それ以降になります。実際にトラフィックが imq デバイスに入る場所は、トラ フィックの方向 (in/out) によって異なります。これらは iptables で用いら れる、定義済みの netfilter フックです。 ┌──────────────────────────────────┐ │enum nf_ip_hook_priorities { │ │ NF_IP_PRI_FIRST = INT_MIN, │ │ NF_IP_PRI_CONNTRACK = -200, │ │ NF_IP_PRI_MANGLE = -150, │ │ NF_IP_PRI_NAT_DST = -100, │ │ NF_IP_PRI_FILTER = 0, │ │ NF_IP_PRI_NAT_SRC = 100, │ │ NF_IP_PRI_LAST = INT_MAX, │ │}; │ └──────────────────────────────────┘ 入口トラフィックでは、imq は自分自身を NF_IP_PRI_MANGLE + 1 の優先度を 持つとして登録します。つまりパケットは、 mangle テーブルの PREROUTING チェインを通過した後に、直接 imq デバイスに入るのです。 出口の imq では、NF_IP_PRI_LAST を用います。つまり、filter テーブルが破 棄したパケットはバンド幅を占有すべきでない、という事情を尊重しています 。 パッチとやや詳しい情報が、 imq のサイト にあります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 10. 複数のインターフェースを用いた負荷分散 これを実現する方法はいくつか存在します。簡単で直截的なのは、'TEQL' - "True" (または "trivial") link equalizer です。キューイングに絡むものは たいていそうですが、負荷分散も両方向に動作します。完全な効果を得るには 、接続の両端の関与が必要です。 このような状況を考えてください。 ┌──────────────────────────────────┐ │ +-------+ eth1 +-------+ │ │ | |==========| | │ │ 'network 1' ----| A | | B |---- 'network 2' │ │ | |==========| | │ │ +-------+ eth2 +-------+ │ └──────────────────────────────────┘ A と B はルータで、いまは両方で Linux が動作しているとしましょう。トラ フィックが network 1 から network 2 に向かうと、ルータ A は B に向かう 2 つの接続に、パケットを分散させる必要があります。ルータ B では、これを 受信できるような設定が必要です。逆方向についても同じで、パケットが network 2 から network 1 に向かうとき、ルータ B はそれらのパケットを eth1 と eth2 の両方を使って送る必要があります。 この分散させる部分が、'TEQL' デバイスによってなされます。次のようになり ます (いちばん簡単な設定): ┌──────────────────────────────────┐ │# tc qdisc add dev eth1 root teql0 │ │# tc qdisc add dev eth2 root teql0 │ │# ip link set dev teql0 up │ └──────────────────────────────────┘ 最後の 'ip link set up' コマンドを忘れないこと! これは両方のホストで実行する必要があります。このデバイス teql0 は、送信 するパケットを、基本的にラウンドロビンで eth1 と eth2 に分散させます。 データが teql デバイスから入ってくることは決してありません。データは ' 生 (raw)' の eth1 と eth2 に到着します。 まだデバイスができただけですから、これらに適切なルーティング設定が必要 です。これを行う方法のひとつは、/31 のネットワークを両接続に割り当て、 teql デバイスにも同じように /31 のネットワークを割り当てるやり方です: ルータ A にて: ┌──────────────────────────────────┐ │# ip addr add dev eth1 10.0.0.0/31 │ │# ip addr add dev eth2 10.0.0.2/31 │ │# ip addr add dev teql0 10.0.0.4/31 │ └──────────────────────────────────┘ ルータ B にて: ┌──────────────────────────────────┐ │# ip addr add dev eth1 10.0.0.1/31 │ │# ip addr add dev eth2 10.0.0.3/31 │ │# ip addr add dev teql0 10.0.0.5/31 │ └──────────────────────────────────┘ これでルータ A は 10.0.0.1, 10.0.0.3, 10.0.0.5 に対し、 2 つの実際の接 続と、1 つの TEQL デバイス経由で ping できるようになったはずです。ルー タ B も、 10.0.0.0, 10.0.0.2, 10.0.0.4 にこれらの接続経由で ping できる はずです。 ここまでが動作したら、ルータ A では 10.0.0.5 を network 2 に向かう経路 にし、ルータ B では 10.0.0.4 を network 1 に向かう経路にします。特殊な 場合として、network 1 が自宅のネットワーク、 network 2 がインターネット としますと、ルータ A では 10.0.0.5 を自分のデフォルトゲートウェイになる ようにします。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.1. 注意 何事も見てくれほど簡単ではありません。ルータ A, B 両方で、eth1 と eth2 の return path filtering (戻り経路によるフィルタリング) は無効にしなけ ればなりません。こうしないと、これらのインターフェースは自分自身以外に 向かっている ip パケットを落としてしまいます。 ┌──────────────────────────────────┐ │# echo 0 > /proc/sys/net/ipv4/conf/eth1/rp_filter │ │# echo 0 > /proc/sys/net/ipv4/conf/eth2/rp_filter │ └──────────────────────────────────┘ さて、次にはパケットの並び替え (reordering) に関する面倒な問題がありま す。仮に 6 つのパケットを A から B に送らなければならないとしましょう。 ルータ B がこれを順番どおりに 1, 2, 3, 4, 5, 6 と受け取れば理想的です。 しかし実際には、カーネルがこれを 2, 1, 4, 3, 6, 5 のように受け取る可能 性も非常に高いのです。問題は、これによって TCP/IP が混乱してしまう、と いうことです。たくさんの TCP/IP セッションを別々に運んでいる回線では問 題になりませんが、複数の回線をまとめて、ひとつのファイルの ftp を高速化 することはできません。ただし受信か送信を行っている OS が Linux なら別で 、 Linux は単純な並び替えでは簡単には混乱しません。 いずれにしても、多くのアプリケーションでは、回線の負荷分散は非常に良い 考えです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.2. 他の可能性 William Stearns は、高度なトンネル設定を用いて、複数の相関のないインタ ーネット接続を効率的に利用しています。やり方は彼のトンネリングページ に書かれています。 この HOWTO でも、将来これについて取り上げるかもしれません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 11. Netfilter と iproute でパケットに印を付ける ここまで、iproute がどのように動作するかについて見てきましたが、 netfilter についても何回か言及しました。このあたりで、Rusty's Remarkably Unreliable Guides を通して眺めておくといいかもしれません。 netfilter そのものはこちら にあります。 netfilter を用いるとパケットをフィルタしたり、ヘッダを書き換えたりでき ます。特殊な機能として、パケットに数値の印を付けることもできます。これ には --set-mark オプションを使います。 例として、以下のコマンドはポート 25 に向けた、送信メールのパケットにす べて印を付けています。 ┌──────────────────────────────────┐ │# iptables -A PREROUTING -i eth0 -t mangle -p tcp --dport 25 \ │ │ -j MARK --set-mark 1 │ └──────────────────────────────────┘ いま複数の回線があるとして、ひとつは高速ですが従量制で高く、もうひとつ は低速ですが定額で安いとしましょう。ほとんどの場合、送信メールは安いほ うの経路で送りたいでしょう。 既にパケットには '1' という印を付けました。ではここで経路ポリシーデータ ベースに、このような動作をするよう指示しましょう。 ┌──────────────────────────────────┐ │# echo 201 mail.out >> /etc/iproute2/rt_tables │ │# ip rule add fwmark 1 table mail.out │ │# ip rule ls │ │0: from all lookup local │ │32764: from all fwmark 1 lookup mail.out │ │32766: from all lookup main │ │32767: from all lookup default │ └──────────────────────────────────┘ ここで mail.out テーブルに、遅いが安い回線に向かう経路を作ります。 ┌───────────────────────────────────┐ │# /sbin/ip route add default via 195.96.98.253 dev ppp0 table mail.out│ └───────────────────────────────────┘ これで終わりです。例外を設けたい場合、やり方はたくさんあります。 netfilter の文を修正して特定のホストを除外してもいいですし、除外ホスト に対して、main テーブルを指すような、優先度が高いルールを挿入する方法も あります。 この機能は、TOS ビットを尊重するのにも使えます。 Type of Service の違い によってパケットに印を付け、それらに対して作用するルールを作ればよいの です。こうすれば、例えば ISDN 接続を、対話的なセッション専用にすること もできます。 言うまでもありませんが、これは NAT (マスカレード) を行っているホストで も問題なく動作します。 重要: パケットへの印付けは、少なくとも MASQ と SNAT とに衝突してしまう 、という報告を受けました。これに関する Rusty Russell の説明はこの投稿 で与えられています。正しく動作させるには、reverse path filter を無効に してください。 注意: パケットに印を付けるには、いくつかのカーネルオプションを有効にす る必要があります。 ┌──────────────────────────────────────┐ │IP: advanced router (CONFIG_IP_ADVANCED_ROUTER) [Y/n/?] │ │IP: policy routing (CONFIG_IP_MULTIPLE_TABLES) [Y/n/?] │ │IP: use netfilter MARK value as routing key (CONFIG_IP_ROUTE_FWMARK) [Y/n/?]│ └──────────────────────────────────────┘ クックブックの Section 15.5 も参照してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 12. 高度なフィルタによるパケットのクラス(再)選別 クラスフルなキューイング規則の節で説明したように、パケットを選別してサ ブキューに送る際にはフィルタが必要です。これらのフィルタはクラスフルな qdisc の内部から呼ばれます。 不完全ではありますが、利用できるクラス選別器のリストです。 fw ファイアウォールがパケットをどのように印付けしたかに基づいて決定を 行います。 tc フィルタの文法を勉強したくない人には、安易な解決法で す。詳細はキューイングの章を見てください。 u32 パケット内部のフィールド (発信元 IP アドレスなど) に基づいて決定を 行います。 route パケットが通ることになる経路に基づいて決定を行います。 rsvp, rsvp6 RSVP に基づいてパケッ トをルーティングします。これが有用なのは管理下にあるネットワークだ けです。インターネットは RSVP をちゃんと見ません。 tcindex DSMARK qdisc で用いられます。関連する節を見てください。 パケットのクラス選別を行う方法は一般には何種類か存在し、そのどれを用い るかは、通常は好みの問題に帰着します。 クラス選別器はたいてい、いくつかの共通な引き数を取ります。参考のために ここにリストしておきます。 protocol このクラス選別器が受け付けるプロトコル。通常は IP トラフィックだけ を受け付けることになるでしょう。必須です。 parent このクラス選別器が属しているハンドル。このハンドルは、すでに存在し ているクラスでなければなりません。必須です。 prio クラス選別器の優先度。小さい数値が先にテストされます。 handle フィルタを一意に識別するためのものです。 以降の節では、HostA に向かうトラフィックを絞ることを考えます。ルートク ラスは 1: と設定されていて、選別されたトラフィックの送り先となるクラス は 1:1 であるとします。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.1. u32 クラス選別器 U32 フィルタは現在実装されている中では最も高度なフィルタです。このフィ ルタ全体はハッシュテーブルに基づいており、多くのフィルタルールがある時 でも確実な動作をします。 最もシンプルな形では、U32 フィルタはレコードのリストであり、各レコード は 2 つのフィールド、セレクタとアクションからなります。セレクタは (以降 で詳述しますが)、現在処理している IP パケットと比較され、最初にマッチし たものに関連付けられたアクションが実行されます。最もシンプルなアクショ ンはパケットを定義済みのクラスに向けるものです。 フィルタを設定するには、コマンドラインから tc filter を実行します。これ は 3 つの部分からなります。フィルタ指定部、セレクタ、アクションです。フ ィルタ指定部は次のように定義されます: ┌──────────────────────────────────┐ │tc filter add dev IF [ protocol PROTO ] │ │ [ (preference|priority) PRIO ] │ │ [ parent CBQ ] │ └──────────────────────────────────┘ protocol フィールドはフィルタが適用されるプロトコルを示します。以降では ip の場合についてのみ解説します。 preference フィールドは、ここで定義す るフィルタの優先度を指定します (priority も同じ意味で使えます)。各フィ ルタはルールのリストという形を取りますが、複数のフィルタをいろいろな優 先度で設定できるので、この数値は重要です。各リストではルールが追加され た順に渡され、リスト全体は優先度の小さい (preference の数値が大きい) も のから処理されます。 parent フィールドは、フィルタの属するツリーのトッ プ (例えば 1:0) を指定します。 これらのオプションは、U32 に限らず、すべてのフィルタに適用されます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.1.1. U32 セレクタ U32 セレクタにはパターン定義が含まれており、これが現在処理しているパケ ットにマッチします。正確にはこれは、どのビットがパケットヘッダにマッチ するかであって、それ以上のものではありません。しかしこの単純な方法は非 常に強力です。以降の例をご覧になって下さい。これらは実際に存在している 、極めて複雑なフィルタから直接採ったものです。 ┌──────────────────────────────────┐ │# tc filter add dev eth0 protocol ip parent 1:0 pref 10 u32 \ │ │ match u32 00100000 00ff0000 at 0 flowid 1:10 │ └──────────────────────────────────┘ ここでは、最初の行は気にしないでください。これらのパラメータは、すべて フィルタのハッシュテーブルを記述するものです。 match キーワードを含む、 セレクタの行に注目しましょう。このセレクタは、2 番目のバイトが 0x10 (0010) である IP ヘッダにマッチします。ご想像の通り、次の 00ff という数 値はマッチのマスクで、どのビットが本当にマッチしなければならないかを指 定します。ここではマスクは 0xff なので、実際に 0x10 であるバイトのみが マッチします。 at キーワードは、このマッチを始めるのが指定したオフセッ ト (バイト単位) からであることを示しています。この場合はパケットの先頭 です。以上を人間の言葉に翻訳すると、このセレクタは Type of Service フィ ールドで 'low delay' ビットだけが立っているパケットにマッチします。別の 規則も解析してみましょう。 ┌──────────────────────────────────┐ │# tc filter add dev eth0 protocol ip parent 1:0 pref 10 u32 \ │ │ match u32 00000016 0000ffff at nexthdr+0 flowid 1:10 │ └──────────────────────────────────┘ nexthdr オプションは、この IP パケットにカプセルされている別のヘッダ (next ヘッダ)、つまり上層プロトコルのヘッダを意味します。このマッチング も、next ヘッダの先頭から開始されています。マッチはそのヘッダの 2 番目 の 32 ビットワードに対して行われます。 TCP と UDP 各プロトコルでは、こ のフィールドにはパケットの送信先ポートが含まれます。数値は big エンディ アン形式 (大きい桁が先) で与えられています。よって 0x0016 はそのまま 10 進数で 22 となりますから、これが TCP なら SSH サービスを表しています。 はい、このマッチは前提がないと一意でないですね。この点についてはまた後 ほど説明します。 以上をご理解いただけていれば、次のセレクタも簡単に読めるでしょう「match c0a80100 ffffff00 at 16」ここでは IP ヘッダの先頭から 17 番目以降の 3 バイト分をマッチさせています。これは送信先アドレスが 192.168.1/24 のネ ットワークのどこかになっているものです。以上、いくつか例を解析したとこ ろで、学んだことをまとめてみることにしましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.1.2. 汎用セレクタ (general selector) 汎用セレクタは、パターン、マスク、パケットでそのパターンをマッチさせる 部分のオフセット、を定義します。この汎用セレクタを用いると、ほぼ IP ヘ ッダ (および上層のヘッダ) のどんなビットにもマッチが可能です。ただしこ れらは、次で説明する特殊セレクタに比べると、読み書きが面倒です。汎用セ レクタの文法は以下の通り: ┌──────────────────────────────────┐ │match [ u32 | u16 | u8 ] PATTERN MASK [ at OFFSET | nexthdr+OFFSET] │ └──────────────────────────────────┘ キーワード u32, u16 u8 はどれかひとつを指定し、パターンの長さをビット単 位で表します。 PATTERN と MASK は、このキーワードで指定した長さでなけれ ばなりません。 OFFSET パラメータはバイト単位のオフセットで、マッチ動作 を始める位置です。 nexthdr+ キーワードが指定されると、オフセットは上層 ヘッダの先頭からの相対位置になります。 いくつか例を示します。次の例では Time to Live (TTL) が 64 であるパケッ トがマッチします。 TTL フィールドは IP ヘッダの 8 番目のバイトの直後か ら始まります。 ┌──────────────────────────────────┐ │# tc filter add dev ppp14 parent 1:0 prio 10 u32 \ │ │ match u8 64 0xff at 8 \ │ │ flowid 1:4 │ └──────────────────────────────────┘ 次の例は ACK ビットが設定されているすべての TCP パケットにマッチします: ┌──────────────────────────────────┐ │# tc filter add dev ppp14 parent 1:0 prio 10 u32 \ │ │ match ip protocol 6 0xff \ │ │ match u8 0x10 0xff at nexthdr+13 \ │ │ flowid 1:3 │ └──────────────────────────────────┘ 64 バイトより小さいパケットの ACK にマッチさせたいときは次を用いましょ う: ┌──────────────────────────────────┐ │## match acks the hard way, │ │## IP protocol 6, │ │## IP header length 0x5(32 bit words), │ │## IP Total length 0x34 (ACK + 12 bytes of TCP options) │ │## TCP ack set (bit 5, offset 33) │ │# tc filter add dev ppp14 parent 1:0 protocol ip prio 10 u32 \ │ │ match ip protocol 6 0xff \ │ │ match u8 0x05 0x0f at 0 \ │ │ match u16 0x0000 0xffc0 at 2 \ │ │ match u8 0x10 0xff at 33 \ │ │ flowid 1:3 │ └──────────────────────────────────┘ この規則は、ACK ビットがセットされていて、その他にペイロードは持たない ような TCP パケットにのみマッチします。これは複数のセレクタを使う例にな っています。最終的な結果は、これらの結果の論理積になります。 TCP ヘッダ のダイアグラムを見ると、ACK ビットは TCP ヘッダの 14 バイト目 (at nexthdr+13) の第二ビット (0x10) になっています。最初のセレクタは、もし 難しい方がお好みなら、固有セレクタ (specific selector) を使って ip protocol 6 0xff と書く代わりに、 match u8 0x06 0xff at 9 とも書けます。 6 は TCP のプロトコル番号で、これは IP ヘッダの 10 番目のバイトに書かれ ます。一方逆にこの例では、四番目のマッチには固有セレクタを使っていませ ん。これは単に、TCP ACK ビットにマッチする固有セレクタが存在しないから です。 次のフィルタは、このフィルタの修正版です。違いは ip ヘッダの長さをチェ ックしていない点です。理由? 前述のヘッダは、32 ビットシステムでしか動作 しないからです。 ┌──────────────────────────────────┐ │tc filter add dev ppp14 parent 1:0 protocol ip prio 10 u32 \ │ │ match ip protocol 6 0xff \ │ │ match u8 0x10 0xff at nexthdr+13 \ │ │ match u16 0x0000 0xffc0 at 2 \ │ │ flowid 1:3 │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.1.3. 固有セレクタ (specific selector) 次の表は、この節の著者が tc プログラムのソースコードから発見した、すべ ての固有セレクタをリストしたものです。これらは単にフィルタ設定の可読性 を増し、暮らしを楽にするためのものです。 FIXME: 表はここに: 現在表は別ファイル "selector.html" にあります。 FIXME: まだこのファイルはポーランド語です :-( FIXME: sgml 化も必要です。 いくつか例を示します: ┌──────────────────────────────────┐ │# tc filter add dev ppp0 parent 1:0 prio 10 u32 \ │ │ match ip tos 0x10 0xff \ │ │ flowid 1:4 │ └──────────────────────────────────┘ FIXME: tcp dport のマッチは、以降で説明されているようには動作しません。 このルールは TOS フィールドが 0x10 になっているパケットにマッチします。 TOS フィールドはパケットの第二バイトから始まり、大きさは 1 バイトです。 よって等価な汎用セレクタは match u8 0x10 0xff at 1 と書けます。これは U32 フィルタの内部を垣間見せてくれます。つまり固有なルールは常に汎用ル ールに書き換えられ、そしてカーネルのメモリにはこのかたちで保存されるの です。ここから別の結論も導けます。すなわち tcp と udp はまったく同じで 、これが match tcp dport 53 0xffff という 1 つのセレクタだけでは、この ポートに送られた TCP パケットにマッチできない理由なのです (このポートに 送られた UDP パケットにもマッチしてしまいます)。同時にプロトコルも指定 しなければならないことを覚えねばなりません。結局最終的には次のようなル ールになります。 ┌──────────────────────────────────┐ │# tc filter add dev ppp0 parent 1:0 prio 10 u32 \ │ │ match tcp dport 53 0xffff \ │ │ match ip protocol 0x6 0xff \ │ │ flowid 1:2 │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.2. route クラス選別器 このクラス選別器は、経路テーブルの結果に基づいてフィルタ動作をします。 パケットがクラス間を遷移して "route" フィルタでマークされたクラスに到達 すると、このフィルタは経路テーブルの情報に基づいてパケットを振り分けま す。 ┌──────────────────────────────────┐ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 route │ └──────────────────────────────────┘ ここでは route クラス選別器を親ノード 1:0 に priority 100 で追加してい ます。パケットがこのノードに到達すると (ここでは root なのですぐに到達 します)、この選別器は経路テーブルを見ます。マッチしたらこのパケットは指 定されたクラスに送られ、 priority 100 が与えられます。続いて、これを動 作させるために、適切なルーティングエントリを追加します。 ここでは、送信先または発信元によって 'realm' を定義します。やり方は、例 えば次のようになります。 ┌──────────────────────────────────┐ │# ip route add Host/Network via Gateway dev Device realm RealmNumber│ └──────────────────────────────────┘ 例えば、送信先のネットワークが 192.168.10.0 の場合に realm 番号 10 を与 えるには次のようにします: ┌──────────────────────────────────┐ │# ip route add 192.168.10.0/24 via 192.168.10.1 dev eth1 realm 10 │ └──────────────────────────────────┘ route フィルタを追加する際には、ネットワークやホストを意味する realm 番 号が利用でき、これによって経路をフィルタにどのようにマッチさせるかを指 定できます。 ┌──────────────────────────────────┐ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 \ │ │ route to 10 classid 1:10 │ └──────────────────────────────────┘ このルールは、ネットワーク 192.168.10.0 に向かうパケットにマッチし、 id が 1:10 のクラスに送ります。 route フィルタは経路の発信元を用いてもマッチできます。例えば Linux ルー タの eth2 にサブネットワークが接続されているとします。 ┌──────────────────────────────────┐ │# ip route add 192.168.2.0/24 dev eth2 realm 2 │ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 \ │ │ route from 2 classid 1:2 │ └──────────────────────────────────┘ このフィルタでは 192.168.2.0 サブネットワークから来た (realm 2) パケッ トにマッチし、id が 1:2 のクラスに送ります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.3. 監視制限 (policing) フィルタ さらに複雑な設定を可能にするため、あるバンド幅を越えた場合にマッチする フィルタを導入することもできます。ある速度を越えたら一切マッチをやめて しまうようなフィルタを宣言することも、あるいはある速度を越えた分に対し てのみマッチさせないようにもできます。 つまり監視制限を 4mbit/s としたときに 5mbit/s のトラフィックがあった場 合、 5mbit/s 全部に対してマッチさせないこともできますし、あるいは 1mbit /s にのみマッチさせず 4mbit/s 分は設定されたクラスに送るようにすること もできます。 バンド幅が設定速度を越えた場合の選択肢としては、パケットを破棄する、再 クラス選別する、別のフィルタにマッチを試みさせる、などがあります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.3.1. 監視制限のやり方 監視制限のやり方は、基本的に 2 つあります。カーネルに「評価器 (estimator)」を組み込むと、そのカーネルは各フィルタの依頼を受け、通過さ せたトラフィック量が多いか少ないかを測定できます。これらの評価器は CPU では非常に容易で、一秒に 25 回ずつ、通過したデータ量を測定してそれらの ビットレートを計算します。 別の方法では、再びトークンバケツフィルタが登場します。こんどはフィルタ の内部に置くわけです。 TBF は、設定したバンド幅までのトラフィックにマッ チします。それ以上のパケットがくると、越えた分だけが、制限越えに対して 設定した動作の対象となります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.3.1.1. カーネル評価器を用いる これはとてもシンプルで、パラメータは avrate のひとつだけです。流量が avrate 以内に留まっていると、フィルタはこのトラフィックを設定された classid にクラス選別します。流量がこれを越えると、指定された動作 (この デフォルトは「再クラス選別 (reclassify)」) を行います。 カーネルはバンド幅に指数重み付け移動平均を用います。これは短期間のバー ストにはあまり敏感ではありません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.3.1.2. トークンバケツフィルタを用いる 次のパラメータを用います: ・ burst/buffer/maxburst ・ mtu/minburst ・ mpu ・ rate これらの動作は、トークンバケツフィルタの節で説明したものとほぼ同じです 。しかし注意してほしいのですが、TBF 制限器の mtu を小さくしすぎると、ま ったくパケットが通りません。これに対し、出口 (egress) での TBF qdisc で は、パケットの通過を遅くするだけです。 別の違いは、制限器ではパケットを通すか破棄するかしかできないことです。 パケットを保持して遅延させることはできません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.3.2. 制限を越えたときの動作 制限を越えたと判断すると、フィルタは指定された「動作」を行います。現在 4 つの動作が利用できます。 continue このフィルタはマッチしませんが、他のフィルタにマッチを試みさせます 。 drop これは極めて厳しい選択肢で、ある速度を越えたトラフィックを単に破棄 します。これは入口の監視制限で良く用いられ、利用を制限します。例え ば、5mbit/s を越えるパケットを送ると落ちてしまうようなネームサーバ がある場合、入口フィルタを用いれば、これ以上のパケットを送らないよ うにできます。 Pass/OK トラフィックを問題なく通します。複雑なフィルタを無効にしたいけれど も、そのまま配置はしておきたい場合に使います。 reclassify ほとんどの場合はベストエフォートの再クラス選別になります。これがデ フォルトの動作です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.3.3. 例 現在知られている唯一の実例は、ホストを SYN フラッドから守るの節で説明し ています。 到着する icmp トラフィックを 2kbit に制限し、これを越えたパケットを置と します。 ┌──────────────────────────────────┐ │tc filter add dev $DEV parent ffff: \ │ │ protocol ip prio 20 \ │ │ u32 match ip protocol 1 0xff \ │ │ police rate 2kbit buffer 10k drop \ │ │ flowid :1 │ └──────────────────────────────────┘ パケットのサイズを適当な値に制限します (ここでは 84 バイトより大きなパ ケットはすべて落とします)。 ┌──────────────────────────────────┐ │tc filter add dev $DEV parent ffff: \ │ │ protocol ip prio 20 \ │ │ u32 match tos 0 0 \ │ │ police mtu 84 drop \ │ │ flowid :1 │ └──────────────────────────────────┘ この方法を用いると、すべてのパケットを落とせます。 ┌──────────────────────────────────┐ │tc filter add dev $DEV parent ffff: \ │ │ protocol ip prio 20 \ │ │ u32 match ip protocol 1 0xff \ │ │ police mtu 1 drop \ │ │ flowid :1 │ └──────────────────────────────────┘ これは実際には 1 バイトより大きな icmp パケットを落とします。サイズ 1 バイトのパケットは理論的にはあり得ますが、実際のネットワークではまず存 在しないでしょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.4. ハッシュフィルタ: 超高速大量フィルタリング たくさんのクライアントやコンピュータがあって数千ものルールを必要とし、 そのすべてに別々の QoS 指定を行うと、カーネルはこれらのルールへのマッチ に、多くの時間を費やすことになるでしょう。 デフォルトでは、すべてのフィルタはひとつの大きなチェインに共存しており 、 priority の順にマッチされていきます。1000 のルールがあったら、パケッ トに対して何を行うか決めるには、 1000 回のチェックが必要となるかもしれ ません。 それぞれ 4 つのルールからなる 256 個のチェインのほうが、マッチの速度は ずっと素早くなります。ただしパケットを、これら 256 のチェインうち、適切 なルールがあるものに分散させることが必要になります。 これはハッシュを用いると可能になります。いまケーブルモデムの顧客がネッ トワークに 1024 名いるとしましょう。IP アドレスは 1.2.0.0 から 1.2.3.255 までで、それぞれ 'lite', 'regular', 'premium' などの、別々の カテゴリに属しているものとします。この場合は以下のような 1024 のルール が必要になるでしょう。 ┌────────────────────────────────────┐ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.0.0 classid 1:1 │ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.0.1 classid 1:1 │ │... │ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.3.254 classid 1:3 │ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.3.255 classid 1:2 │ └────────────────────────────────────┘ IP アドレスの末尾部分を「ハッシュキー」として利用すれば、これを高速化で きます。こうすると 256 のテーブルができ、その最初のものはこのようになり ます。 ┌────────────────────────────────────┐ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.0.0 classid 1:1 │ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.1.0 classid 1:1 │ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.2.0 classid 1:3 │ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.3.0 classid 1:2 │ └────────────────────────────────────┘ 次のテーブルはこのようになります。 ┌────────────────────────────────────┐ │# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \ │ │ 1.2.0.1 classid 1:1 │ │... │ └────────────────────────────────────┘ このようにして、最大で 4 つのチェック、平均で 2 つのチェックしか必要な くなります。 設定は極めて複雑ですが、これだけ多くのルールがあるような状況では、十分 作業に見合います。まず root にフィルタを作り、 256 のエントリがあるテー ブルを作ります。 ┌────────────────────────────────────────┐ │# tc filter add dev eth1 parent 1:0 prio 5 protocol ip u32 │ │# tc filter add dev eth1 parent 1:0 prio 5 handle 2: protocol ip u32 divisor 256│ └────────────────────────────────────────┘ ここでいま作ったテーブルのエントリにいくつかルールを追加します。 ┌───────────────────────────────────┐ │# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \ │ │ match ip src 1.2.0.123 flowid 1:1 │ │# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \ │ │ match ip src 1.2.1.123 flowid 1:2 │ │# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \ │ │ match ip src 1.2.3.123 flowid 1:3 │ │# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \ │ │ match ip src 1.2.4.123 flowid 1:2 │ └───────────────────────────────────┘ これはエントリ 123 で、1.2.0.123, 1.2.1.123, 1.2.2.123, 1.2.3.123 への マッチを含んでおり、それぞれを 1:1, 1:2, 1:3, 1:2 に送ります。ここでハ ッシュバケツは 16 進数で指定しなければならないことにご注意ください。 0x7b は 123 です。 次に「ハッシュフィルタ」を作ります。これはトラフィックを、ハッシュテー ブルの正しいエントリに向けます。 ┌───────────────────────────────────┐ │# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 800:: \ │ │ match ip src 1.2.0.0/16 \ │ │ hashkey mask 0x000000ff at 12 \ │ │ link 2: │ └───────────────────────────────────┘ はい、ここで数字のいくつかについては説明が必要でしょう。デフォルトのハ ッシュテーブルは 800:: という名前になります。すべてのフィルタはここから 始まります。次に IP ヘッダの 12, 13, 14, 15 バイト目にある発信元アドレ スを選び、この最後の部分にのみ注目していることを指定しています。これは 先に作ったハッシュテーブル 2: に送っています。 かなり複雑ですが、実際に動作しますし性能向上は驚異的です。この例はさら に改良が可能で、理想的には各チェインに 1 つのフィルタしか入っていない状 況にできます! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.5. IPv6 トラフィックのフィルタリング 12.5.1. なぜ IPv6 では tc フィルタが動かないのか Linux カーネルでは、経路ポリシーデータベース (Routing Policy Database: RPDB) が IPv4 のルーティング・アドレッシング構造を置き換え、この HOWTO で説明している素晴らしい機能の源となっています。残念ながら、Linux での IPv6 構造は、コアの構造の外側で実装されています。共有している機能もある のですが、基本的には RPDB の構造は、 IPv6 のルーティング・アドレッシン グ構造とは別々なのです。 この状況はもちろん変わるでしょう。少々待てば良いだけです。 FIXME: 既に作業している人がいれば、何かアイデアは? 計画は? ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.5.2. IPv6 パケットに ip6tables で印を付ける ipv6tables はパケットに印を付け、数値を割り当てることができます。 ┌──────────────────────────────────┐ │# ip6tables -A PREROUTING -i eth0 -t mangle -p tcp -j MARK --mark 1 │ └──────────────────────────────────┘ しかしこうしても、このパケットは RPDB 構造には渡されないので、役に立ち ません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.5.3. u32 選別器を使って IPv6 パケットにマッチさせる IPv6 は通常 SIT トンネルにカプセルされて IPv4 ネットワークを輸送されま す。このようなトンネルの設定については、IPv6 トンネリングの節を参照して ください。この場合、IPv6 パケットをペイロードに持つ IPv4 パケットに対し て、フィルタを適用することが可能です。 次のフィルタは、IPv6 をカプセルした IPv4 パケットすべてにマッチします。 ┌──────────────────────────────────┐ │# tc filter add dev $DEV parent 10:0 protocol ip prio 10 u32 \ │ │ match ip protocol 41 0xff flowid 42:42 │ └──────────────────────────────────┘ この方向ですすめましょう。IPv6 パケットが IPv4 経由で送信されたとし、こ れらのパケットには何のオプションもセットされていないとします。次のフィ ルタを用いると、オプションのない、 IPv4 中の IPv6 中の ICMPv6 にマッチ します。 0x3a (58) は ICMPv6 の next ヘッダタイプです。 ┌──────────────────────────────────┐ │# tc filter add dev $DEV parent 10:0 protocol ip prio 10 u32 \ │ │ match ip protocol 41 0xff \ │ │ match u8 0x05 0x0f at 0 \ │ │ match u8 0x3a 0xff at 26 \ │ │ flowid 42:42 │ └──────────────────────────────────┘ 送信先 IPv6 アドレスにマッチさせるには、もう少々作業が必要です。次のフ ィルタは送信先アドレスが 3ffe:202c:ffff:32:230:4fff:fe08:358d: である場 合にマッチします。 ┌──────────────────────────────────┐ │# tc filter add dev $DEV parent 10:0 protocol ip prio 10 u32 \ │ │ match ip protocol 41 0xff \ │ │ match u8 0x05 0x0f at 0 \ │ │ match u8 0x3f 0xff at 44 \ │ │ match u8 0xfe 0xff at 45 \ │ │ match u8 0x20 0xff at 46 \ │ │ match u8 0x2c 0xff at 47 \ │ │ match u8 0xff 0xff at 48 \ │ │ match u8 0xff 0xff at 49 \ │ │ match u8 0x00 0xff at 50 \ │ │ match u8 0x32 0xff at 51 \ │ │ match u8 0x02 0xff at 52 \ │ │ match u8 0x30 0xff at 53 \ │ │ match u8 0x4f 0xff at 54 \ │ │ match u8 0xff 0xff at 55 \ │ │ match u8 0xfe 0xff at 56 \ │ │ match u8 0x08 0xff at 57 \ │ │ match u8 0x35 0xff at 58 \ │ │ match u8 0x8d 0xff at 59 \ │ │ flowid 10:13 │ └──────────────────────────────────┘ 同じテクニックはサブネットへのマッチにも使えます。例えば 2001:: の場合 は次のようになります。 ┌──────────────────────────────────┐ │# tc filter add dev $DEV parent 10:0 protocol ip prio 10 u32 \ │ │ match ip protocol 41 0xff \ │ │ match u8 0x05 0x0f at 0 \ │ │ match u8 0x20 0xff at 28 \ │ │ match u8 0x01 0xff at 29 \ │ │ flowid 10:13 │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 13. カーネルのネットワークパラメータ カーネルにはたくさんのパラメータがあり、環境に応じて調整できます。これ までの通り、99% はデフォルトのパラメータでまったく問題ないのですが、し かしこの文書はだてに Advanced HOWTO と名乗っているわけではありません! ここでの興味の対象は /proc/sys/net です。ちょっとご覧になってみてくださ い。このすべてが最初からここで全て文書化できるわけではありませんが、作 業は継続していくつもりです。 それまでの間は、Linux のカーネルソースをご覧になり、 Documentation/ filesystems/proc.txt を読んでください。ほとんどの機能はこのファイルで説 明されています。 (FIXME) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.1. 戻り経路フィルタ (Reverse Path Filtering) デフォルトでは、ルータはすべてをルーティングします。パケットが「明らか に」自分のネットワークには属していなくてもです。よくある例は、プライベ ートの IP 空間がインターネットに漏れてしまう問題です。 195.96.96.0/24 に向かう経路があるインターフェースに対しては、 212.64.94.1 から発したパ ケットは、本来到着しないはずです。 ほとんどの人はこの機能を無効にしたいと思うはずですから、カーネルハッカ ー達はこれを簡単できるようにしてくれました。 /proc 以下にあるファイルを 使うと、カーネルに対してこの指示ができます。この方法は戻り経路フィルタ (Reverse Path Filtering) と呼ばれています。基本的には、あるパケットに対 する返信が、そのパケットの入ってきたインターフェースに向かわない場合、 このパケットはインチキだとみなされて無視されることになります。 以下のコマンドを使うと、現在存在するインターフェース (と将来作られるイ ンターフェース) すべてに対して、この機能を有効にできます。 ┌──────────────────────────────────┐ │# for i in /proc/sys/net/ipv4/conf/*/rp_filter ; do │ │> echo 2 > $i │ │> done │ └──────────────────────────────────┘ 上記の例を実行しておくと、Linux ルータの ISP 側インターフェースに到着し たパケットが、オフィスからやって来たよ、と称していると、そのパケットは 破棄されます。同様に、オフィスのサブネットからきたパケットが、ファイア ウォールの外側からやって来た、と称していると、それも同じように破棄され ます。 これは完全な戻り経路フィルタです。デフォルトでは、直接接続しているネッ トワークの IP だけに基づいてフィルタを行います。完全なフィルタを行うと 、非対称なルーティングで問題が出るからです (パケットの入ってくる経路と 出て行く経路が違う場合です。例えば衛星通信や、動的 (bgp, ospf, rip) 経 路がネットワークにある場合など。衛星のパラボラからやって来たデータには 、通常の地上回線を通して返信することになります)。 もしこの例外があなたの環境に当てはまる (実際にそうかは、おそらくご存じ でしょう) 場合は、衛星データがやって来るインターフェースで rp_filter を 無効にすれば良いです。パケットが破棄されているかを確認したければ、同じ ディレクトリにある log_martians ファイルを使って、それらのイベントを syslog に記録するようカーネルに伝えます。 ┌──────────────────────────────────┐ │# echo 1 >/proc/sys/net/ipv4/conf//log_martians │ └──────────────────────────────────┘ FIXME: conf/{default,all}/* ファイルに設定するだけでいいのか? - martijn ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.2. あまり知られていない設定 はい、変更できるパラメータはとてもたくさんあります。ここではそれらすべ てをリストしたいと考えています。一部は Documentation/ip-sysctl.txt でも 説明されています。 カーネルのコンパイル時に 'Configure as router and not host' に 'Yes' と 答えていると、これらの設定のデフォルトが、ここに示すものとは異なってい るかもしれません。 Oskar Andreasson も、これらのフラグに関するページを公開しています。ここ のものより良いように思えますので、彼のページ もチェックしてみてください。 訳注: この部分の訳出にあたっては、 2.2 カーネル付属文書 proc.txt の翻訳 を参考 にさせていただきました。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.2.1. ipv4 全体 一般的な注意ですが、ほとんどの速度制限機能は loopback に対しては効きま せん。ですので、ローカルでのテストはしないでください。制限は 'jiffies' 単位で与えられ、先に紹介したトークンバケツフィルタによって適用されてい ます。 カーネル内部の時計は、1 秒あたり 'HZ' 刻み (あるいは 'jiffies') で動作 します。 Intel では 'HZ' は大抵の場合 100 です。よって *_rate ファイル を、例えば 50 と設定すると、一秒当たり 2 パケットを許可することになりま す。トークンバケツフィルタは、十分なトークンが溜まった場合、最大 6 パケ ットまでのバーストを許すような設定になっています。 以下のリストの一部は、Alexey Kuznetsov と Andi Kleen による /usr/src/linux/Documentation/networking/ ip-sysctl.txt からコピーしてきたものです。 /proc/sys/net/ipv4/icmp_destunreach_rate カーネルは、パケットを配送できないと判断すると、そのパケットを破棄 します。そしてそのパケットの発信元に、ICMP メッセージをこの速度で送 ります。 /proc/sys/net/ipv4/icmp_echo_ignore_all echo パケットに一切反応しません。これはデフォルトでは設定しないでく ださい。しかし DoS 攻撃の中継に利用されてしまった場合には有用です。 /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts [便利] ネットワークのブロードキャストアドレスに ping すると、すべてのホス トが反応することになっています。これを使うと洒落た利用不能攻撃ツー ルになります。ので、これは 1 にして、このようなブロードキャストメッ セージは無視してください。 /proc/sys/net/ipv4/icmp_echoreply_rate ある 1 つの目的地に対する echo リプライの送信速度。 /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses これを設定すると、ネットワーク上のホストが、ブロードキャストアドレ ス向けとみなしたフレームに対して不正に反応したために発した ICMP error を無視します。 /proc/sys/net/ipv4/icmp_paramprob_rate あまり知られていない ICMP メッセージですが、 IP ないし TCP ヘッダが 壊れている、不正なパケットに対する返信として送られます。このファイ ルを用いると、このメッセージの送信速度を制御できます。 /proc/sys/net/ipv4/icmp_timeexceed_rate これは traceroute における「Solaris の真ん中の *」の原因として有名 です。 ICMP Time Exceeded メッセージの送信速度を制限します。 /proc/sys/net/ipv4/igmp_max_memberships このホストで待ち受けする igmp (マルチキャスト) ソケットの最大数。 FIXME: 正しいか? /proc/sys/net/ipv4/inet_peer_gc_maxtime FIXME: inet peer storage について少々説明を追加? ガベージコレクショ ンを行う最大間隔。この間隔は、プール上のメモリ負荷が低い (あるいは 無い) 場合に効力を持ちます。 jiffies 単位です。 /proc/sys/net/ipv4/inet_peer_gc_mintime ガベージコレクションを行う最小間隔。この間隔は、プール上のメモリ負 荷が高い場合に効力を持ちます。 jiffies 単位です。 /proc/sys/net/ipv4/inet_peer_maxttl エントリの time-to-live の最大値。使われなかったエントリは、プール にメモリ負荷がない場合 (つまりプールのエントリ数が非常に小さい場合) 、この期間がすぎると期限切れとなります。 jiffies 単位です。 /proc/sys/net/ipv4/inet_peer_minttl エントリの time-to-live の最小値。パケットの再構成を行う側では、フ ラグメントの time-to-live をカバーできる十分な大きさにしなければな りません。この time-to-live の最小値は、プールのサイズが inet_peer_threshold より小さい場合に保証されます。 jiffies 単位です 。 /proc/sys/net/ipv4/inet_peer_threshold INET peer storage の概算サイズです。この閾値を越えると、エントリは 積極的に削除され始めます。この閾値は、エントリの time-to-live と、 ガベージコレクションの間隔にも影響します。エントリが多くなると、 time-to-live は短くなり、GC の間隔も短くなります。 /proc/sys/net/ipv4/ip_autoconfig このホストが IP 設定を RARP, BOOTP, DHCP などの機構で取得した場合、 このファイルの内容は 1 になります。そうでなければ 0 です。 /proc/sys/net/ipv4/ip_default_ttl パケットの Time To Live 値です。 64 にしておけば安全でしょう。巨大 なネットワークでは増やしてください。ただし興味だけでそうしないよう に。経路のループがあった場合の被害が大きくなります。場合によっては 減らすことを考えてもよいでしょう。 /proc/sys/net/ipv4/ip_dynaddr インターフェースアドレスが動的に決まるダイヤルオンデマンドを用いて いる場合はこれを設定しておく必要があります。オンデマンドのインター フェースが起動すると、返信を見ていなかったローカルの TCP ソケットが 、正しいアドレスにバインドしなおします。これによってインターフェー スを起動させた接続が動作しないが、二番目からは大丈夫になる、という 問題が解決します。 /proc/sys/net/ipv4/ip_forward カーネルにパケットのフォワードをさせたいかどうか。デフォルトでは無 効になっています。 /proc/sys/net/ipv4/ip_local_port_range 外に向かう接続における、ローカルポートの範囲。実際のデフォルトは極 めて小さく、1024 から 4999 です。 /proc/sys/net/ipv4/ip_no_pmtu_disc Path MTU discovery を無効にしたければこれを設定してください。これは 経路の Maximum Transfer Unit の最大値を決定する方法です。クックブッ クの章における Path MTU discovery の節もご覧ください。 /proc/sys/net/ipv4/ipfrag_high_thresh IP フラグメントを再構成する際に用いる最大メモリ。 ipfrag_high_thresh バイトのメモリがこの目的に割り当てられると、フラ グメントハンドラは ipfrag_low_thresh になるまでパケットを投げ込みま す。 /proc/sys/net/ipv4/ip_nonlocal_bind アプリケーションを自分のシステムには属していないデバイスにバインド させたければ、これを設定してください。これはマシンの接続が永続的で ない (あるいは動的な) 場合、接続が切れたときにもサービスが起動して 特定のアドレスにバインドできるようにするのに便利です。 /proc/sys/net/ipv4/ipfrag_low_thresh IP フラグメントの再構成に用いられるメモリの最小値。 /proc/sys/net/ipv4/ipfrag_time IP フラグメントをメモリに保持する時間 (秒単位)。 /proc/sys/net/ipv4/tcp_abort_on_overflow ブール値のフラグで、接続要求がたくさん来たときの動作を制御します。 有効になっていると、サービスが過負荷になったとき、カーネルは積極的 に RST パケットを送るようになります。 /proc/sys/net/ipv4/tcp_fin_timeout ソケットをこちら側からクローズしたときに、FIN-WAIT-2 状態に保つ時間 。通信先が壊れていると、先方でのクローズをずっと行わなかったり、あ るいは予期しない形で死んでしまうかもしれません。デフォルトの値は 60 秒です。2.2 で用いられていた通常の値は 180 秒で、こちらにすることも できますが、マシンが (必ずしも負荷が高くなくても) WEB サーバだった りすると、膨大な FIN-WAIT-2 ソケットのせいでメモリが溢れる危険もあ ります。 FIN-WAIT-2 ソケットは 1.5K のメモリしか食わないので FIN-WAIT-1 ほど危険ではありませんが、しかし長く生きる傾向があります 。参照: tcp_max_orphans /proc/sys/net/ipv4/tcp_keepalive_time keepalive が有効になっているとき、 TCP が keepalive メッセージを送 る頻度。デフォルトは 2 時間です。 /proc/sys/net/ipv4/tcp_keepalive_intvl プローブに対する確認が返信されなかったときにプローブを再送する頻度 。デフォルトは 75 秒です。 /proc/sys/net/ipv4/tcp_keepalive_probes TCP が keepalive プローブを送る数。この数に達すると、その接続が壊れ たとみなします。デフォルトの値は 9 です。この値に tcp_keepalive_intvl をかけると、ある keepalive が送られた後に許され る無反応時間が得られます。 /proc/sys/net/ipv4/tcp_max_orphans システムが、どのユーザファイルハンドルにもアタッチしていない TCP ソ ケットを保有できる最大数。この数を越えると、みなしごの接続は直ちに リセットされ、警告が表示されます。この制限は単純な DoS 攻撃を防ぐた めにあります。この機能に頼りすぎたり、わざと制限を小さくしたりして はいけません。ネットワークの状況によって必要な場合は、 (できればメ モリを追加してから) デフォルトより増やすのはかまいません。また、ぐ ずぐずするネットワークサービスを調整して、そのような状態を積極的に 殺すようにしてください。再度強調しておきますが、みなしごの接続は、 それぞれスワップ不可能なメモリを最大 64K 消費するのです。 /proc/sys/net/ipv4/tcp_orphan_retries こちら側からクローズした TCP コネクションを終了する前の再送回数。デ フォルト値は 7 で、これは RTO によりますが、50 秒から 16 分です。マ シンで WEB サーバを動作している場合、このようなソケットはリソースを 著しく消費するかもしれないので、この値を低くすることを考えてくださ い。参照: tcp_max_orphans /proc/sys/net/ipv4/tcp_max_syn_backlog 送信した接続要求のうち、まだ接続先から ACK を受け取っていないものを 記憶しておく最大数です。デフォルト値は、128Mb 以上のメモリを搭載し ているマシンで 1024、メモリの少ないマシンでは 128 です。サーバが過 負荷に苦しんでいる場合は、この数を増加させてみてください。警告! 1024 以上にする場合は、 include/net/tcp.h の TCP_SYNQ_HSIZE を変更 して TCP_SYNQ_HSIZE*16<=tcp_max_syn_backlog が満足されるようにし、 カーネルを再コンパイルした方がいいでしょう。 /proc/sys/net/ipv4/tcp_max_tw_buckets システムが同時に保持する time-wait ソケットの最大数。この数を越える と、time-wait ソケットは直ちに破棄され、警告が表示されます。この制 限は単純な DoS 攻撃を防ぐためにあります。わざと制限を小さくしてはい けません。ネットワークの状況によって必要な場合は、 (できればメモリ を追加してから) デフォルトより増やすのはかまいません。 /proc/sys/net/ipv4/tcp_retrans_collapse 問題のあるプリンタのバグ-バグ互換性を保つためのもの。特定の TCP ス タックのバグに対処するため、再送時に大きなパケットを送ります。 /proc/sys/net/ipv4/tcp_retries1 再送回数で、この数を越えると何か問題があると判断し、その疑いをネッ トワーク層に報告する必要があります。 RFC での最小値は 3 で、これが デフォルトです。 RTO によりますが、3 秒から 8 分に相当します。 /proc/sys/net/ipv4/tcp_retries2 接続中の TCP セッションを殺すまえに再送を試みる回数。 RFC 1122 によれば、この時間は 100 秒以 上にならなければいけません。これは実際には短すぎる。デフォルト値は 15 で、RTO の値によりますが 13-30 分に相当します。 /proc/sys/net/ipv4/tcp_rfc1337 このブール値は RFC1337 で説明されている 'time-wait assassination hazards in tcp' の修正です。有効になっていると、カーネルは time-wait 状態にあるソケットへの RST パケットを破棄します。 /proc/sys/net/ipv4/tcp_sack Selective ACK を用います。特定のパケットが失われているかを調査でき 、よって回復が速やかになります。 /proc/sys/net/ipv4/tcp_stdurg TCP urg ポインタフィールドを、Host requirements に従って解釈します 。多くのホストは古い BSD の解釈を使っているので、 Linux でこれをセ ットすると正しく通信ができないかもしれません。既定値: FALSE /proc/sys/net/ipv4/tcp_syn_retries 新しい接続を開始するとき、カーネルはこの回数 SYN パケットを送っても 駄目ならばあきらめます。 /proc/sys/net/ipv4/tcp_synack_retries 接続を受付側としてオープンするとき、カーネルは SYN に ACK を詰め込 んで送り、先に受けとった SYN を確認します。これは 3 方向ハンドシェ ークの 2 番目の部分です。この設定は、カーネルが接続をあきらめるまで に送る、 SYN+ACK パケットの再送数を指定します。 /proc/sys/net/ipv4/tcp_timestamps タイムスタンプは、特に、シーケンス番号の重なりを防ぐために使用され ます。 1 ギガビットの接続では、おそらく順序通りでない、古いシーケン ス番号に出くわすことがあります (前の世代の番号であるため)。タイムス タンプを用いると、これを「昔のパケット」と認識できます。シークエン ス番号がオーバーフローして、また0から始まることを言ってる? --> /proc/sys/net/ipv4/tcp_tw_recycle TIME-WAIT ソケットを高速にリサイクルできるようにします。デフォルト 値は 1 です。これは専門技術者のアドバイスか要求がなければ、変更すべ きではありません。 /proc/sys/net/ipv4/tcp_window_scaling 通常 TCP/IP では、最大 65535 バイトのウィンドウが可能です。本当に高 速なネットワークでは、これでは十分でないかもしれません。ウィンドウ 拡大オプションを用いると、ほぼギガバイトのウィンドウが利用できます 。これはバンド幅と遅延時間の積が大きいような製品に適しています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.2.2. デバイスごとの設定 DEV は実際のインターフェースか、あるいは 'all' または 'default' を表し ます。 default は、まだ作成されていないインターフェースの設定にも影響し ます。 /proc/sys/net/ipv4/conf/DEV/accept_redirects ルータは、自分が間違った目的で利用されている (つまり受けとったパケ ットを同じインターフェースに再送しなければならない) と判断すると、 そのパケットの発信元に ICMP Redirect を送信します。しかしこれは少々 セキュリティ上の問題があるので、この受信を拒否したり、あるいは安全 なリダイレクトを利用できます。 /proc/sys/net/ipv4/conf/DEV/accept_source_route もうあまり用いられません。以前はパケットに、途中で訪れるべき IP ア ドレスのリストを与えることが可能でした。 Linux はこの IP オプション を尊重するような動作が可能です。 /proc/sys/net/ipv4/conf/DEV/bootp_relay 発信元アドレスが 0.b.c.d で、送信先がこのホストでないパケットを、ロ ーカルなパケットとして許可します。これは BOOTP リレーデーモンが、そ のようなパケットを受信・フォワードしているのだと考えられます。 この機能はまだ (カーネルバージョン 2.2.12 では) 実装されていないの で、デフォルトは 0 です。 /proc/sys/net/ipv4/conf/DEV/forwarding このインターフェースでの IP フォワーディングを有効/無効にします。 /proc/sys/net/ipv4/conf/DEV/log_martians 戻り経路フィルタ (Reverse Path Filtering) の節を見てください。 /proc/sys/net/ipv4/conf/DEV/mc_forwarding このインターフェースでマルチキャストフォワーディングを行うかどうか です。 /proc/sys/net/ipv4/conf/DEV/proxy_arp これを 1 にすると、このインターフェースはカーネルが経路を知っている アドレスに対する ARP 要求に返信します。「疑似 ip ブリッジ」を構築す るのに非常に便利に使えます。これを有効にする前には、ネットマスクが 本当に正しいかをしっかり確認してください! また rp_filter (別のとこ ろで紹介しています) が、 ARP 問い合わせに対して動作することも覚えて おいてください! /proc/sys/net/ipv4/conf/DEV/rp_filter 戻り経路フィルタ (Reverse Path Filtering) の節を見てください。 /proc/sys/net/ipv4/conf/DEV/secure_redirects ICMP redirect メッセージを、デフォルトゲートウェイにリストされてい るゲートウェイに対してのみ許可します。デフォルトで有効です。 /proc/sys/net/ipv4/conf/DEV/send_redirects このホストが上述の redirect を送信するかどうかです。 /proc/sys/net/ipv4/conf/DEV/shared_media 設定されていないと、カーネルはこのデバイスにつながっている別々のサ ブネットは、直接通信できないとみなします。デフォルトの設定は 'yes' です。 /proc/sys/net/ipv4/conf/DEV/tag FIXME: ここを埋めてください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.2.3. 近隣ポリシー DEV は実際のインターフェースか、あるいは 'all' または 'default' を表し ます。 default は、まだ作成されていないインターフェースの設定にも影響し ます。 /proc/sys/net/ipv4/neigh/DEV/anycast_delay 近隣確認メッセージへの返答のランダム遅延の最大値 (jiffies [1/100 秒] 単位)。まだ実装されていません (Linux はまだ anycast をサポート していません)。 /proc/sys/net/ipv4/neigh/DEV/app_solicit ユーザレベルの ARP デーモンに送るリクエストの数を定義します。無効に するには 0 にします。 /proc/sys/net/ipv4/neigh/DEV/base_reachable_time RFC2461 で規定されているランダム到達可能時間の計算のベースになる値 です。 /proc/sys/net/ipv4/neigh/DEV/delay_first_probe_time 近隣が到達可能かどうかのプローブを最初に行うまでの遅延時間です (gc_stale_time を参照)。 /proc/sys/net/ipv4/neigh/DEV/gc_stale_time 古い ARP エントリをどの程度の頻度でチェックするかを決めます。古くな った ARP エントリは再び解決されます (これは IP アドレスが他のマシン へ移る時に役に立ちます)。 ucast_solicit が 0 より大きければ、まず既 知のホストに直接 ARP パケットを送信します。これが失敗したら、 mcast_solicit が 0 より大きければ ARP 要求をブロードキャストします 。 /proc/sys/net/ipv4/neigh/DEV/locktime ARP/近隣エントリは、最低でも locktime だけ経過しないと新しいものと 置き換えできません。これは ARP キャッシュのスラッシングを防ぎます。 /proc/sys/net/ipv4/neigh/DEV/mcast_solicit マルチキャスト確認の最大再試行回数です。 /proc/sys/net/ipv4/neigh/DEV/proxy_delay 代理 ARP エントリを持っている場合に、その ARP 要求に答えるまでの最 大時間です。実際の時間は 0 以上 proxy_delay 以下の乱数になります。 ある場合には、これはネットワークの溢れを防ぐために使用されます。 /proc/sys/net/ipv4/neigh/DEV/proxy_qlen 遅延した代理 ARP タイマのキューの最大長です (proxy_delay を参照)。 /proc/sys/net/ipv4/neigh/DEV/retrans_time 近隣確認メッセージを再送信するまでの時間 (単位は jiffy = 1/100 秒) です。アドレスを解決したり、近隣が到着不能かどうかを確認したりする 際に使用します。 /proc/sys/net/ipv4/neigh/DEV/ucast_solicit ユニキャスト確認の最大再試行回数です。 /proc/sys/net/ipv4/neigh/DEV/unres_qlen 待ち状態の ARP 要求を保持するキューの最大長です。 ARP アドレスが解 決されている最中に、他の層からの要求パケットを受け入れる数です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.2.4. ルーティング設定 /proc/sys/net/ipv4/route/error_burst このパラメータと次の error_cost はルーティングのコードからカーネル のログに書き出される警告メッセージの制限に使用されます。 error_cost を大きくすると書き出されるメッセージは少なくなります。 error_burst はメッセージを捨てる際の制御を行います。デフォルトでは警告メッセー ジを 5 秒間に一回までに制限しています。 /proc/sys/net/ipv4/route/error_cost /proc/sys/net/ipv4/route/error_burst の項を参照してください。 /proc/sys/net/ipv4/route/flush このファイルに書き込むとルーティングキャッシュをフラッシュします。 /proc/sys/net/ipv4/route/gc_elasticity ルーティングキャッシュのガベージコレクションの頻度と動作を決める値 です。これはフェイルオーバー動作で重要となります。ある経路が死んだ とき、Linux が別の経路に移るまでには、最低 gc_timeout 秒が経過しな ければなりません。デフォルトでは 300 に設定してありますが、フェイル オーバーを早く行いたければ、もっと小さい値にすると良いでしょう。 Ard van Breemen のこの投稿 も参照してください。 /proc/sys/net/ipv4/route/gc_interval /proc/sys/net/ipv4/route/gc_elasticity の項をご覧ください。 /proc/sys/net/ipv4/route/gc_min_interval /proc/sys/net/ipv4/route/gc_elasticity の項をご覧ください。 /proc/sys/net/ipv4/route/gc_thresh /proc/sys/net/ipv4/route/gc_elasticity の項をご覧ください。 /proc/sys/net/ipv4/route/gc_timeout /proc/sys/net/ipv4/route/gc_elasticity の項をご覧ください。 /proc/sys/net/ipv4/route/max_delay ルーティングキャッシュがフラッシュされるまでの最大時間です。 /proc/sys/net/ipv4/route/max_size ルーティングキャッシュの最大サイズです。キャッシュがこのサイズに達 すると、古いエントリは破棄されます。 /proc/sys/net/ipv4/route/min_adv_mss FIXME: ここを埋めてください。 /proc/sys/net/ipv4/route/min_delay ルーティングキャッシュがフラッシュされるまでの最小時間です。 /proc/sys/net/ipv4/route/min_pmtu FIXME: ここを埋めてください。 /proc/sys/net/ipv4/route/mtu_expires FIXME: ここを埋めてください。 /proc/sys/net/ipv4/route/redirect_load 特定のホストに対して ICMP リダイレクトをより多く送るべきかを決定す るための因子です。負荷がここで指定した数に達した場合には、それ以上 のリダイレクトは送信されません。 /proc/sys/net/ipv4/route/redirect_number /proc/sys/net/ipv4/route/redirect_load の項目を参照してください。こ ちらはリダイレクト数の制限です。 /proc/sys/net/ipv4/route/redirect_silence リダイレクトの時間切れ。負荷やリダイレクト数が制限に達してリダイレ クトが停止されていた場合でも、この期間が過ぎるとリダイレクトを再び 送り始めます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 14. 知られざる高度なキューイング規則 これまで紹介してきたキューでは解決できない要望がある人のために、ここで はカーネルに含まれている、より特殊なキューを紹介します。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.1. bfifo/pfifo これらのクラスレスなキューは、pfifo_fast に比べてもシンプルです。内部バ ンドが無く、すべてのトラフィックがまったく等価なのです。しかしこれらに はひとつ、重要な利点があります。統計が取れるのです。よって帯域制限や優 先付けが必要ない場合でも、これらの qdisc を用いると、インターフェースの backlog を決定するのに役立ちます。 pfifo の長さはパケット単位で、bfifo ではバイト単位で指定します。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.1.1. パラメータと使い方 limit キューの長さを指定します。bfifo ではバイト単位、pfifo ではパケット 単位です。デフォルトは pfifo の場合インターフェースの txqueuelen 分 のパケットで、 bfifo では txqueuelen*mtu バイトです (txqueuelen に ついては pfifo_fast の章を参照)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.2. Clark-Shenker-Zhang アルゴリズム (CSZ) これは非常に理論的なので、Alexey (CBQ の主要な作者) ですら理解すること を求めていません。Alexey のソースにあった参考文献です: David D. Clark, Scott Shenker and Lixia Zhang Supporting Real-Time Applications in an Integrated Services Packet Network: Architecture and Mechanism. 私の理解では、このアイディアの肝は、保証するサービスそれぞれに WFQ フローを生成し、残りのバンド幅をダミーの flow-0 に割り当てるところ にあります。 flow-0 は予測的サービス (predictive services) とベスト エフォートのトラフィックとからなります。これは優先度スケジューラに よって管理され、予測的サービスに最も高い優先度が割り当てられます。 残りはベストエフォートのパケットになります。 CSZ はバンド幅の制限を「行いません」。フローはすでに QoS ネットワー クの境界で流入管理をされたとみなしており、それ以上の帯域制限は行い ません。経路途中 (intermediate hop) で、フローを向上させたりトーク ンバケツフィルタによる絞りを行おうとすると、望ましくない遅延が起き たり、ジッターの原因になります。 現在のところ CSZ は本物の「保証付きサービス」を提供する唯一のスケジ ューラです。 (CBQ も含む) 他の機構は、保証付き遅延やランダムなジッ ターを提供しません。 現在のところは、先に紹介した記事を読んで理解できない限り、あまり実 際の利用には向いていないようです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.3. DSMARK Esteve Camps この文章は、2000 年 9 月の私の論文 QoS Support in Linux から抜粋し たものです。 元になった文書: ・ Draft-almesberger-wajhak-diffserv-linux-01.txt . ・ iproute2 配布にある例 ・ White Paper-QoS protocols and architectures and IP QoS Frequently Asked Questions both by Quality of Service Forum. この章は Esteve Camps が執筆しました。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.3.1. はじめに まず最初に、このテーマについて書かれた RFC (RFC2474, RFC2475, RFC2597, RFC2598) を読むことを強くお奨めします。これらは IETF DiffServ working Group web site および Werner Almesberger web site にあります (彼は Linux で Differentiated Services の 機能をサポートするコードを書きました)。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.3.2. Dsmark の対象 Dsmark は、Differentiated Services (DiffServ とか、単に DS とも呼ばれま す) で必要となる機能を提供するキューイング規則です。 DiffServ は 2 つあ る実際の QoS 機構のうちのひとつ (もうひとつは Integrated Services と呼 ばれています) で、パケットの IP ヘッダの DS フィールドに含まれている値 に基づいて動作します。 IP で、何らかの QoS レベルのサービスを得るために設計された最初の解は、 IP ヘッダの Type of Service フィールド (TOS バイト) でした。この値を変 えることにより、スループット・遅延・信頼性などのレベルの高低を選択でき たのでした。しかしこれは、最近のサービス (リアルタイムアプリケーション 、対話的アプリケーションなど) で必要とされる、十分な柔軟性を持っていま せんでした。その後、新たな機構が現れました。そのひとつが DiffServ で、 これは TOS の各ビットを占有し、 DS フィールドとして再定義して使用します 。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.3.3. Differentiated Services のガイドライン DiffServ はグループ指向です。つまり、フローについては何も関知しないので す (これは Integrated Services の目的となります)。対象にするのはフロー の集合で、パケットがどの集合に属しているかによって、異なる動作を適用す ることになります。 パケットが境界ノード (DiffServ ドメインのエントリノード) に到着すると、 DiffServ ドメインへ入る際に、これらのパケットには制限・帯域制限・マーキ ングなどを行う必要があります (マーキングとは、DS フィールドに値を代入す ることです。牛みたいなもんです :-) )。 DiffServ ドメインの内部 (コア) ノードで、どのような動作や QoS レベルを適用するかは、このマーク (値) を 見て判断されます。 ご想像の通り、Differentiated Services にはドメインがあり、すべての DS 規則はここで適用されます。実際、ドメインに入ってきたすべてのパケットを クラス選別する、と考えることも可能です。パケットはドメインに入ると、ク ラス選別機構が命ずる規則に従うことになり、経由するノードのすべてでその QoS レベルが適用されるのです。 実のところ、自分のローカルなドメインには自前の制限を適用できますが、他 の DS ドメインと接続する際には、 Service Level Agreements を考慮しなけ ればなりません。 この段階では、おそらくたくさんの疑問があるでしょう。 DiffServ は、ここ で説明した以上の内容を含んでいます。 3 つの RFC を 50 行で要約するのが 不可能であることは、理解していただけるでしょう :-) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.3.4. Dsmark を利用する DiffServe の文献が示す通り、我々は境界ノードと内部ノードを差別化します 。これらはトラフィック経路のうち、重要な 2 つのポイントです。いずれにお いても、パケットが到着するとクラス選別が行われます。その結果は、DS プロ セスがパケットをネットワークに放つにあたって、どこか別の場所で用いられ ることになるでしょう。 diffserv のコードに skb->tc_index というフィール ドを含む sk_buff という構造体があるのは、要するにこのためなのです。ここ には初期クラス選別の結果が保存され、これが DS 処理のいくつかの場所で用 いられることになります。 最初 skb->tc_index の値は、すべての到着パケットの IP ヘッダの DS フィー ルドから抽出され、 DSMARK qdisk によって設定されます。また、cls_tcindex 選別器は、 skb->tcindex の全体を読み、その結果をクラス選別に用います。 しかし、まず最初に、DSMARK qdisc のコマンドとパラメータを見てみましょう 。 ┌──────────────────────────────────────┐ │... dsmark indices INDICES [ default_index DEFAULT_INDEX ] [ set_tc_index ] │ └──────────────────────────────────────┘ これらのパラメータの意味するものはなんでしょうか? ・ indices: (mask,value) ペアからなるテーブルのサイズ。最大値は 2^n (ただし n>=0) です。 ・ Default_index: クラス選別器がまったくマッチを見つけられなかった場合 のデフォルトとなる、テーブルエントリのインデックス。 ・ Set_tc_index: dsmark qdisc に、DS フィールドの値を取得して skb-> tc_index へ保存するよう指示します。 では DSMARK のプロセスを見てみましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.3.5. SCH_DSMARK の動作 この qdisc は次のような段階からなります: ・ qdisc コマンドで set_tc_index を宣言していた場合は、 DS フィールド が抽出されて skb->tc_index 変数に保存される。 ・ クラス選別器が起動する。このクラス選別器はクラス ID を返し、これは skb->tc_index 変数に保存される。マッチするフィルタが見つからないと 、 default_index オプションを見て、どの classID を保存するか決める 。 set_tc_index と default_index のいずれも指定していなかった場合の 結果は未定義となる。 ・ 内部の qdisc に送られた後も、このフィルタの結果を再利用できる。内部 qdisc が返した classid は skb->tc_index に保存される。この値は、将 来 mask-value テーブルのインデックスとして利用できる。最終的にこの パケットに適用される結果は、次で示す操作の結果となる。 ┌────────────────────────────┐ │New_Ds_field = ( Old_DS_field & mask ) | value │ └────────────────────────────┘ ・ よって新たな値は、 ds_field とマスク値の AND を取り、続いてその結果 を value パラメータと OR したものになる。このプロセスを理解するには 次の図を見てください。 ┌────────────────────────────────────────────┐ │ skb->ihp->tos │ │- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > │ │ | | ^ │ │ | -- If you declare set_tc_index, we set DS | | <-----May change │ │ | value into skb->tc_index variable | |O DS field │ │ | A| |R │ │ +-|-+ +------+ +---+-+ Internal +-+ +---N|-----|----+ │ │ | | | | tc |--->| | |--> . . . -->| | | D| | | │ │ | | |----->|index |--->| | | Qdisc | |---->| v | | │ │ | | | |filter|--->| | | +---------------+ | ---->(mask,value) | │ │-->| O | +------+ +-|-+--------------^----+ / | (. , .) | │ │ | | | ^ | | | | (. , .) | │ │ | | +----------|---------|----------------|-------|--+ (. , .) | │ │ | | sch_dsmark | | | | | │ │ +-|------------|---------|----------------|-------|------------------+ │ │ | | | <- tc_index -> | | │ │ | |(read) | may change | | <--------------Index to the │ │ | | | | | (mask,value) │ │ v | v v | pairs table │ │- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -> │ │ skb->tc_index │ └────────────────────────────────────────────┘ マーキングはどのように行うのでしょうか。単に再マークしたいクラスのマス クと値を変えるだけです。次のコードを見てください。 ┌──────────────────────────────────┐ │tc class change dev eth0 classid 1:1 dsmark mask 0x3 value 0xb8 │ └──────────────────────────────────┘ これはハッシュテーブルの (mask,value) ペアを変更し、 class 1:1 に属して いるパケットを再マークします。 (mask,value) は最初デフォルトの値を取る (以下の表を参照) ので、この値は「変更」しなければなりません。 これで、TC_INDEX フィルタの動作と、これをどのように利用するかを説明しま した。なおまた、TC_INDEX フィルタは DS サービスに含まれているのとは別の 設定で用いることも可能です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.3.6. TC_INDEX フィルタ TC_INDEX フィルタを宣言する基本的なコマンドは次の通りです: ┌──────────────────────────────────┐ │... tcindex [ hash SIZE ] [ mask MASK ] [ shift SHIFT ] │ │ [ pass_on | fall_through ] │ │ [ classid CLASSID ] [ police POLICE_SPEC ] │ └──────────────────────────────────┘ 次に、TC_INDEX の動作モードを説明する例を示します。強調された単語に注意 してください: ┌──────────────────────────────────────────────────────────────────────────┐ │tc qdisc add dev eth0 handle 1:0 root dsmark indices 64 set_tc_index │ │ │ │tc filter add dev eth0 parent 1:0 protocol ip prio 1 tcindex mask 0xfc shift 2 │ │ │ │tc qdisc add dev eth0 parent 1:0 handle 2:0 cbq bandwidth 10Mbit cell 8 avpkt 1000 mpu 64 │ │ │ │# EF トラフィッククラス │ │ │ │tc class add dev eth0 parent 2:0 classid 2:1 cbq bandwidth 10Mbit rate 1500Kbit avpkt 1000 prio 1 bounded isolated allot 1514 weight 1 maxburst 10 │ │ │ │# EF トラフック用の pfifo qdisc │ │ │ │tc qdisc add dev eth0 parent 2:1 pfifo limit 5 │ │ │ │tc filter add dev eth0 parent 2:0 protocol ip prio 1 handle 0x2e tcindex classid 2:1 pass_on │ └──────────────────────────────────────────────────────────────────────────┘ (このコードは完全ではありません。 iproute2 配布にある EFCBQ の例から抜 粋したものにすぎません)。 まず、EF とマークされたパケットが到着したとしましょう。 RFC2598 を読む と、EF トラフィックに推奨される DSCP の値は 101110 です。つまり DS フィ ールドは 10111000 (TOS バイトの最低位ビットは DS では用いられないことに 注意)、あるいは 16 進では 0xb8 となります。 ┌─────────────────────────────────────────┐ │ TC INDEX │ │ FILTER │ │ +---+ +-------+ +---+-+ +------+ +-+ +-------+ │ │ | | | | | | | |FILTER| +-+ +-+ | | | | │ │ | |----->| MASK | -> | | | -> |HANDLE|->| | | | -> | | -> | | │ │ | | . | =0xfc | | | | |0x2E | | +----+ | | | | | │ │ | | . | | | | | +------+ +--------+ | | | | │ │ | | . | | | | | | | | | │ │-->| | . | SHIFT | | | | | | | |--> │ │ | | . | =2 | | | +----------------------------+ | | | │ │ | | | | | | CBQ 2:0 | | | │ │ | | +-------+ +---+--------------------------------+ | | │ │ | | | | │ │ | +-------------------------------------------------------------+ | │ │ | DSMARK 1:0 | │ │ +-------------------------------------------------------------------------+ │ └─────────────────────────────────────────┘ パケットが到着すると、DS フィールドには 0xb8 という値が設定されます。先 に説明した通り、1:0 という id の dsmark qdisc は、 DS フィールドを抽出 して skb->tc_index 変数に保存します。この例における次の段階は、この qdisc に関連付けられたフィルタに対応します (例の 2 行目)。これは次の操 作を実行します。 ┌──────────────────────────────────┐ │Value1 = skb->tc_index & MASK │ │Key = Value1 >> SHIFT │ └──────────────────────────────────┘ この例では MASC=0xFC で SHIFT=2 です。 ┌──────────────────────────────────┐ │Value1 = 10111000 & 11111100 = 10111000 │ │Key = 10111000 >> 2 = 00101110 -> 0x2E (16 進) │ └──────────────────────────────────┘ 返り値は qdisc の内部フィルタハンドル (この例では id が 2:0 のもの) に 対応します。この id を持ったフィルタが実際に存在すれば、制限条件と測定 条件が (フィルタにこれらが含まれていれば) 調査され、 classid が返され (我々の例では 2:1)、 skb->tc_index 変数に保存されます。 しかしこの id を持つフィルタが見つからなければ、結果は fall_through フ ラグの宣言に依存します。宣言されていれば、value キーが class id として 返されます。宣言されていないとエラーが返され、プロセスは残りのフィルタ へ続きます。 fall_through フラグの利用には注意が必要です。 skb-> tc_index 変数の値とクラス id の間に単純な関係があれば、これは行われてし まいます。 説明が必要な残りのパラメータは、hash と pass_on でしょうか。 hash はハ ッシュテーブルサイズに関連しています。 pass_on は、このフィルタの結果と 等しい class id が無い場合は次のフィルタを用いる、ということを示すため に用いられます。デフォルトの動作は fall_through です (次表を参照)。 最後に、TCINDEX の各パラメータに設定できる値を一覧しておきましょう: ┌──────────────────────────────────┐ │TC Name Value Default │ │----------------------------------------------------------------- │ │Hash 1...0x10000 Implementation dependent │ │Mask 0...0xffff 0xffff │ │Shift 0...15 0 │ │Fall through / Pass_on Flag Fall_through │ │Classid Major:minor None │ │Police ..... None │ └──────────────────────────────────┘ このフィルタは非常に強力です。あらゆる可能性を探るためには、この力が必 要なのです。ところで、このフィルタは DiffServ の設定でのみ用いられるわ けではなく、他の形式におけるフィルタとしても利用できます。 iproute2 配布に含まれている DiffServ の例をすべて見ておくことをお奨めし ます。ここの文章も、できるだけ早く補完するつもりです。ところで、ここで 説明した内容は、多くのテストの結果です。もしどこかで間違っていたら、指 摘してくださると幸いです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.4. 入口 (ingress) qdisc ここまで説明してきた qdisc は、すべて出口 (egress) qdisc です。しかしイ ンターフェースには入口 (ingress) qdisc を持たせることもできます。これは 、パケットをネットワークアダプタに送るのではなく、アダプタを経由して到 着したパケットに tc フィルタをかけるためのものです。パケットがローカル 宛のものか、フォワードされるものかは問われません。 tc フィルタにはトークンバケツフィルタが完全に実装されており、またカーネ ルの流量評価器によってマッチを行うこともできますから、たくさんの機能が 実現可能です。実際に、流入トラフィックを IP スタックに入る以前のところ で制限することさえできます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.4.1. パラメータと使い方 入口 qdisc 自体は、どのようなパラメータも要求しません。この qdisc は他 とは異なり、デバイスの root を占有しません。次のようにしてアタッチしま す。 ┌──────────────────────────────────┐ │# tc qdisc add dev eth0 ingress │ └──────────────────────────────────┘ よってこの入口 qdisc と同時に、他の (送信で用いる) qdisc をこのデバイス に適用することもできます。 入口 qdisc の (ややわざとらしい) 利用例については、クックブックを参照し てください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.5. ランダム初期検知 (Random Early Detection: RED) この節は、バンド幅が 100メガビット以上になるような、バックボーンの経路 に関する解説を意図して書かれました。自宅の ADSL モデムなどとは異なるア プローチが必要になります。 インターネットにおけるルータのキューの通常の動作は、 tail-drop と呼ばれ ます。 tail-drop は、ある量まではキューを行い、「溢れた」トラフィックを すべて破棄します。これは非常に不公平であり、再送の衝突 (retransmit synchronization) の原因にもなります。再送の衝突が起こると、限界に達した ルータで突然の破棄がバースト的に生じ、それがまたしばらく後に再送のバー ストを生じさせ、混雑したルータはまた限界に達することになります。 接続における一時的な混雑に対処するため、バックボーンルータでは大きなキ ューが用意されていることが多いです。しかし残念ながら、このようなキュー はスループットには良いのですが、遅延 (latency) が大きくなりますし、 TCP 接続では混雑時のバーストが非常に大きくなってしまいます。 この tail-drop の特性は、インターネットにおいてだんだん厄介な問題になっ てきています。ネットワークに優しくないアプリケーションの利用が増加して きたからです。このため Linux カーネルでは RED が提供されています (RED は Random Early Detection の略。またその動作から、 Random Early Drop と も呼ばれます)。 RED はこの問題についての万能薬ではありません。指数的な backoff を正しく 実装していないアプリケーションは、いずれにしても不公平にバンド幅を獲得 してしまうことになります。しかし RED を用いれば、スループットや遅延に関 して、他の接続に大きな害を与えることはなくなります。 RED は、ハードリミットに達する前に、フローからパケットを統計的に破棄し ます。こうすると混雑したバックボーンの接続を、より穏やかに減速でき、再 送の衝突を防ぐことができます。これはキューのサイズを小さく、遅延を一定 に保ちつつ、早めにパケットを破棄するので、TCP が自分の「公正な」速度を より早く発見するのにも役立ちます。ある特定の接続においてパケットが破棄 される確率は、送ったパケットの数にではなく、バンド幅の利用率に比例しま す。 公平なキューイングで必要とされる、複雑なセッションごとの状態追跡が無理 であるようなバックボーンに対し、 RED は適したキューです。 RED を使うには、3 つのパラメータ min, max, burst を決める必要があります 。 min は破棄を始める際のキューサイズ (の最小値)、 max はこのアルゴリズ ムで留めておきたいソフトリミット (最大値)、 burst は「バースト通過」が 可能な最大パケット数です。 min を計算するには、基本キューイング遅延 (base queueing latency) の最大 許容値にバンド幅をかけます。例えば 64kbit/s の ISDN 接続に対し、基本キ ューイング遅延を 200ms まで認める場合、min は 1600 バイトとなります。 min を小さくしすぎるとスループットは悪化しますし、大きくしすぎると latency が劣化します。なお min を小さくしても、遅い接続で対話的セッショ ンの反応を良くするために MTU を小さくするのと同じ効果は得られません。 衝突を防ぐには、max は少なくとも min の 2 倍にしなければなりません。遅 い接続で min も小さいときは、おそらく max は min の 4 倍以上にするほう が良いでしょう。 burst は RED アルゴリズムのバーストに対する反応を決めます。 burst は min/avpkt より大きくしなければなりません。私が実験したところでは、 (min+min+max)/(3*avpkt) でうまく動作するようです。 また、limit と avpkt の設定も必要です。 limit は安全弁となる値で、キュ ーの中身が limit バイトを越えると、 RED は tail-drop に「変化」します。 私は大体の場合、limit を max の 8 倍に設定します。avpkt はパケットサイ ズの平均値です。 MTU が 1500 バイトの高速なインターネット接続では、 1000 にしておけばよいでしょう。 技術的な情報は、 Sally Floyd と Van Jacobson による RED キューイングの 論文 をご覧ください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.6. 汎用ランダム初期検知 (Generic Random Early Detection) GRED については、あまりわかっていることはありません。 GRED はいくつか内 部キューを持っているようで、それらは DiffServ の tcindex に基づいて選択 されているようです。こちら のスライドによれば、GRED は Cisco の 'Distributed Weighted RED' の機能や、Dave Clark の RIO のような機能を持っているようです。 各仮想キューには、それぞれ固有の破棄パラメータを指定できます。 FIXME: Jamel か Werner に、もうちょっと教えてもらわないと。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.7. VC/ATM エミュレーション これは Werner Almesberger による割に有名なプロジェクトで、仮想回路 (Virtual Circuits) を TCP/IP ソケット上に構築しようというものです。 Virtual Circuit は、ATM ネットワーク理論の概念です。 より詳しくは、ATM on Linux ホームページ を参照してください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.8. 重み付きラウンドロビン (Weighted Round Robin: WRR) この qdisc は標準のカーネルには入っていませんが、こちら からダウンロードできます。現在この qdisc は Linux 2.2 でのみテストされていますが、おそらく 2.4/2.5 カーネルでの作業もなさ れるでしょう。 WRR qdisc は、重み付きラウンドロビンの仕組みを使って、バンド幅を自分の 持つ複数のクラスに分散させます。つまり WRR qdisc は、CBQ qdisc のように 、自分の配下にクラスを持ち、それぞれには任意の qdisc を接続できます。要 求を行ったクラスには、それぞれに付けられた重みに比例したバンド幅があて がわれます。重みは tc プログラムを用いて手動で設定できます。しかし、大 量のデータを転送させたクラスの重みを自動的に減らすようにもできます。 この qdisc には組み込みのクラス選別器があり、異なるマシンとの間で送信・ 受信したパケットを、それぞれ別々のクラスに割り当てます。 MAC または IP と、発信元アドレスまたは送信先アドレスが利用できます。ただし MAC アドレ スは Linux マシンがイーサネットブリッジとして動作している場合でのみ利用 できます。各クラスは、あるマシンからのパケットが現れると、自動的にその マシンに対して割り当てられます。 この qdisc は、寮のように、たくさんのそれぞれ独立したマシンがひとつのイ ンターネット接続を共有している場合には、とても便利でしょう。このような サイトで、望む動作をさせるためのスクリプト群は、 WRR の配布アーカイブに 含まれています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 15. クックブック この章は、問題解決の助けとなる「クックブック」が収められています。ただ しクックブックは理解の代わりになるものではありません。ですので試したら 何が起きているかを理解するように心がけてください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.1. SLA の異なる複数のサイトを動作させる いくつかのやり方があります。 Apache でもこの機能をモジュールでサポート していますが、ここでは Linux を使ってできること、他のサービスでも同様に 適用できることを示します。これらのコマンドは、この文書の末尾に示す Jamal Hadi の発表から拝借してきたものです。 http, ftp, ストリーミングオーディオを使っている 2 つの顧客があり、それ ぞれに限られた量のバンド幅を販売したいとしましょう。これはサーバ自身で 行います。 顧客 A には最大 2 メガビットを、顧客 B には 5 メガビットを割り当てます 。これらの顧客は、サーバに仮想 IP アドレスを作って分離します。 ┌──────────────────────────────────┐ │# ip address add 188.177.166.1 dev eth0 │ │# ip address add 188.177.166.2 dev eth0 │ └──────────────────────────────────┘ それぞれのサーバに適切な IP アドレスを割り当てるのは読者にお任せします 。有名なデーモンは、ほぼ間違いなくこの機能をサポートしています。 まず eth0 に CBQ qdisc をアタッチします。 ┌────────────────────────────────────────┐ │# tc qdisc add dev eth0 root handle 1: cbq bandwidth 10Mbit cell 8 avpkt 1000 \ │ │ mpu 64 │ └────────────────────────────────────────┘ 続いて各顧客のクラスを作ります。 ┌──────────────────────────────────────┐ │# tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate \ │ │ 2MBit avpkt 1000 prio 5 bounded isolated allot 1514 weight 1 maxburst 21 │ │# tc class add dev eth0 parent 1:0 classid 1:2 cbq bandwidth 10Mbit rate \ │ │ 5Mbit avpkt 1000 prio 5 bounded isolated allot 1514 weight 1 maxburst 21 │ └──────────────────────────────────────┘ ここで 2 つのクラスそれぞれにフィルタを追加します。 ┌────────────────────────────────────────┐ │##FIXME: この行の意味と動作は? divisor とは? │ │##FIXME: divisor はハッシュテーブルとバケツの数に何か関係があるようだ -ahu │ │# tc filter add dev eth0 parent 1:0 protocol ip prio 5 handle 1: u32 divisor 1 │ │# tc filter add dev eth0 parent 1:0 prio 5 u32 match ip src 188.177.166.1 │ │ flowid 1:1 │ │# tc filter add dev eth0 parent 1:0 prio 5 u32 match ip src 188.177.166.2 │ │ flowid 1:2 │ └────────────────────────────────────────┘ これで終了です。 FIXME: なぜトークンバケツフィルタが不要なのか? どこかでデフォルトの pfifo_fast に落ちているのか? ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.2. ホストを SYN フラッドから守る Alexey の iproute 文書からとった内容を netfilter に合わせ、パスを調整し たものです。これを用いる場合には、数値をご自分のシステムにあわせて修正 してください。 ネットワーク全体を守りたい場合は、このスクリプトは読み飛ばしてください 。これは単一のホストを守るためのものです。 これを 2.4.0 で動作させるには、最新版の iproute2 が必要なようです。 ┌─────────────────────────────────────┐ │#! /bin/sh -x │ │# │ │# 入口 (ingress) の機能を用いたサンプルスクリプト │ │# このスクリプトでは、到着する SYN を制限する例を示します。 │ │# TCP-SYN 攻撃に対する防御として有用です。SYN に対してより │ │# 強力な機能 (例えばサブネットを追加するなど) を追加した │ │# ければ IPchains を使えます。 │ │# │ │# いろいろなユーティリティのパス。 │ │# 自分の環境にあわせてください。 │ │# │ │TC=/sbin/tc │ │IP=/sbin/ip │ │IPTABLES=/sbin/iptables │ │INDEV=eth2 │ │# │ │# $INDEV に到着した SYN パケットに MARK が 1 のタグを付けます。 │ │############################################################ │ │$iptables -A PREROUTING -i $INDEV -t mangle -p tcp --syn \ │ │ -j MARK --set-mark 1 │ │############################################################ │ │# │ │# 入口 qdisc を、この入口インターフェースにインストールします │ │############################################################ │ │$TC qdisc add dev $INDEV handle ffff: ingress │ │############################################################ │ │ │ │# │ │# │ │# SYN パケットは 40 バイト (320 ビット) なので、3 つの SYN │ │# は 960 ビット (およそ 1kビット) になります。よって帯域制限 │ │# として、到着 SYN は 3/秒 にします (実はあまり便利ではあり │ │# ません。しかし要点は押さえています -JHS │ │############################################################ │ │$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 1 fw \ │ │police rate 1kbit burst 40 mtu 9k drop flowid :1 │ │############################################################ │ │ │ │ │ │# │ │echo "---- qdisc parameters Ingress ----------" │ │$TC qdisc ls dev $INDEV │ │echo "---- Class parameters Ingress ----------" │ │$TC class ls dev $INDEV │ │echo "---- filter parameters Ingress ----------" │ │$TC filter ls dev $INDEV parent ffff: │ │ │ │# 入口 qdisc を削除します。 │ │#$TC qdisc del $INDEV ingress │ └─────────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.3. ICMP を帯域制限して dDoS を防ぐ 昨今のインターネットでは、分散利用不能攻撃 (distributed denaial of service attack: dDoS) が頭の痛い問題になっています。ネットワークに正し くフィルタリングと帯域制限をかけると、このような攻撃の対象 (および原因) となることをいずれも防げます。 ネットワークにフィルタをかけ、送信元アドレスがローカルでない IP パケッ トは、ネットワークを出て行けないようにします。こうすると、匿名でゴミを インターネットに送ることができなくなります。 帯域制限は、先に示したものと同様です。読者のメモリをリフレッシュするた めに、再び ASCII 図を示しましょう。 ┌────────────────────────────────────┐ │[The Internet] ------ [Linux router] --- [Office+ISP] │ │ eth1 eth0 │ └────────────────────────────────────┘ まず、あらかじめ必要な部分の設定です。 ┌───────────────────────────────────────┐ │# tc qdisc add dev eth0 root handle 10: cbq bandwidth 10Mbit avpkt 1000 │ │# tc class add dev eth0 parent 10:0 classid 10:1 cbq bandwidth 10Mbit rate \ │ │ 10Mbit allot 1514 prio 5 maxburst 20 avpkt 1000 │ └───────────────────────────────────────┘ インターフェースが 100Mビット (あるいはそれ以上) だったら、 3 つの数値 を調整してください。次に、ICMP トラフィックをどの程度許可するかを決めま す。tcpdump で測定を行ってみて、しばらく結果をファイルに記録し、どの程 度の ICMP がネットワークを流通しているか見てみましょう。 snapshot の長 さを増やすのを忘れないように。 測定ができない場合は、利用できるバンド幅の 5% にしてみるといいでしょう 。ではクラスを設定しましょう。 ┌────────────────────────────────────────┐ │# tc class add dev eth0 parent 10:1 classid 10:100 cbq bandwidth 10Mbit rate \ │ │ 100Kbit allot 1514 weight 800Kbit prio 5 maxburst 20 avpkt 250 \ │ │ bounded │ └────────────────────────────────────────┘ これで 100Kビットに制限しました。ではフィルタを追加して、 ICMP トラフィ ックをこのクラスに割り当てるようにしましょう。 ┌────────────────────────────────────┐ │# tc filter add dev eth0 parent 10:0 protocol ip prio 100 u32 match ip │ │ protocol 1 0xFF flowid 10:100 │ └────────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.4. 対話的トラフィックを優先する たくさんのデータが接続から出て行ったり入ってきたりしていると、 telnet や ssh で何らかのメンテナンス作業を行う際の反応が悪くなります。他のパケ ットがキー入力をブロックするのです。このような対話的なパケットを、こっ そりバルク転送トラフィックとは別の経路で通すことができたらすごいと思い ませんか? Linux ならできるのです。 前述のように、両方向のトラフィックを扱う必要があります。当然ながら、接 続の両端がいずれも Linux マシンであれば最高ですが、他の UNIX でも可能で す。お近くの Solaris/BSD グルに尋ねてみましょう。 標準の pfifo_fast スケジューラには 3 つの「バンド」があります。バンド 0 のトラフィックは先に送信され、その後バンド 1 と 2 のトラフィックが考慮 されます。よって、私たちの対話的トラフィックをバンド 0 に割り当てるのが ミソです! これらは (そろそろ obsolete になる) ipchains HOWTO からパクってきました 。 IP ヘッダには、あまり用いられない 4 つのビット、 Type of Service (TOS) ビットと呼ばれる領域があります。これらはパケットの扱いに影響します。4 つのビットはそれぞれ "Minimum Delay", "Maximum Throughput", "Maximum Reliability", "Minimum Cost" を意味します。これらのビットのうち、ひとつ だけが設定できます。 ipchains の TOS 修正コードの著者である Rob van Nieuwkerk は、次のように言っています。 私にとっては "Minimum Delay" が特に重要です。私は「対話的な」パケッ トには、上流のルータ (Linux) でこれを有効にしました。自分は 33.6k モデム接続の下流にいます。 Linux はパケットを 3 つのキューに優先付 けします。このようにして、私は大きなダウンロードをしている最中にも 、まあまあの対話性能を得ることができています。 最もよくある設定は、telnet と ftp の制御接続に "Minimum Delay" を用い、 FTP データに "Maximum Throughput" を用いるやり方です。これには上流のル ータで、次のようにします。 ┌──────────────────────────────────┐ │# iptables -A PREROUTING -t mangle -p tcp --sport telnet \ │ │ -j TOS --set-tos Minimize-Delay │ │# iptables -A PREROUTING -t mangle -p tcp --sport ftp \ │ │ -j TOS --set-tos Minimize-Delay │ │# iptables -A PREROUTING -t mangle -p tcp --sport ftp-data \ │ │ -j TOS --set-tos Maximize-Throughput │ └──────────────────────────────────┘ これだと、telnet 先のホストからローカルの PC へと向かうパケットにしか作 用しません。しかし逆方向はすでに設定されているのです。つまり telnet や ssh などでは、送信パケットの TOS フィールドを自動的に設定しているのです 。 これを行っていないアプリケーションがあっても、 netfilter を用いれば同じ ことができます。ローカルマシンで次のようにします。 ┌──────────────────────────────────┐ │# iptables -A OUTPUT -t mangle -p tcp --dport telnet \ │ │ -j TOS --set-tos Minimize-Delay │ │# iptables -A OUTPUT -t mangle -p tcp --dport ftp \ │ │ -j TOS --set-tos Minimize-Delay │ │# iptables -A OUTPUT -t mangle -p tcp --dport ftp-data \ │ │ -j TOS --set-tos Maximize-Throughput │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.5. netfilter, iproute2, ipchains, squid を用いた透過 Web キャッシュ この節は読者の Ram Narula (Internet for Education, Thailand) から寄せら れました。 Linux でこの目的を満たすための通常のテクニックは、おそらくポート 80 (web) の「外向き」トラフィックが squid の動作しているサーバにルーティン グされるようにした「後で」、ipchains を用いる方法でしょう。 外向きのポート 80 トラフィックを squid の動作しているサーバにルーティン グするには、3 つの方法が知られています。ここでは 4 番目の方法を紹介しま す。 ゲートウェイルータにやらせる ゲートウェイルータに、外向きで送信先ポートが 80 のパケットを squid サーバの IP アドレスに送らせることが可能な場合。 しかし これはルータに負荷をかけることになりますし、商用のルータではサポー トしていないこともあります。 レイヤ 4 スイッチを用いる レイヤ 4 スイッチは、この作業をまったく問題なく処理できます。 しかし レイヤ 4 スイッチの価格は非常に高く、たいていの場合は、(一般的なル ータ + 良い Linux サーバ) の合計よりも高いです。 キャッシュサーバをネットワークのゲートウェイにする すべてのトラフィックにキャッシュサーバを経由させることもできます。 しかし squid はかなりの CPU パワーを消費するため、これはかなりリスクが高い です。つまりネットワーク全体の性能が遅くなるかもしれませんし、万一 サーバがクラッシュしたら、誰もネットワークにアクセスできなくなりま す。 Linux+NetFilter ルータ NetFilter を用いると、別のテクニックが実装できます。つまり NetFilter に到達先ポートが 80 のパケットをマーキングさせ、 iproute2 を使って印のついたパケットを squid サーバにルーティングさせるのです 。 ┌──────────────────────────────────┐ │|----------------| │ │| 実装 | │ │|----------------| │ │ │ │ 用いたアドレス │ │ 10.0.0.1 naret (NetFilter サーバ) │ │ 10.0.0.2 silom (Squid サーバ) │ │ 10.0.0.3 donmuang (インターネットに接続しているルータ) │ │ 10.0.0.4 kaosarn (ネットワークの別のサーバ) │ │ 10.0.0.5 RAS │ │ 10.0.0.0/24 メインネットワーク │ │ 10.0.0.0/19 ネットワーク全体 │ │ │ │|---------------| │ │| ネットワーク図| │ │|---------------| │ │ │ │Internet │ │| │ │donmuang │ │| │ │------------hub/switch---------- │ │| | | | │ │naret silom kaosarn RAS etc. │ │ │ └──────────────────────────────────┘ まず naret を silom 以外のマシンのデフォルトゲートウェイにして、すべて のトラフィックが naret を経由するようにします。 silom のデフォルトゲー トウェイは donmuang (10.0.0.3) にします。さもないとこの環境では web ト ラフィックがループします。 (以前はこのネットワークのすべてのサーバでは 10.0.0.1 がデフォルトゲート ウェイで、これは donmuang ルータの以前の IP アドレスでした。よってここ では donmuang の IP アドレスを 10.0.0.3 に変更し、 naret の IP アドレス を 10.0.0.1 にしたのです) ┌──────────────────────────────────┐ │Silom │ │----- │ │-squid と ipchains を設定する │ │ │ └──────────────────────────────────┘ silom で squid サーバを設定し、透過キャッシュ/プロクシをサポートするよ うにします。デフォルトのポートは通常 3128 なので、ポート 80 へのトラフ ィックはローカルの 3128 にリダイレクトしなければ鳴りません。これは ipchains を用いて次のように書けます: ┌──────────────────────────────────────┐ │silom# ipchains -N allow1 │ │silom# ipchains -A allow1 -p TCP -s 10.0.0.0/19 -d 0/0 80 -j REDIRECT 3128 │ │silom# ipchains -I input -j allow1 │ │ │ └──────────────────────────────────────┘ あるいは netfilter の言葉では: ┌─────────────────────────────────────────────┐ │silom# iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128 │ │ │ └─────────────────────────────────────────────┘ (注意: 他のエントリもあるかもしれません) squid サーバの設定に関する詳しい情報は、 Squid FAQ のページ を参照してください。 このサーバでは ip forwarding を有効にしておいてください。またこのサーバ のデフォルトゲートウェイは、 donmuang ルータにしてください (naret では ありません)。 ┌──────────────────────────────────┐ │Naret │ │----- │ │-iptables と iproute2 の設定 │ │- icmp REDIRECT メッセージを無効にする (必要な場合) │ │ │ └──────────────────────────────────┘ 1. 送信先ポートが 80 のパケットに、値 2 でマークします。 ┌──────────────────────────────────┐ │naret# iptables -A PREROUTING -i eth0 -t mangle -p tcp --dport 80 \ │ │ -j MARK --set-mark 2 │ │ │ └──────────────────────────────────┘ 2. iproute2 を設定し、2 というマークのついたパケットを silom にルーテ ィングします。 ┌────────────────────────────────┐ │naret# echo 202 www.out >> /etc/iproute2/rt_tables │ │naret# ip rule add fwmark 2 table www.out │ │naret# ip route add default via 10.0.0.2 dev eth0 table www.out │ │naret# ip route flush cache │ │ │ └────────────────────────────────┘ donmuang と naret が同じサブネットにある場合は、 naret に icmp REDIRECT メッセージを送信させてはいけません。この場合はそうだったの で、次のコマンドで icmp REDIRECT を無効化しました。 ┌────────────────────────────────┐ │naret# echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects │ │naret# echo 0 > /proc/sys/net/ipv4/conf/default/send_redirects │ │naret# echo 0 > /proc/sys/net/ipv4/conf/eth0/send_redirects │ │ │ └────────────────────────────────┘ これで設定は完成です。調べてみましょう。 ┌───────────────────────────────────────────┐ │naret にて: │ │ │ │naret# iptables -t mangle -L │ │Chain PREROUTING (policy ACCEPT) │ │target prot opt source destination │ │MARK tcp -- anywhere anywhere tcp dpt:www MARK set 0x2 │ │ │ │Chain OUTPUT (policy ACCEPT) │ │target prot opt source destination │ │ │ │naret# ip rule ls │ │0: from all lookup local │ │32765: from all fwmark 2 lookup www.out │ │32766: from all lookup main │ │32767: from all lookup default │ │ │ │naret# ip route list table www.out │ │default via 203.114.224.8 dev eth0 │ │ │ │naret# ip route │ │10.0.0.1 dev eth0 scope link │ │10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.1 │ │127.0.0.0/8 dev lo scope link │ │default via 10.0.0.3 dev eth0 │ │ │ │(silom は上述の行のどれかに属していることを確認のこと。 │ │この場合は 10.0.0.0/24 の行です) │ │ │ │|------| │ │|-DONE-| │ │|------| │ │ │ └───────────────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.5.1. 実装後のトラフィックフロー図 ┌──────────────────────────────────┐ │|-----------------------------------------| │ │| 実装後のトラフィックフロー図 | │ │|-----------------------------------------| │ │ │ │INTERNET │ │/\ │ │|| │ │\/ │ │-----------------donmuang router--------------------- │ │/\ /\ || │ │|| || || │ │|| \/ || │ │naret silom || │ │*destination port 80 traffic=========>(cache) || │ │/\ || || │ │|| \/ \/ │ │\\===================================kaosarn, RAS, etc. │ └──────────────────────────────────┘ 通常の外向きの経路には余計に 1 hop が挟まるので、ネットワークは非対称に なっています。 ここには kaosarn とインターネットの間で送受信されるパケットを追跡してみ ましょう。 web/http トラフィック ┌─────────────────────────────────────┐ │ kaosarn の http リクエスト→naret→silom→donumuang→インターネット │ │ インターネットから到着したデータ→donmuang→kaosarn │ │ │ └─────────────────────────────────────┘ web/http 以外のトラフィック (telnet など) ┌───────────────────────────────┐ │ kaosarn の外向きデータ→naret→donumuang→インターネット │ │ インターネットから到着したデータ→donmuang→kaosarn │ │ │ └───────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.6. 経路ごとに MTU を設定し Path MTU Discovery 問題を回避する インターネットで大量のデータを送信するとき、一般的に大きなパケットを利 用するほうが効率よく動作します。各パケットは経路の決定を反映しますが、 1 メガバイトのファイルを送信する場合、できるだけ大きなパケットを使えば 約 700 パケットになりますし、デフォルトの最小サイズを使うと約 4000 にな ります。 1 パケットあたりのペイロードの最大値は 1460 バイトですが、この値はイン ターネットのあらゆる部分でサポートされているわけではありません。よって 接続を最適化するには、適用できる最大のパケットサイズを試行を通して見つ ける必要があります。 このプロセスは 'Path MTU Discovery' と呼ばれています。 MTU は 'Maximum Transfer Unit' の意味です。 ルータがひとまとめで送るには大きすぎるパケットを受信し、かつそのパケッ トで「フラグメント不可」ビットが立っていると、ルータは ICMP メッセージ を送り、この理由でパケットを破棄せざるを得なかったことを伝えます。送信 元のホストはこのヒントに対応し、より小さいパケットを送ります。これを繰 り返すことで、ある経路を経由する接続における、最適なパケットサイズを発 見できるのです。 これはうまく動作していたのですが、通信を混乱させることに心血を注ぐフー リガン達がインターネットを発見すると、状況が変わりました。管理者達は、 インターネットサービスの安全性・堅牢性の向上を行うため、 ICMP トラフィ ックを (間違ったやり方で) ブロックしたり絞ったりするようになってしまっ たのです。 これによって現在では、Path MTU Discovery が正しく動作する状況はどんどん 少なくなり、特定の経路では失敗するようになってしまいました。この場合 TCP/IP セッションがしばらくすると落ちるなど、妙な動作をします。 証拠はないのですが、私の経験では、この問題が生じた 2 つのサイトでは、い ずれもそのシステムの手前で Alteon Acedirectors を使っていました。この原 因については、より詳しいどなたかが手がかりを与えてくれると期待していま す。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.6.1. 解決法 このような問題のあるサイトに行き当たったら、手動設定で Path MTU discovery を無効にしなければなりません。以下は Koo van den Hout の書い たものです (少々編集しています): 問題はこのようなものです: 私は借りている ppp 接続の mtu/mru を 296 にしました。この接続は 33.6k しかなく、接続先のキューイングは操作で きなかったからです。 296 にすると、キーを押したときの反応が妥当な時 間に収まるようになりました。 また私の側では、(当然) Linux の、マスカレードルータが動作しています 。 最近私は「サーバ」と「ルータ」を別にしました。よってほとんどのアプ リケーションは、ルーティングを行っているのとは別のマシンで動作する ようになりました。 すると irc へのログインに問題が出るようになりました。たいへんです! あれこれ調べたところ、irc への接続はできており、'connected' も表示 されているとわかったのですが、 irc からの motd を受信できないのです 。何がおかしいかをチェックしたところ、以前から特定の web サイトへの 接続に、 MTU 関連で問題があったことを思い出しました。これは MTU を 296 に設定したときに出現したものでした。 irc サーバは、直接の動作に 関係のないトラフィックはすべてブロックしていました。 icmp もブロッ クの対象でした。 私は web サーバのオペレータに対しては、これが問題の原因になるのだと 納得させることができましたが、 irc サーバのオペレータはこの問題を修 正しようとはしませんでした。 そこで、私は外に出て行くマスカレードトラフィックに対して、より小さ な mtu を与えるようにしなければなりませんでした。ただしローカルのイ ーサネットトラフィックでは、通常の mtu を使いたかったのです (例えば nfs のトラフィックなど)。 解決法: ┌────────────────────────────────┐ │ip route add default via 10.0.0.1 mtu 296 │ └────────────────────────────────┘ (10.0.0.1 はデフォルトゲートウェイで、マスカレードルータの内側のア ドレス) 一般に、PMTU Discovery の設定を特定の経路に対して変更することは可能です 。例えば、あるサブネットでのみ問題があるのなら、次のようにするとよいで す。 ┌──────────────────────────────────┐ │ip route add 195.96.96.0/24 via 10.0.0.1 mtu 1000 │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.7. MSS クランプによって Path MTU Discovery 問題を回避する (ADSL, ケ ーブル, PPPoE, PPtP ユーザ向け) 先に説明があったように、Path MTU Discovery は、もう以前のようにはうまく 動作しません。ネットワークの経路のどこかに、MTU の制限 (<1500) があると わかっているような場合に、 PMTU Discovery がこれを発見してくれると期待 することはできません。 MTU 以外にも、パケットの最大サイズを設定する方法はあります。これは Maximum Segment Size と呼ばれています。これは SYN パケットの一部、TCP オプションのフィールドです。 最近の Linux カーネルと、いくつかの PPPoE ドライバ (特に Roaring Penguin のが素晴らしい) は、この MSS を「クランプする」機能を持っていま す。 この方法の長所は、MSS の値を設定すると、リモート側にも「この値より大き なパケットを送るな」ということをはっきり伝えられることです。この機構の 動作には ICMP トラフィックは必要ありません。 一方欠点は、これは明らかにその場しのぎだということです。この方法はパケ ットを修正しており、end-to-end の関係を保存しません。この点を除けば、こ の小技は多くの場合で使えますし、魔法のように効きます。 これを行うには、iptables-1.2.1a 以降と Linux 2.4.3 以降が必要です。基本 的なコマンドラインは次の通り: ┌──────────────────────────────────────────┐ │# iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu │ └──────────────────────────────────────────┘ これは接続に対する適切な MSS を計算します。もうちょっと勇気がある方、あ るいは自分の知識に確信がある方は、次のようにすることもできます。 ┌──────────────────────────────────────┐ │# iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 128│ └──────────────────────────────────────┘ これは先方に渡す SYN パケットの MSS を 128 にします。パケットの小さな VoIP を使っていて、 http の巨大なパケットが来ると音声通話が途切れてしま うような場合には、これを用いると良いでしょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.8. 究極のトラフィック調整器: 低遅延, 高速アップロード/ダウンロード 注意: このスクリプトは最近アップグレードされました。以前では、ネットワ ークの Linux クライアントに対してしか機能していませんでした! ですからネ ットワークに Windows マシンや Mac があり、それらでのダウンロードが他の マシンのアップロード時に速くないことにお気づきでしたら、更新されたほう がよいでしょう。 私は聖杯を作ろうと試みたのです: あらゆる場合に、対話的トラフィックの遅延は小さく保つ つまり、ファイルのダウンロードやアップロードが、SSH (あるいは telnet) には影響しない、ということです。これは最も重要な点で、たと え 200ms の遅延でも、作業には耐えがたいほどの遅さとなります。 アップロード・ダウンロード時にも、それなりの速度でネットサーフィンした い http は「バルク」トラフィックですが、他のトラフィックによる減速をあ まり酷くはさせたくありません。 アップロードがダウンロードを劣化させること (およびその逆) がないように する 上りのトラフィックがダウンロードの速度を落としてしまうというのは、 よくみられる現象です。 実はこれらのすべては、ほんの少々バンド幅を犠牲にすれば可能です。アップ ロード・ダウンロード・ssh がお互いを妨害し合うのは、ケーブルモデムや DSL モデムのような手元のアクセスデバイスに、大きなキューがあるためなの です。 次の節では、何が遅延の原因なのか、どうすればそれを修正できるかを、より 詳しく説明します。もしこの魔法の動作原理に関心がなければ、次は飛ばして 直接スクリプトに向かっても問題ありません。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.8.1. なぜデフォルトでは動作しないのか ISP は、自分達のベンチマークがダウンロードの速度のみによってなされるこ とを知っています。バンド幅以外に、ダウンロードの速度はパケットロスによ っても大きく影響されます。パケットロスは TCP/IP の性能を大きく低下させ るからです。キューを大きくするとパケットロスを防ぐのに役立ち、ダウンロ ードの速度を大きくできます。よって ISP は大きなキューを使うように設定し ています。 しかしこのような大きなキューは対話性には害となります。キーストロークは まず上りのキューを通過しなければならず、これはリモートホストに届く前に 数秒 (!) も留まることがあるのです。その後、返信パケットが戻り、スクリー ンに表示される前には、そのパケットも (ISP にある) 下りのキューを通過し なければならないのです。 この HOWTO は、いろいろな点でキューを修正・処理しなければならないことを 教えています。しかし悲しいことに、すべてのキューにアクセスできるとは限 りません。 ISP にあるキューはおそらくまったく触れません。一方、上りのキ ューはおそらく手元のケーブルモデムか DSL デバイスの内部にあるでしょう。 設定できる場合もできない場合もあるでしょうが、たいていはダメだと思いま す。 ではどうしましょう? これらのキューは制御できないのですから、殺してしま わなければなりません。キューは Linux ルータに移動するのです。ありがたい ことに、これは可能です。 アップロードの速度を制限する アップロードの速度を、実際に利用できる値よりわずかに絞ることによっ て、モデムのキューにはデータが溜まりません。これによってキューは Linux に移動します。 ダウンロードの速度を制限する インターネットのデータ転送速度を制御することは不可能なので、こちら はやや技巧的になります。非常に頻繁にくるパケットを破棄すれば、 TCP/ IP は減速して望む速度になってくれます。不必要にトラフィックを破棄す ることはしたくありませんから、「バースト (burst)」の許可サイズの設 定は、大きくしておくといいでしょう。 さて、これらを行うと、下りのキューを完全に (短いバーストを除いて) 無効 にでき、下りのキューの管理にも、Linux の持つ能力をすべて利用できるよう になります。 あと行わなければならないことは、対話的なトラフィックを確実に上りキュー の先頭に押し出すことです。またアップロードがダウンロードの害にならない ように、 ACK パケットもキューの先頭に出します。これは、両方向でバルクト ラフィックを発生させたときに通常見られる、激しいスローダウンの原因なの です。下りトラフィックの ACK (応答) は通常の上りトラフィックより優先さ せる必要があり、さもないとこの処理は遅延させられてしまいます。 これらをすべて設定すれば、オランダの xs4all における excellent ADSL 接 続では、次のような測定結果が得られます。 ┌──────────────────────────────────┐ │基本的な遅延: │ │round-trip min/avg/max = 14.4/17.1/21.7 ms │ │ │ │トラフィック調整器なし、ダウンロード中: │ │round-trip min/avg/max = 560.9/573.6/586.4 ms │ │ │ │トラフィック調整器なし、アップロード中: │ │round-trip min/avg/max = 2041.4/2332.1/2427.6 ms │ │ │ │トラフィック調整器あり、220kbit/s のアップロード中: │ │round-trip min/avg/max = 15.7/51.8/79.9 ms │ │ │ │トラフィック調整器あり、850kbit/s のダウンロード中: │ │round-trip min/avg/max = 20.4/46.9/74.0 ms │ └──────────────────────────────────┘ アップロード時のダウンロードは、最高速度の約 80% になります。アップロー ドは約 90% になります。ただし遅延は 850 ms に跳ね上がり、この原因はまだ 調査中です。 このスクリプトから得られる効果は、実際の接続速度に大きく依存します。最 高速度でアップロードしていると、キーストロークのパケットの前には必ず 1 パケットが存在します。これが達成できる遅延の下限値です。MTU を上り速度 で割れば計算できます。通常はこの値よりは少々大きくなります。より効果を 上げたければ MTU を小さくしてください! 次に、このスクリプトの 2 つの版を示します。ひとつは Devik の優れた HTB を使ったもので、もうひとつは Linux カーネルに (HTB とは違い) 最初から入 っている CBQ を使ったものです。両方ともテスト済みで、うまく動作します。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.8.2. 実際のスクリプト (CBQ) すべてのカーネルで動作します。 CBQ qdisc の内部に 2 つの確率的不偏キュ ーを置き、複数のバルクストリームがお互いを完全に殺してしまわないように しています。 下りトラフィックはトークンバケツフィルタを用いた tc フィルタで制限して います。 'tc class add .. classid 1:20' で始まる行に 'bounded' を追加すると、こ のスクリプトを改善できるかも知れません。 MTU を小さくする場合は、allot と avpkt の数も減らすこと! ┌───────────────────────────────────────┐ │#!/bin/bash │ │ │ │# 自宅のインターネット接続用の究極設定 │ │# │ │# │ │# 次のパラメータを、実際のダウンロード・アップロード速度より │ │# 少々小さくしてください (キロバイト単位) │ │DOWNLINK=800 │ │UPLINK=220 │ │DEV=ppp0 │ │ │ │# 既存の下り・上りの qdisc を削除。エラーは隠す。 │ │tc qdisc del dev $DEV root 2> /dev/null > /dev/null │ │tc qdisc del dev $DEV ingress 2> /dev/null > /dev/null │ │ │ │###### 上り │ │ │ │# root CBQ をインストール │ │ │ │tc qdisc add dev $DEV root handle 1: cbq avpkt 1000 bandwidth 10mbit │ │ │ │# あらゆる物を $UPLINK 速度の内部に収める - これは DSL モデムの │ │# 巨大なキューを無効にし、遅延が出ないようにします: │ │# メインクラス │ │ │ │tc class add dev $DEV parent 1: classid 1:1 cbq rate ${UPLINK}kbit \ │ │allot 1500 prio 5 bounded isolated │ │ │ │# 高優先度クラス 1:10 │ │ │ │tc class add dev $DEV parent 1:1 classid 1:10 cbq rate ${UPLINK}kbit \ │ │ allot 1600 prio 1 avpkt 1000 │ │ │ │# バルクおよびデフォルトのクラス 1:20。ややトラフィックが少なく、 │ │# 優先度が低くなります │ │ │ │tc class add dev $DEV parent 1:1 classid 1:20 cbq rate $[9*$UPLINK/10]kbit \ │ │ allot 1600 prio 2 avpkt 1000 │ │ │ │# 両者に確率的不偏キューをあてがいます │ │tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10 │ │tc qdisc add dev $DEV parent 1:20 handle 20: sfq perturb 10 │ │ │ │# ここからフィルタ │ │# TOS が Minimum Delay (scp 以外の ssh) のものを 1:10 へ: │ │tc filter add dev $DEV parent 1:0 protocol ip prio 10 u32 \ │ │ match ip tos 0x10 0xff flowid 1:10 │ │ │ │# ICMP (ip プロトコル 1) を対話的クラス 1:10 へ。 │ │# 測定と相手への通知ができるようになります: │ │tc filter add dev $DEV parent 1:0 protocol ip prio 11 u32 \ │ │ match ip protocol 1 0xff flowid 1:10 │ │ │ │# アップロード時のダウンロードを高速化するために、ACK パケットを │ │# 対話的クラスへ: │ │ │ │tc filter add dev $DEV parent 1: protocol ip prio 12 u32 \ │ │ match ip protocol 6 0xff \ │ │ match u8 0x05 0x0f at 0 \ │ │ match u16 0x0000 0xffc0 at 2 \ │ │ match u8 0x10 0xff at 33 \ │ │ flowid 1:10 │ │ │ │# 残りは「非対話的」つまり「バルク」なので 1:20 へ │ │ │ │tc filter add dev $DEV parent 1: protocol ip prio 13 u32 \ │ │ match ip dst 0.0.0.0/0 flowid 1:20 │ │ │ │###### 下り │ │# ダウンロードを実際の速度よりも少々遅くして、ISP でのキューを │ │# 無効にする。調整してできるだけ大きくしてください。 │ │# ISP は大きなサイズのダウンロードを高速化するために「巨大」な │ │# キューを持つことが多い │ │# │ │# 入口監視制限を追加: │ │ │ │tc qdisc add dev $DEV handle ffff: ingress │ │ │ │# 「すべて」(0.0.0.0/0) をこちらへフィルタし、速すぎるものは │ │# 破棄する: │ │ │ │tc filter add dev $DEV parent ffff: protocol ip prio 50 u32 match ip src \ │ │ 0.0.0.0/0 police rate ${DOWNLINK}kbit burst 10k drop flowid :1 │ └───────────────────────────────────────┘ このスクリプトを ppp の接続時に実行したければ、 /etc/ppp/ip-up.d にコピ ーしてください。 最後の 2 行でエラーになる場合は、 tc ツールを新しいバージョンにアップデ ートしてください! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.8.3. 実際のスクリプト (HTB) このスクリプトは素晴らしい HTB キューを用い、すべての目的を達成していま す。 HTB の章を見てください。カーネルへのパッチ当て作業に十分見合いま す! ┌───────────────────────────────────────┐ │#!/bin/bash │ │ │ │# 自宅のインターネット接続用の究極設定 │ │# │ │# │ │# 次のパラメータを、実際のダウンロード・アップロード速度より │ │# 少々小さくしてください (キロバイト単位) │ │DOWNLINK=800 │ │UPLINK=220 │ │DEV=ppp0 │ │ │ │# 既存の下り・上りの qdisc を削除。エラーは隠す。 │ │tc qdisc del dev $DEV root 2> /dev/null > /dev/null │ │tc qdisc del dev $DEV ingress 2> /dev/null > /dev/null │ │ │ │###### 上り │ │ │ │# root HTB をインストールし、デフォルトのトラフィックを 1:20 へ: │ │ │ │tc qdisc add dev $DEV root handle 1: htb default 20 │ │ │ │# あらゆる物を $UPLINK 速度の内部に収める - これは DSL モデムの │ │# 巨大なキューを無効にし、遅延が出ないようにします: │ │ │ │tc class add dev $DEV parent 1: classid 1:1 htb rate ${UPLINK}kbit burst 6k │ │ │ │# 高優先度クラス 1:10 │ │ │ │tc class add dev $DEV parent 1:1 classid 1:10 htb rate ${UPLINK}kbit \ │ │ burst 6k prio 1 │ │ │ │# バルクおよびデフォルトのクラス 1:20。ややトラフィックが少なく、 │ │# 優先度が低くなります │ │ │ │tc class add dev $DEV parent 1:1 classid 1:20 htb rate $[9*$UPLINK/10]kbit \ │ │ burst 6k prio 2 │ │ │ │# 両者に確率的不偏キューをあてがいます │ │tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10 │ │tc qdisc add dev $DEV parent 1:20 handle 20: sfq perturb 10 │ │ │ │# TOS が Minimum Delay (scp 以外の ssh) のものを 1:10 へ: │ │tc filter add dev $DEV parent 1:0 protocol ip prio 10 u32 \ │ │ match ip tos 0x10 0xff flowid 1:10 │ │ │ │# ICMP (ip プロトコル 1) を対話的クラス 1:10 へ。 │ │# 測定と相手への通知ができるようになります: │ │tc filter add dev $DEV parent 1:0 protocol ip prio 10 u32 \ │ │ match ip protocol 1 0xff flowid 1:10 │ │ │ │# アップロード時のダウンロードを高速化するために、ACK パケットを │ │# 対話的クラスへ: │ │ │ │tc filter add dev $DEV parent 1: protocol ip prio 10 u32 \ │ │ match ip protocol 6 0xff \ │ │ match u8 0x05 0x0f at 0 \ │ │ match u16 0x0000 0xffc0 at 2 \ │ │ match u8 0x10 0xff at 33 \ │ │ flowid 1:10 │ │ │ │# 残りは「非対話的」つまり「バルク」なので 1:20 へ │ │ │ │ │ │###### 下り │ │# ダウンロードを実際の速度よりも少々遅くして、ISP でのキューを │ │# 無効にする。調整してできるだけ大きくしてください。 │ │# ISP は大きなサイズのダウンロードを高速化するために「巨大」な │ │# キューを持つことが多い │ │# │ │# 入口監視制限を追加: │ │ │ │tc qdisc add dev $DEV handle ffff: ingress │ │ │ │# 「すべて」(0.0.0.0/0) をこちらへフィルタし、速すぎるものは │ │# 破棄する: │ │ │ │tc filter add dev $DEV parent ffff: protocol ip prio 50 u32 match ip src \ │ │ 0.0.0.0/0 police rate ${DOWNLINK}kbit burst 10k drop flowid :1 │ └───────────────────────────────────────┘ このスクリプトを ppp の接続時に実行したければ、 /etc/ppp/ip-up.d にコピ ーしてください。 最後の 2 行でエラーになる場合は、 tc ツールを新しいバージョンにアップデ ートしてください! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.9. 単一のホストまたはネットワークの速度制限 この問題は他の場所や私たちの man ページで非常に詳細に解説してあるのです が、質問がたくさん寄せられますし、幸いなことにトラフィック制御の完全な 理解を必要としない簡単な答えが存在します。 この 3 行のスクリプトが仕事をしてくれます: ┌────────────────────────────────────┐ │# tc qdisc add dev $DEV root handle 1: cbq avpkt 1000 bandwidth 10mbit │ │ │ │# tc class add dev $DEV parent 1: classid 1:1 cbq rate 512kbit \ │ │allot 1500 prio 5 bounded isolated │ │ │ │# tc filter add dev $DEV parent 1: protocol ip prio 16 u32 \ │ │match ip dst 195.96.96.97 flowid 1:1 │ │ │ └────────────────────────────────────┘ 最初の行はクラスベースのキューをインターフェースにインストールし、これ が 10mbit のインターフェースであることを (計算のため) カーネルに伝えて います。ここを間違えても実害はありません。しかし正しく設定すると、全体 がより正確になります。 二番目の行は 512kbit のクラスを作り、妥当なデフォルト値を与えています。 詳細は cbq の man ページと Chapter 9 をご覧ください。 最後の行は、この帯域制限クラスに向けるべきトラフィックを指定しています 。このルールにマッチしないトラフィックは制限されません。より複雑なマッ チ (サブネット、発信元ポート、送信先ポート) をさせたい場合は、 Section 9.6.2 をご覧ください。 何かを変更してスクリプトをリロードする際には、 'tc qdisc del dev $DEV root' を実行して現在の設定を削除してください。 このスクリプトの最後に 'tc qdisc add dev $DEV parent 1:1 sfq perturb 10' という行を追加すると、さらに効果的です。この行が何を行うのかについ ては、 Section 9.2.3 をご覧ください。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.10. QoS 付き nat の完全な例 私は Pedro Larroy です。ここではたくさんのユーザ がいるプライベートネットワークを、パブリックな ip アドレスを持つ Linux ルータを通してインターネットにつなぎ、この Linux ルータにネットワークア ドレス変換 (NAT) をやらせる方法について、よくある設定例を説明したいと思 います。ここでは QoS 設定を用いて、大学寮の 198 ユーザ (私もその一人。 ただし管理者です) にインターネットアクセスを提供します。ユーザはみなピ アツーピアプログラムのヘビーユーザですので、適切なトラフィック制御が不 可欠です。これが興味を持たれた lartc 読者に対する、実用的な例になってい ることを期待します。 まず先に、順番に段階を追った実践的なアプローチを取り、最後にその処理を ブート時に自動的に行うやり方を説明します。この例が適用されるネットワー クは、パブリック ip アドレスをひとつだけ持つ Linux ルータを介して、イン ターネットにつながっているプライベート LAN です。これを複数のパブリック アドレスに拡張することは非常に簡単で、 iptables のルールをいくつか追加 するだけです。動作環境を作るには、以降のものが必要となります。 Linux 2.4.18 以降のカーネルがインストールされていること 2.4.18 を使っている場合は、HTB パッチが必要です。 iproute tc のバイナリが HTB に対応していること。コンパイル済みのバイナリが HTB と一緒に配布されています。 iptables ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.10.1. まず乏しいバンド幅を最適化しましょう まずいくつか qdisc を設定して、トラフィックをクラス選別します。 htb qdisc を作り、昇順の優先度を持つ 6 つのクラスを付属させます。次に、必ず 割り当てられた速度を使え、他のクラスが不要としているバンド幅も使えるク ラスを作ります。優先度を高く (つまり prio 番号を小さく) したクラスは、 余ったバンド幅を先に利用できます。私たちの接続は下り 2Mb 上り 300kbit/s の ADSL です。私は 240kbit/s を上限速度としました。これ以上にすると、お そらく接続のどこかのバッファが効くためでしょうが、遅延が大きくなり始め るからです。このパラメータは実験的に測定して、近くのホストに対する遅延 を見ながら増減してください。 CEIL を上りバンド幅上限値の 75% に調整してください。 eth0 になっている ところは、インターネットのアクセスに使っているパブリックなインターフェ ースに変更してください。まず手始めに、以降を root のシェルで実行します 。 ┌───────────────────────────────────────────┐ │CEIL=240 │ │tc qdisc add dev eth0 root handle 1: htb default 15 │ │tc class add dev eth0 parent 1: classid 1:1 htb rate ${CEIL}kbit ceil ${CEIL}kbit │ │tc class add dev eth0 parent 1:1 classid 1:10 htb rate 80kbit ceil 80kbit prio 0 │ │tc class add dev eth0 parent 1:1 classid 1:11 htb rate 80kbit ceil ${CEIL}kbit prio 1 │ │tc class add dev eth0 parent 1:1 classid 1:12 htb rate 20kbit ceil ${CEIL}kbit prio 2 │ │tc class add dev eth0 parent 1:1 classid 1:13 htb rate 20kbit ceil ${CEIL}kbit prio 2 │ │tc class add dev eth0 parent 1:1 classid 1:14 htb rate 10kbit ceil ${CEIL}kbit prio 3 │ │tc class add dev eth0 parent 1:1 classid 1:15 htb rate 30kbit ceil ${CEIL}kbit prio 3 │ │tc qdisc add dev eth0 parent 1:12 handle 120: sfq perturb 10 │ │tc qdisc add dev eth0 parent 1:13 handle 130: sfq perturb 10 │ │tc qdisc add dev eth0 parent 1:14 handle 140: sfq perturb 10 │ │tc qdisc add dev eth0 parent 1:15 handle 150: sfq perturb 10 │ │ │ └───────────────────────────────────────────┘ ここではまず、深さが 1 レベルの htb ツリーを作りました。次のような感じ です。 ┌──────────────────────────────────┐ │+---------+ │ │| root 1: | │ │+---------+ │ │ | │ │+---------------------------------------+ │ │| class 1:1 | │ │+---------------------------------------+ │ │ | | | | | | │ │+----+ +----+ +----+ +----+ +----+ +----+ │ │|1:10| |1:11| |1:12| |1:13| |1:14| |1:15| │ │+----+ +----+ +----+ +----+ +----+ +----+ │ │ │ └──────────────────────────────────┘ classid 1:10 htb rate 80kbit ceil 80kbit prio 0 これが優先度が最高のクラスです。このクラスのパケットは、遅延が最も 小さく、余ったバンド幅を最初に割り当てられます。よってこのクラスの ceil は抑え目に設定しておくのが良いでしょう。対話的トラフィックのよ うに、遅延が小さいことによる利益が大きいパケットは、このクラスを使 って送ります。具体的には ssh, telnet, dns, quake3, irc, SYN フラグ の立ったパケットです。 classid 1:11 htb rate 80kbit ceil ${CEIL}kbit prio 1 これがバルクトラフィックをあてがう最初のクラスです。この例では、ロ ーカルの web サーバから発するトラフィック (発信元ポートが 80) と、 web ページのリクエスト (送信先ポートが 80) です。 classid 1:12 htb rate 20kbit ceil ${CEIL}kbit prio 2 このクラスには、TOS フィールドで Maximize-Throughput ビットが立って いるトラフィックと、ルータの「ローカルプロセス」からインターネット に向けて発するトラフィックをおきます。よって以降のクラスは、このマ シンを「経由する」トラフィックだけになります。 classid 1:13 htb rate 20kbit ceil ${CEIL}kbit prio 2 このクラスは、他の NAT されるマシンで、高い優先度を必要とするバルク トラフィックのためのものです。 classid 1:14 htb rate 10kbit ceil ${CEIL}kbit prio 3 ここにはメール関連のトラフィック (SMTP, pop3 など) と、 TOS フィー ルドの Minimize-Cost ビットが立ったパケットを入れます。 classid 1:15 htb rate 30kbit ceil ${CEIL}kbit prio 3 最後に、ここにはルータの背後に置かれた、NAT されたマシンからのトラ フィックを入れます。 kazaa, edonkey などはここに入れ、他のサービス と干渉しないようにします。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.10.2. パケットのクラス選別 qdisc 設定は行いましたが、パケットのクラス選別はまだです。ですので現在 は、送信されるパケットはすべて 1:15 に入ります (なぜなら tc qdisc add dev eth0 root handle 1: htb default 15 を用いたから)。ここで、どのパケ ットがどこに行くのかを伝える必要があります。ここが最も重要な部分です。 ではフィルタを設定し、パケットを iptables でクラス選別できるようにしま す。私はこの作業には、まずほとんどの場合 iptables を用います。 iptables は柔軟ですし、各ルールでのパケットの計数もできるからです。また RETURN ターゲットを用いれば、パケットにすべてのルールを適用しなくて済みます。 次のコマンドを実行します。 ┌───────────────────────────────────────┐ │tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 1 fw classid 1:10 │ │tc filter add dev eth0 parent 1:0 protocol ip prio 2 handle 2 fw classid 1:11 │ │tc filter add dev eth0 parent 1:0 protocol ip prio 3 handle 3 fw classid 1:12 │ │tc filter add dev eth0 parent 1:0 protocol ip prio 4 handle 4 fw classid 1:13 │ │tc filter add dev eth0 parent 1:0 protocol ip prio 5 handle 5 fw classid 1:14 │ │tc filter add dev eth0 parent 1:0 protocol ip prio 6 handle 6 fw classid 1:15 │ │ │ └───────────────────────────────────────┘ ここでは単に、特定の FWMARK 値 (handle x fw) を持った各パケットを対応す るクラス (classid x:x) に送るようカーネルに伝えただけです。次は、パケッ トへのマーク付けを iptables を使って行う方法です。 まず、パケットが iptables のフィルタをどのように通るのかを理解しなけれ ばなりません。 ┌─────────────────────────────────────────────┐ │ +------------+ +---------+ +-------------+ │ │Packet -| PREROUTING |--- routing-----| FORWARD |-------+-------| POSTROUTING |- Packets │ │input +------------+ decision +---------+ | +-------------+ out │ │ | | │ │ +-------+ +--------+ │ │ | INPUT |---- Local process -| OUTPUT | │ │ +-------+ +--------+ │ │ │ │ │ └─────────────────────────────────────────────┘ すべてのテーブルが存在し、デフォルトのポリシーが ACCEPT (-P ACCEPT) に なっているとします。まだ iptables に触ったことがなければ、デフォルトで ok のはずです。私たちのプライベートネットワークはクラス B のアドレス 172.17.0.0/16 を持ち、パブリック ip は 212.170.21.172 です。 次にカーネルに実際に NAT を行うよう指示し、プライベートネットワークのク ライアントが外部と通信を開始できるようにします。 ┌──────────────────────────────────────────────────┐ │echo 1 > /proc/sys/net/ipv4/ip_forward │ │iptables -t nat -A POSTROUTING -s 172.17.0.0/255.255.0.0 -o eth0 -j SNAT --to-source 212.170.21.172 │ │ │ └──────────────────────────────────────────────────┘ ここでパケットが 1:15 経由で流れていることを確認しましょう: ┌──────────────────────────────────┐ │tc -s class show dev eth0 │ │ │ └──────────────────────────────────┘ パケットへの印付けを開始するには、mangle テーブルの PREROUTING チェイン にルールを追加します。 ┌──────────────────────────────────┐ │iptables -t mangle -A PREROUTING -p icmp -j MARK --set-mark 0x1 │ │iptables -t mangle -A PREROUTING -p icmp -j RETURN │ │ │ └──────────────────────────────────┘ これでプライベートネットワークからインターネットのどこかに ping を行う と、 1:10 のパケット数が増加するのがわかるはずです。見てみましょう: ┌──────────────────────────────────┐ │tc -s class show dev eth0 │ │ │ └──────────────────────────────────┘ ここでは -j RETURN を行って、パケットが他のルールには行かないようにしま した。 icmp パケットは RETURN 以降のルールのマッチ動作の対象にはなりま せん。覚えておいてください。では適切に TOS を処理するよう、他にもルール を追加しましょう。 ┌─────────────────────────────────────────────┐ │iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j MARK --set-mark 0x1 │ │iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j RETURN │ │iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j MARK --set-mark 0x5 │ │iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j RETURN │ │iptables -t mangle -A PREROUTING -m tos --tos Maximize-Throughput -j MARK --set-mark 0x6 │ │iptables -t mangle -A PREROUTING -m tos --tos Maximize-Throughput -j RETURN │ │ │ └─────────────────────────────────────────────┘ では ssh パケットを優先付けします: ┌─────────────────────────────────────────┐ │iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 22 -j MARK --set-mark 0x1 │ │iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 22 -j RETURN │ │ │ └─────────────────────────────────────────┘ tcp 接続を開始するパケット、つまり SYN フラグの立ったパケットは、優先し ましょう。 ┌─────────────────────────────────────────────────┐ │iptables -t mangle -I PREROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x1 │ │iptables -t mangle -I PREROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j RETURN │ │ │ └─────────────────────────────────────────────────┘ などなど。mangle の PREROUTING へのルール追加が終わったら、次のコマンド で PREROUTING テーブルを締めくくりましょう。 ┌──────────────────────────────────┐ │iptables -t mangle -A PREROUTING -j MARK --set-mark 0x6 │ │ │ └──────────────────────────────────┘ これで、ここまで印付けされなかったトラフィックは 1:15 に向かいます。実 はデフォルトのクラスは 1:15 なので、この最終ステップは不必要です。です が設定全体の整合性を保つため、またこのルールのカウンタを見るために、こ こでは印付けを行っています。 同様の作業を OUTPUT ルールに対しても行うといいでしょう。よってこれらの コマンドを、-A PREROUTING の代わりに -A OUTPUT とおいて繰り返します (s/ PREROUTING/OUTPUT/)。こうするとローカル (この Linux ルータ) で生成され たトラフィックもクラス選別できます。 OUTPUT チェインの最後は、-j MARK --set-mark 0x3 で締めくくり、ローカルのトラフィックには高めの優先度を与 えるようにしました。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.10.3. この設定を改善する これでこの設定はすべて動作するようになりました。グラフを見て、バンド幅 がどのように使われているか、それをどのようにしたいか考えましょう。これ には長い時間をかけましょう。私の場合は最終的に、このインターネット接続 を非常にうまく動作させられるようになりました。これを行わなければ、常に タイムアウトに悩まされたり、新しく生成される tcp 接続にまったくバンド幅 の配分がなされなかったり、という状態だったでしょう。 特定のクラスが、ほとんどの間一杯になっているような状況でしたら、他のキ ューイング規則をそこにあてがって、バンド幅の共有をより公平にしてあげる といいでしょう。 ┌──────────────────────────────────┐ │tc qdisc add dev eth0 parent 1:13 handle 130: sfq perturb 10 │ │tc qdisc add dev eth0 parent 1:14 handle 140: sfq perturb 10 │ │tc qdisc add dev eth0 parent 1:15 handle 150: sfq perturb 10 │ │ │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.10.4. このすべてをブート時に起動する 当然ですが、いろいろな方法があります。私の場合は [start | stop | stop-tables | start-tables | reload-tables] といったオプションを受け付 ける /etc/init.d/packetfilter というスクリプトを書き、qdisc を設定し、 必要なカーネルモジュールをロードし、デーモンのように動作するようにしま した。このスクリプトは同時に、/etc/network/iptables-rules から iptables のルールもロードします。このファイルの内容は iptables-save で保存、 iptables-restore で復元できます。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 16. ブリッジと、代理 ARP を用いた擬似ブリッジの構築 ブリッジは設定を変更せずにネットワークへ挿入できるデバイスです。ネット ワークスイッチは、基本的には多ポートのブリッジです。ブリッジはそういう 意味では 2 ポートのスイッチです。しかし Linux では複数のインターフェー スを用いたブリッジをサポートしており、ほんとうの意味でのスイッチにでき ます。 ブリッジは、壊れたネットワークを、他には一切変更を与えずに直したいよう な状況で、よく用いられます。ブリッジはレイヤ 2 のデバイスですから IP よ り一層下で、ルータやサービスはその存在に気づきません。つまり透過的に特 定のパケットをブロック・修正したり、絞ったりできるのです。 もうひとつの利点、ブリッジは (もし壊れた場合には) クロスケーブルやハブ に置き換え可能です。 悪い知らせですが、ブリッジは非常に良く文書化されているにも関わらず、大 きな混乱を引き起こすことがあります。ブリッジは traceroute に現れません が、なぜかパケットが A 地点から B 地点までの間で消えたり変化したりする のです (「このネットワークは呪われている!」)。また、「何も変えたくない 」という組織が正しいことをしているかどうかにも、議論の余地があるかもし れません。 Linux 2.4/2.5 のブリッジは、このページ < http://bridge.sourceforge.net /> で説明されています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.1. ブリッジと iptables の状況 Linux 2.4.20 では、ブリッジと iptables は何もしないとお互いを「見る」こ とができません。ブリッジのパケットが eth0 から eth1 に向かうとき、その パケットは iptables を経由しないのです。つまりフィルタリング、NAT、修正 (mangling) などは行えません。 Linux 2.5.45 以降では、この点は修正されて います。 あるいはまた別のプロジェクトである 'ebtables' が紹介されているのを見か けた人もいるかもしれません。これは MACNAT とか「ブルーティング」などの 荒業ができます。非常におそろしいものです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.2. ブリッジと帯域制限 これは宣伝どおりに動作します。各インターフェースがそれぞれどちら側につ いているかを確認しておいてください。さもないと外向きのトラフィックを内 側のインターフェースで絞りかねません。これは動作しません。必要に応じて tcpdump を使いましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.3. 代理 ARP を用いた擬似ブリッジ 単に擬似ブリッジを実装したいだけでしたら、数節飛ばして「実装する」まで 進んでください。しかし実際の動作に関して少々読んでおくのは、賢いことだ と思います。 擬似ブリッジは少々違った動作をします。ブリッジでは、デフォルトだとパケ ットは変更されずに、あるインターフェースから別のインターフェースへと渡 ります。ブリッジが見るのはパケットのハードウェアアドレスだけで、それで 行き先を決めます。これはつまり、Linux が理解できないトラフィックであっ ても、ハードウェアアドレスを持っていさえすればブリッジできるわけです。 「擬似ブリッジ」は異なる動作をし、ブリッジというよりは見えないルータに 近いのですが、しかしブリッジと同様、ネットワークの設計にはほとんど影響 を与えません。 実際にはブリッジではない、ということの利点は、パケットが実際にカーネル を通り、したがってフィルタ・修正・リダイレクト・経路変更などができる、 というところにあります。 実際のブリッジにもこれらの機能を持たせることはできますが、イーサネット フレームダイバータのような特殊なコードが必要だったり、あるいは前述した ようなパッチが必要です。 擬似ブリッジのもうひとつの利点は、理解しないパケットは通さないことです 。よって汚物まみれのネットワークを掃除できるのです。このような汚物 (SAP パケットとか Netbeui とか) が必要な場合は、本物のブリッジを使ってくださ い。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.3.1. ARP と代理 ARP あるホストが、同じ物理ネットワークセグメントに存在する別のホストと通信 したくなると、そのホストはアドレス解決プロトコルのパケットを送ります。 これは簡単にいうと、「誰が 10.0.0.1 を持ってるか 10.0.0.7 に教えてくれ 」というようなものです。これに対する反応として、10.0.0.1 は「ここだよ」 という短いパケットを返します。 すると 10.0.0.7 は、「ここだよ」パケットが示すハードウェアアドレスへと パケットを送ります。またこのハードウェアアドレスは比較的長い間キャッシ ュされ、キャッシュの期限が切れると、再び質問が発せられます。 擬似ブリッジを作るときは、ブリッジに対してこのような ARP パケットに返信 するよう命じるのです。よってネットワークのホストは、その後のパケットを このブリッジに対して送信します。するとブリッジはこれらのパケットを処理 し、適切なインターフェースへと送るのです。 つまり、簡単にいえば、ブリッジの一方にあるホストが、逆側にあるホストの ハードウェアアドレスを尋ねると、ブリッジが「俺に渡せ」というパケットで 答えるのです。 このようにして、すべてのデータトラフィックは正しい場所に配達され、常に ブリッジを経由するのです。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.3.2. 実装する 以前あまり状況の良くなかった頃は、Linux カーネルに「代理 ARP」の動作を するよう命令できるのは、あるサブネットに対してだけでした。よって擬似ブ リッジを設定するには、ブリッジの両側に両方への正しい経路を指定して、代 理 ARP のマッチングルールを作る必要がありました。これには大量の入力が必 要で、また間違いも起こりやすく、ブリッジが経路を知らないネットワークへ の ARP 要求に応答することも起こりがちでした。 Linux 2.4/2.5 (おそらくは 2.2 も) では、この機能はなくなり、 /proc ディ レクトリの 'proxy_arp' というフラグが取って代わりました。よって擬似ブリ ッジを構築する手続きは次のようになります: 1. 両側のインターフェース (ここでは 'left' と 'right' とします) に IP アドレスを割り当てる 2. 経路を作り、left 側にどのホストがあり、 right 側にどのホストがある のか、わかるようにする。 3. echo 1 > /proc/sys/net/ipv4/conf/ethL/proxy_arp, echo 1 > /proc/sys /net/ipv4/conf/ethR/proxy_arp, によって両インターフェースの代理 ARP を有効にする。ただし L と R は left 側、right 側各インターフェース の番号 また、ip_forwarding フラグを ON にするのも忘れないように! 本物のブリッ ジにはこのフラグは必要ないため、移行してきたときにはこのフラグは OFF に なっているかもしれません。 もうひとつ移行の際に注意しなければならないのは、ネットワークの各コンピ ュータの ARP キャッシュをクリアしなければならない、ということです。ARP キャッシュには、前の古いブリッジのハードウェアアドレスが残っている (そ してそれはもう正しくない) でしょうから。 Cisco では、これは 'clear arp-cache' コマンドで実行できます。 Linux で は 'arp -d ip.address' です。キャッシュが期限切れになるのを待っていても 良いですが、これは比較的時間がかかります。 これを高速化するには、素晴らしいツール 'arping' が使えます。これは多く のディストリビューションで 'iputils' パッケージに入っています。 arping を用いると、要求のなかった (unsolicited な) ARP メッセージを送りつける ことができ、リモートの ARP キャッシュを更新できます。 これは非常に強力なテクニックで、悪者がルーティングを崩壊させるのにも使 えます! Note: Linux 2.4 では、この「要求のなかった ARP メッセージ」を送信す る前には、 'echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind' を実行す る必要があるかもしれません。 また、ネットマスクを省略して経路を指定するクセのある (あった) 人は、作 業の結果ネットワーク設定が壊れてしまうかもしれません。特定のバージョン の route はネットマスクを正しく推量していたかもしれませんし、何も表示す ることなく間違っていたかもしれません。前述したような外科手術的なルーテ ィングをするときには、ネットマスクをチェックするのは「生死」に関わりま す。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 17. 動的ルーティング - OSPF と BGP ネットワークが本当に大きくなり始めたり、またこれを「インターネット」と 考える必要が生じてきたら、データを動的にルーティングするツールが必要で す。サイト同士はたいてい複数の経路でつながっており、またたくさんの経路 が常に生まれています。 インターネットはほぼ OSPF (RFC 2328) と BGP4 (RFC 1771) で標準化されて います。Linux はこの両者を、 gated と zebra によってサポートしています 。 現在のところはこの文書の守備範囲ではありませんが、最も信頼できる著作を 示しておきます。 概論: Cisco Systems Designing large-scale IP Internetworks OFPF について: Moy, John T. "OSPF. The anatomy of an Internet routing protocol" Addison Wesley. Reading, MA. 1998. Halabi 氏も OSPF のルーティング設計に関する良いガイドを書いています。し かしこれは Cisco の web サイトから消されてしまったようです。 BGP について: Halabi, Bassam "Internet routing architectures" Cisco Press (New Riders Publishing). Indianapolis, IN. 1997. また Cisco Systems Using the Border Gateway Protocol for interdomain routing 例は Cisco 指向ですが、これらは Zebra での設定言語と大変良く似ています :-) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.1. Zebra による OSPF の設定 もし以降の情報に不正確な部分があったり、他にも提案があったら、どうか私 に知らせてください。 Zebra は石黒邦宏、Toshiaki Takada, Yasuhiro Ohara によって書か れた偉大な動的ルーティングソフトウェアです。 Zebra を使うと、OSPF の設 定は速やかでシンプルになりますが、しかしその裏には、特殊な用途に向けた 多くのパラメータがあるのです。 OSPF は Open Shortest Path First (最短経 路を先に開く) を表しています。その基本的な機能としては: 階層的 ネットワークは area によってグループ化され、それぞれは area 0 と名 付けられたバックボーン area によって接続されます。すべてのトラフィ ックは area 0 を通り、 area 0 のすべてのルータは、その他の area す べてに対する経路情報を持っています。 早い収束 経路情報は、例えば RIP に比べて、非常に速く伝播します。 バンド幅を効率的に使う ブロードキャストではなくマルチキャストを使うので、経路情報には興味 がないであろう他のホストをこの情報で溢れさせることはなく、ネットワ ークのオーバーヘッドが減少します。また内部ルータ (ひとつの area に 属するインターフェースしか持っていないルータ) は他の area の経路情 報を持つことがありません。複数の別々の area に属するインターフェー スを持っているルータは area 境界ルータと呼ばれ、接続している area に関するトポロジー情報を保持します。 CPU 負荷が高い OSPF は Dijkstra の Shortest Path First algorithm を基にしており、 これは他のルーティングアルゴリズムに比べ高価です。しかし実際には、 最短経路は各 area の内部でのみ計算されますので、それほどは悪くあり ません。また中小サイズのネットワークではこれは問題にならないでしょ う。気づくこともないかもしれません。 接続状態 OSPF はネットワークとインターフェースの特殊な特性 (バンド幅、断線状 態、価格など) も考慮します。 オープンなプロトコルと GPL のソフトウェア OSPF はオープンなプロトコルで、Zebra は GPL ソフトウェアです。これ は独占的なソフトウェアやプロトコルに対して、明らかな優位点です。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.1.1. 必要条件 Linux カーネル CONFIG_NETLINK_DEV と CONFIG_IP_MULTICAST を組み込む必要があります (他に必要とされるものは、著者はちゃんと理解していません)。 iproute Zebra お好みのパッケージマネージャか、 zebra.org から入手しましょう。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.1.2. Zebra の設定 このようなネットワークを例に取りましょう: ┌─────────────────────────────────────────────┐ │---------------------------------------------------- │ │| 192.168.0.0/24 | │ │| | │ │| Area 0 100BaseTX Switched | │ │| Backbone Ethernet | │ │---------------------------------------------------- │ │ | | | | │ │ | | | | │ │ |eth1 |eth1 |eth0 | │ │ |100BaseTX |100BaseTX |100BaseTX |100BaseTX │ │ |.1 |.2 |.253 | │ │ --------- ------------ ----------- ---------------- │ │ |R Omega| |R Atlantis| |R Legolas| |R Frodo | │ │ --------- ------------ ----------- ---------------- │ │ |eth0 |eth0 | | | │ │ | | | | | │ │ |2MbDSL/ATM |100BaseTX |10BaseT |10BaseT |10BaseT │ │------------ ------------------------------------ ------------------------------- │ │| Internet | | 172.17.0.0/16 Area 1 | | 192.168.1.0/24 wlan Area 2| │ │------------ | Student network (dorm) | | barcelonawireless | │ │ ------------------------------------ ------------------------------- │ │ │ └─────────────────────────────────────────────┘ この図に驚かないでください。 zebra はほとんどを自動的に処理してくれます ので、これらの経路すべてを設定するのも簡単です。これらの経路を毎日維持 するのは苦痛を伴う作業です。把握しておくべき最も重要な点は、ネットワー クのトポロジーです。また area 0 は最も重要な area なので、細心の注意を 払ってください。まず zebra.conf を要求にあわせて編集し、zebra を設定し ます。 ┌──────────────────────────────────┐ │hostname omega │ │password xxx │ │enable password xxx │ │! │ │! Interface's description. │ │! │ │!interface lo │ │! description test of desc. │ │! │ │interface eth1 │ │multicast │ │! │ │! Static default route │ │! │ │ip route 0.0.0.0/0 212.170.21.129 │ │! │ │log file /var/log/zebra/zebra.log │ │ │ └──────────────────────────────────┘ Debian では、/etc/zebra/daemons も編集して、ブート時に起動するように設 定する必要もありました。 ┌──────────────────────────────────┐ │zebra=yes │ │ospfd=yes │ │ │ └──────────────────────────────────┘ 次に、まだ IPv4 を使っているなら ospfd.conf を、 IPv6 なら ospf6d.conf を編集します。私の ospfd.conf は次のような感じです: ┌──────────────────────────────────┐ │hostname omega │ │password xxx │ │enable password xxx │ │! │ │router ospf │ │ network 192.168.0.0/24 area 0 │ │ network 172.17.0.0/16 area 1 │ │! │ │! log stdout │ │log file /var/log/zebra/ospfd.log │ │ │ └──────────────────────────────────┘ ここでは我々のネットワークトポロジーを ospf に教えています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.1.3. Zebra を実行する では Zebra を実行します。手動で "zebra -d" とタイプするか、"/etc/init.d /zebra start" のようなスクリプトを使います。起動したら ospfd のログを注 意深く監視しましょう。次のような内容が現れるはずです。 ┌───────────────────────────────────────────┐ │2002/12/13 22:46:24 OSPF: interface 192.168.0.1 join AllSPFRouters Multicast group. │ │2002/12/13 22:46:34 OSPF: SMUX_CLOSE with reason: 5 │ │2002/12/13 22:46:44 OSPF: SMUX_CLOSE with reason: 5 │ │2002/12/13 22:46:54 OSPF: SMUX_CLOSE with reason: 5 │ │2002/12/13 22:47:04 OSPF: SMUX_CLOSE with reason: 5 │ │2002/12/13 22:47:04 OSPF: DR-Election[1st]: Backup 192.168.0.1 │ │2002/12/13 22:47:04 OSPF: DR-Election[1st]: DR 192.168.0.1 │ │2002/12/13 22:47:04 OSPF: DR-Election[2nd]: Backup 0.0.0.0 │ │2002/12/13 22:47:04 OSPF: DR-Election[2nd]: DR 192.168.0.1 │ │2002/12/13 22:47:04 OSPF: interface 192.168.0.1 join AllDRouters Multicast group. │ │2002/12/13 22:47:06 OSPF: DR-Election[1st]: Backup 192.168.0.2 │ │2002/12/13 22:47:06 OSPF: DR-Election[1st]: DR 192.168.0.1 │ │2002/12/13 22:47:06 OSPF: Packet[DD]: Negotiation done (Slave). │ │2002/12/13 22:47:06 OSPF: nsm_change_status(): scheduling new router-LSA origination │ │2002/12/13 22:47:11 OSPF: ospf_intra_add_router: Start │ │ │ └───────────────────────────────────────────┘ SMUX_CLOSE メッセージは SNMP 関連のものなので、いまは無視しましょう。 192.168.0.1 が Designated ルータで、 192.168.0.2 がバックアップ Designated ルータとなっていることがわかります。 zebra や ospfd インターフェースとは、次のようなコマンドで対話できます。 ┌──────────────────────────────────┐ │$ telnet localhost zebra │ │$ telnet localhost ospfd │ │ │ └──────────────────────────────────┘ 経路がどのように伝播しているか見てみましょう。 zebra にログインして次を 入力します: ┌────────────────────────────────────┐ │root@atlantis:~# telnet localhost zebra │ │Trying 127.0.0.1... │ │Connected to atlantis. │ │Escape character is '^]'. │ │ │ │Hello, this is zebra (version 0.92a). │ │Copyright 1996-2001 Kunihiro Ishiguro. │ │ │ │User Access Verification │ │ │ │Password: │ │atlantis> show ip route │ │Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF, │ │ B - BGP, > - selected route, * - FIB route │ │ │ │K>* 0.0.0.0/0 via 192.168.0.1, eth1 │ │C>* 127.0.0.0/8 is directly connected, lo │ │O 172.17.0.0/16 [110/10] is directly connected, eth0, 06:21:53 │ │C>* 172.17.0.0/16 is directly connected, eth0 │ │O 192.168.0.0/24 [110/10] is directly connected, eth1, 06:21:53 │ │C>* 192.168.0.0/24 is directly connected, eth1 │ │atlantis> show ip ospf border-routers │ │============ OSPF router routing table ============= │ │R 192.168.0.253 [10] area: (0.0.0.0), ABR │ │ via 192.168.0.253, eth1 │ │ [10] area: (0.0.0.1), ABR │ │ via 172.17.0.2, eth0 │ │ │ └────────────────────────────────────┘ あるいは直接 iproute を使って: ┌─────────────────────────────────────┐ │root@omega:~# ip route │ │212.170.21.128/26 dev eth0 proto kernel scope link src 212.170.21.172 │ │192.168.0.0/24 dev eth1 proto kernel scope link src 192.168.0.1 │ │172.17.0.0/16 via 192.168.0.2 dev eth1 proto zebra metric 20 │ │default via 212.170.21.129 dev eth0 proto zebra │ │root@omega:~# │ │ │ └─────────────────────────────────────┘ このように zebra の経路をみることができます。以前には存在していなかった ものです。 zebra と ospfd を起動して数秒後には経路が現れているのを見る のは、とても気分のいいものです。他のホストへの接続状態は ping で確認で きます。 zebra は自動的にルーティングを行いますから、単に他のルータをネ ットワークに加え、zebra を設定すれば。どうです! ヒント: OSPF パケットをキャプチャして解析するには次が使えます: ┌──────────────────────────────────┐ │tcpdump -i eth1 ip[9] == 89 │ │ │ └──────────────────────────────────┘ OSPF の ip プロトコル番号は 89 で、プロトコルフィールドは ip ヘッダの第 9 オクテットです。 OSPF には、特に大きなネットワーク向けに、調整できるパラメータがたくさん あります。この howto をさらに拡充できれば、 OSPF のファインチューニング の方法論を示せると思います。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.2. Zebra による BGP4 の設定 Border Gateway Protocol Version 4 (BGP4) は、 RFC 1771 で記述されている 動的ルーティングプロトコルです。 BGP4 を使うと、到達性情報すなわち経路 テーブルを、他の BGP4 ノードに配布できます。EGP か IGP が使え、 IGP モ ードでは各ノードに固有の Autonomous System (AS) 番号がつきます。 BGP4 は、クラスレスインターネットドメインルーティング (CIDR) と、経路集合 (複数の経路をひとつにまとめる) とをサポートします。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.2.1. ネットワーク図 (例) 次のネットワーク図を以降での例として用います。 AS 1 と 50 には他にも近 隣ノードがありますが、ここでは 1 と 50 だけを私たちの近隣として設定すれ ば良いだけです。この例では、各ノードはトンネルを経由して通信しますが、 これは必ずしも必要ではありません。 注意: この例での AS 番号は予約済みですので、公式な接続を設定する場合は 、RIPE から AS を取得してください。 ┌──────────────────────────────────┐ │ -------------------- │ │ | 192.168.23.12/24 | │ │ | AS: 23 | │ │ -------------------- │ │ / \ │ │ / \ │ │ / \ │ │------------------ ------------------ │ │| 192.168.1.1/24 |-------| 10.10.1.1/16 | │ │| AS: 1 | | AS: 50 | │ │------------------ ------------------ │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.2.2. 設定 (例) 次の設定は 192.168.23.12/24 のものです。他のノードにあわせるのも簡単で しょう。 まずホスト名、パスワード、デバッグスイッチなどの一般情報から始まります 。 ┌──────────────────────────────────┐ │! hostname │ │hostname anakin │ │ │ │! login password │ │password xxx │ │ │ │! enable password (super user mode) │ │enable password xxx │ │ │ │! path to logfile │ │log file /var/log/zebra/bgpd.log │ │ │ │! debugging: be verbose (can be removed afterwards) │ │debug bgp events │ │debug bgp filters │ │debug bgp fsm │ │debug bgp keepalives │ │debug bgp updates │ └──────────────────────────────────┘ プライベートネットワーク (RFC 1918) の再配布を制限するためのアクセスリ ストです。 ┌──────────────────────────────────┐ │! RFC 1918 networks │ │access-list local_nets permit 192.168.0.0/16 │ │access-list local_nets permit 172.16.0.0/12 │ │access-list local_nets permit 10.0.0.0/8 │ │access-list local_nets deny any │ └──────────────────────────────────┘ 次のステップは AS ごとの設定です。 ┌───────────────────────────────────┐ │! Own AS number │ │router bgp 23 │ │ │ │ ! IP address of the router │ │ bgp router-id 192.168.23.12 │ │ │ │ ! announce our own network to other neighbors │ │ network 192.168.23.0/24 │ │ │ │ ! advertise all connected routes (= directly attached interfaces) │ │ redistribute connected │ │ │ │ ! advertise kernel routes (= manually inserted routes) │ │ redistribute kernel │ └───────────────────────────────────┘ 'router bgp' ブロックには、必ずルータが接続する近隣のリストが含まれます 。 ┌──────────────────────────────────┐ │ neighbor 192.168.1.1 remote-as 1 │ │ neighbor 192.168.1.1 distribute-list local_nets in │ │ neighbor 10.10.1.1 remote-as 50 │ │ neighbor 10.10.1.1 distribute-list local_nets in │ └──────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.2.3. 設定の確認 注意: vtysh はマルチプレクサで、 Zebra インターフェースをすべてひとまと めに接続します。 ┌─────────────────────────────────────────┐ │anakin# sh ip bgp summary │ │BGP router identifier 192.168.23.12, local AS number 23 │ │2 BGP AS-PATH entries │ │0 BGP community entries │ │ │ │Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd │ │10.10.0.1 4 50 35 40 0 0 0 00:28:40 1 │ │192.168.1.1 4 1 27574 27644 0 0 0 03:26:04 14 │ │ │ │Total number of neighbors 2 │ │anakin# │ │anakin# sh ip bgp neighbors 10.10.0.1 │ │BGP neighbor is 10.10.0.1, remote AS 50, local AS 23, external link │ │ BGP version 4, remote router ID 10.10.0.1 │ │ BGP state = Established, up for 00:29:01 │ │ .... │ │anakin# │ └─────────────────────────────────────────┘ 近隣から受けとった経路を見てみましょう。 ┌────────────────────────────────────┐ │anakin# sh ip ro bgp │ │Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF, │ │ B - BGP, > - selected route, * - FIB route │ │ │ │B>* 172.16.0.0/14 [20/0] via 192.168.1.1, tun0, 2d10h19m │ │B>* 172.30.0.0/16 [20/0] via 192.168.1.1, tun0, 10:09:24 │ │B>* 192.168.5.10/32 [20/0] via 192.168.1.1, tun0, 2d10h27m │ │B>* 192.168.5.26/32 [20/0] via 192.168.1.1, tun0, 10:09:24 │ │B>* 192.168.5.36/32 [20/0] via 192.168.1.1, tun0, 2d10h19m │ │B>* 192.168.17.0/24 [20/0] via 192.168.1.1, tun0, 3d05h07m │ │B>* 192.168.17.1/32 [20/0] via 192.168.1.1, tun0, 3d05h07m │ │B>* 192.168.32.0/24 [20/0] via 192.168.1.1, tun0, 2d10h27m │ │anakin# │ └────────────────────────────────────┘ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 18. その他の候補 この章では、高度な Linux ルーティングとトラフィックの帯域制限に関連のあ るプロジェクトを紹介します。このうちのいくつかは、これまでの章で既に紹 介していたかもしれません。あるいは他に HOWTO を必要としないくらい、自前 で非常に良く文書化されているものもあります。 Linux 用の 802.1Q VLAN 実装 VLAN はネットワークを物理的ではなく仮想的に集合させる、非常に賢いや り方です。VLAN に関する優れた情報はこちら にあります。この実装を使うと、Linux に VLAN をしゃべらせ 、 Cisco Catalyst, 3Com: {Corebuilder, Netbuilder II, SuperStack II switch 630}, Extreme Ntwks Summit 48, Foundry: {ServerIronXL, FastIron} などと会話させることができます。 VLAN に関する素晴らしい HOWTO がこちら にあります。 更新: 2.4.14 (多分 13) ではカーネルに含まれています。 Linux 用 802.1Q VLAN の別の実装 Linux 用 VLAN の別の実装です。このプロジェクトは「権威ある」VLAN プ ロジェクトのアーキテクチャやコーディングスタイルに対する意見の相違 からスタートし、より全体の設計を見通し良くすることを狙ったものです 。 Linux Virtual Server この人たちは最高です。Linux Virtual Server は、本物のサーバのクラス タ上に、 Linux OS で動作するロードバランサを使って構築する、非常に スケーラブルかつ高品位なサーバです。クラスタのアーキテクチャはエン ドユーザに対して透過的で、エンドユーザは単に一台の仮想サーバを見る ことになります。 要するに、何か負荷分散したいものがあったら、それがどんなレベルのト ラフィックでも、LVS にはそれを行う能力があるのです。彼らのテクニッ クは良い意味で邪悪です! 例えば、複数のマシンにあるセグメント内で同 じ IP アドレスを持たせ、それらに対する ARP を無効にするのです。LVS マシンだけが ARP を行い - そして到着パケットの処理をどのバックエン ドホストが行うかを決め、直接そのバックエンドサーバの MAC アドレスへ とパケットを送るのです。外向きのトラフィックは直接ルータへ流れ、LVS マシンは経由しません。よって LVS マシンは世界へ向けた 5Gbit/s のコ ンテンツのフローを見る必要はなく、よってボトルネックにもなりません 。 LVS は Linux 2.0, 2.2 ではカーネルパッチとして実装されていますが、 2.4/2.5 では netfilter のモジュールになっていますので、カーネルパッ チ必要はありません。 2.4 サポートはまだ開発の初期段階ですので、是非 いじくり回して、フィードバックやパッチを送ってください。 CBQ.init CBQ の設定は、ちょっとやる気をくじかれるところがあります。特に、や りたいことが単にルータの背後のコンピュータをいくつか絞りたいだけ、 というような場合にはなおさらです。 CBQ.init は、Linux の設定をより 単純な文法で可能にしてくれます。 例えば、(10mbit eth1 につながった) 192.168.1.0/24 サブネットのすべ てのコンピュータに対し、ダウンロードの速度を 28kbit/s に制限したけ れば、次の内容を CBQ.init の設定ファイルに書くだけです: ┌────────────────────────────┐ │DEVICE=eth1,10Mbit,1Mbit │ │RATE=28Kbit │ │WEIGHT=2Kbit │ │PRIO=5 │ │RULE=192.168.1.0/24 │ └────────────────────────────┘ 「なぜ、どのように」という内容に興味がなければ、絶対にこのプログラ ムを使うべきです。私たちは製品で CBQ.init を使っていますが、非常に うまく動作しています。時刻に依存した帯域制限といったような、より高 度な設定も可能です。文書はスクリプトに埋め込まれており、これが README が見つからない理由です。 Chronox 手軽な帯域制限スクリプト Stephan Mueller (smueller@chronox.de) は 2 つの便利なスクリプトを書 きました。 'limit.conn' と 'shaper' です。'limit.conn' を使うと、あ るひとつのダウンロードセッションを手軽に絞れます。こんな感じです: ┌────────────────────────────┐ │# limit.conn -s SERVERIP -p SERVERPORT -l LIMIT │ └────────────────────────────┘ Linux 2.2 と 2.4/2.5 で動作します。 'shaper' の方はより複雑で、たくさんの異なるキューを、iptables のル ールをもとにして作れます。 iptables は絞りたいパケットに印を付ける のに使います。 Virtual Router Redundancy プロトコルの実装 (サイト 1 , サイト 2 ) これは純粋に冗長性 (redundancy) のためのものです。 IP アドレスと MAC アドレスが同じ 2 台のマシンを用いて、さらに別の IP アドレスと MAC アドレスを持った、仮想的なマシンを作ります。もともとは (常に同 じ MAC アドレスを必要とする) ルータ向けのものだったのですが、他のサ ーバでも使えます。 このアプローチの美点は、信じられないくらい設定が簡単なことです。カ ーネルのコンパイルもパッチ当ても必要なく、すべてはユーザ空間です。 次のコマンドを、サービスに参加するすべてのマシンで実行するだけです: ┌────────────────────────────┐ │# vrrpd -i eth0 -v 50 10.0.0.22 │ └────────────────────────────┘ これで出来上がり! この時点で 10.0.0.22 はどれかひとつのサーバ (多分 最初に vrrp デーモンを実行したやつ) が保有します。ここでそのコンピ ュータをネットワークから切り離すと、非常にすばやく他のコンピュータ が 10.0.0.22 とその MAC アドレスとを引き継ぎます。 私は手元で試してみて、1 分で動作させることができました。何かおかし な理由で、デフォルトゲートウェイの情報が消えてしまったのですが、 -n フラグを付ければこれは回避できました。 これはフェイルオーバーの「ライブ画像」です: ┌─────────────────────────────┐ │64 bytes from 10.0.0.22: icmp_seq=3 ttl=255 time=0.2 ms │ │64 bytes from 10.0.0.22: icmp_seq=4 ttl=255 time=0.2 ms │ │64 bytes from 10.0.0.22: icmp_seq=5 ttl=255 time=16.8 ms │ │64 bytes from 10.0.0.22: icmp_seq=6 ttl=255 time=1.8 ms │ │64 bytes from 10.0.0.22: icmp_seq=7 ttl=255 time=1.7 ms │ └─────────────────────────────┘ ping パケットはひとつも失われていません! 4 番目のパケットの後、私は P200 をネットワークから切り離したのですが、 486 が引き継ぎました。 よって遅延が大きくなっていることがわかります。 tc-config (サイト) tc_config は linux 2.4 以降でのトラフィック制御を RedHat システムと (おそらく) その派生システムで行うためのスクリプト集です (linux 2.2.X と ipchains とでのものは古くなっています)。ルートには cbq qdisc を、葉には sfq qdisc を使っています。 snmp でトラフィック制御の統計を取るため、 snmp_pass ユーティリティ が含まれています。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 19. さらに先に進むために http://snafu.freedom.org/linux2.2/iproute-notes.html カーネルからのたくさんの情報とコメント http://www.davin.ottawa.on.ca/ols/ Linux トラフィック制御の著者の一人である Jamal Hadi Salim のスライ ド http://defiant.coinet.com/iproute2/ip-cref/ Alexey の LaTeX 文書の HTML 版 - iproute2 の一部を非常に詳細に説明 しています http://www.aciri.org/floyd/cbq.html Sally Floyd の CBQ に関する優れたページ。彼女のオリジナルの論文もあ ります。Linux 固有ではありませんが、 CBQ の理論と利用について、良い 議論をしています。非常に技術的な内容ですが、尖った人たちには良い読 み物でしょう。 Differentiated Services on Linux Werner Almesberger, Jamal Hadi Salim, Alexey Kuzunetsov によるこの 文書 は、Linux カーネルにおける DiffServe の機能、なかでも TBF, GRED, DSMARK qdisc と tcindex クラス選別器について説明しています。 http://ceti.pl/~kravietz/cbq/NET4_tc.html また別の HOWTO ですが、こちらはポーランド語です! でもコマンドライン をコピー/ペーストすれば、どの言語でも同じように動作します。著者は我 々とも共同作業をしているので、近いうちにこの HOWTO にも書いてくれる でしょう。 IOS Committed Access Rate 賞賛すべき気質を持った、Cisco の協力的な人たちが、彼らの文書をオン ラインに載せてくれました。 Cisco では文法は異なりますが、概念は同じ です。ただし我々のほうができることは多く、また車に等しい価格のルー タは不要ですが :-) Docum experimental site(site) Stef Coene は、Linux サポートを販売するよう彼の上司を納得させるのに 忙しく、よって、特にバンド幅の管理に関して、たくさんの実験を行って います。彼のサイトには実用的な情報・例・テストが満載されており、ま た CBQ/tc のいくつかのバグについても指摘があります。 TCP/IP Illustrated, volume 1, W. Richard Stevens, ISBN 0-201-63346-9 TCP/IP を本当に理解したければ、読まなければならない本です。面白い本 でもあります。 Policy Routing Using Linux, Matthew G. Marsh, ISBN 0-672-32052-5 ポリシールーティングの入門書で、たくさん例があります。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 20. 謝辞 最終的な目的は、この HOWTO に貢献してくれた人、また物事の動作原理に対す る我々の理解を助けてくれた人、のすべてをリストすることです。いまのとこ ろ Netfilter のようなスコアボードを置く計画はありませんが、協力してくだ さった人たちは是非覚えておきたいと思っています。 ・ Junk Alins ・ Joe Van Andel ・ Michael T. Babcock ・ Christopher Barton ・ Ard van Breemen ・ Ron Brinker ・ ?ukasz Bromirski ・ Lennert Buytenhek ・ Esteve Camps ・ Ricardo Javier Cardenes ・ Stef Coene ・ Don Cohen ・ Jonathan Corbet ・ Gerry N5JXS Creager ・ Marco Davids ・ Jonathan Day ・ Martin aka devik Devera ・ Hannes Ebner ・ Derek Fawcus ・ David Fries ・ Stephan "Kobold" Gehring ・ Jacek Glinkowski ・ Andrea Glorioso ・ Thomas Graf ・ Sandy Harris ・ Nadeem Hasan ・ Erik Hensema ・ Vik Heyndrickx ・ Spauldo Da Hippie ・ Koos van den Hout ・ Stefan Huelbrock ・ Alexander W. Janssen ・ Andreas Jellinghaus ・ Gareth John ・ Dave Johnson ・ Martin Josefsson ・ Andi Kleen ・ Andreas J. Koenig ・ Pawel Krawczyk ・ Amit Kucheria ・ Pedro Larroy □ Chapter 15, section 10: Example of a full nat solution with QoS □ Chapter 17, section 1: Setting up OSPF with Zebra ・ Edmund Lau ・ Philippe Latu ・ Arthur van Leeuwen ・ Jose Luis Domingo Lopez ・ Robert Lowe ・ Jason Lunz ・ Stuart Lynne ・ Alexey Mahotkin ・ Predrag Malicevic ・ Patrick McHardy ・ Andreas Mohr ・ James Morris ・ Andrew Morton ・ Wim van der Most ・ Stephan Mueller ・ Togan Muftuoglu ・ Chris Murray ・ Patrick Nagelschmidt ・ Ram Narula ・ Jorge Novo ・ Patrik ・ P?l Osgy?ny ・ Lutz Preler ・ Jason Pyeron ・ Rod Roark ・ Pavel Roskin ・ Rusty Russell ・ Mihai RUSU ・ Rob Pitman ・ Jamal Hadi Salim ・ Ren? Serral ・ David Sauer ・ Sheharyar Suleman Shaikh ・ Stewart Shields ・ Nick Silberstein ・ Konrads Smelkov ・ William Stearns ・ Andreas Steinmetz ・ Matthew Strait ・ Jason Tackaberry ・ Charles Tassell ・ Glen Turner ・ Tea Sponsor: Eric Veldhuyzen ・ Thomas Walpuski ・ Song Wang ・ Frank v Waveren ・ Chris Wilson ・ Lazar Yanackiev ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Chapter 21. 日本語訳について 本文書は中野武雄が翻訳しました。ライセンスは原文と同じく Open Publication License の v1.0 およびそれ以降に従います。 本文書の翻訳にあたっては、JF Project のメーリングリストを利用させていた だきました。杉山友章さんには、全文にわたるチェックをしていただきました 。石塚進さんには、多くの技術的なコメントをいただきました。ここに記して 感謝します。