優雅な生活の設計と実装

The Design and Implementation of the Gracious Days


LAST MODIFIED: 2009/11/05 15:14:20 UTC

新しい秩序の確立は、他の何にも増して難しく、
成功する可能性が低く、危険な事業である。
改革者は旧秩序から利益を得ている
全ての者を敵にまわし、
新秩序から利益を受けるはずの者からは
及び腰の支持しか集められない。
--- Niccolo Machiavelli, The Prince

この種の「保護」は初心者を保護するかも知れないが、
熟練ユーザを窮地に追い込むことになる。
というのは、何が親切であり、何が適切でないかかという
オペレーティングシステムの考え方の裏をかくことばかりに
かなりの労力を費やさなければならないからである。
--- A.S.Tannenbaum, Modern Operating Systems


不定期更新の日記です。ディスクスペースの関係上、 あまりに古くなったものは順次消していきます。 この日記の更新は、今野さんの *BSD Diary Links から取得することが可能です。

September 2009

感想はこちらまで (内容は匿名のメールで送られます)

コメント:

注: お返事が必要な場合は直接メールください。 ただし、確実にお返事するかどうかはわかりませんのであしからず。

発売中:「Absolute BSD〜FreeBSDシステム管理とチューニング」
[book image] [→ 書籍情報]
[→ amazon.co.jp]
[→ cbook24.com]
[→ 関連する日記のエントリ (09/24)]

Sunday, September 20

* EuroBSDCon 2009 + FreeBSD Developer Summit

‥‥に参加するために、イギリスのケンブリッジ大学に滞在中。 リリースノートを早く書け、と言われつつ、 別のバグ潰しをしたりしなかったり。

* vimage on FreeBSD 8.0

8.0 から使えるようになる仮想ネットワークスタック環境の話。 ドキュメントがあまりないので、試してみたい人向けのメモ。

vimage を使えるようにするには、 カーネルに OPTIONS VIMAGE を追加することが必要。i386 なら、 次のような感じでカーネルを再構築しましょう。

# cd /usr/src/sys/i386/conf
# cp GENERIC VIMAGE
# echo "nooptions SCTP" >> VIMAGE
# echo "options VIMAGE" >> VIMAGE
# cd /usr/src
# make KERNCONF=VIMAGE buildkernel
# make KERNCONF=VIMAGE installkernel

(訂正: 現時点では、VIMAGE と SCTP は共存できません。SCTP が入っていると構築時にエラーになるので、nooptions を追記しました。)

そして再起動すると、vimage 対応のシステムになります。

ではまず、3 つほど仮想環境をつくってみます。

# jail -c vnet host.hostname=vnet1.example.net path=/ persist
# jail -c vnet host.hostname=vnet2.example.net path=/ persist
# jail -c vnet host.hostname=vnet3.example.net path=/ persist

仮想ネットワークスタックをつくるには、jail(8) を使います。 ホスト名は、区別できれば何でも OK。「path=/」は、 仮想空間と仮想じゃない空間で、同じファイルシステム (厳密にはその名前空間) を共有するために指定します。簡単に言うと、どの仮想空間も同じルートディレクトリが見える、という形になります。「persist」は、その仮想空間のプロセスがひとつもない状態になっても、仮想空間を消さないための指定です。デフォルトでは、プロセスがなくなると、仮想空間は自動的に消えます。

できたかどうか確認するには、jls コマンドを使います。

# jls
JID   IP Address     Hostname             Path
  1   -              vnet1.example.net    /
  2   -              vnet2.example.net    /
  3   -              vnet3.example.net    /

仮想環境の中でコマンドを実行するには、jexec を使います。

# jexec 1 ifconfig
lo0: flags=8008<LOOPBACK,MULTICAST> metric 0 mtu 16384
        options=3<RXCSUM,TXCSUM>

jexec の後ろに JID (仮想環境の番号) を指定して、 その後ろにコマンドを入れます。できたばかりの仮想環境は、 lo0 しか持ってません。とりあえず、ループバックアドレスを 割り振っておきましょう。

# jexec 1 ifconfig lo0 inet 127.0.0.1/8
# jexec 2 ifconfig lo0 inet 127.0.0.1/8
# jexec 3 ifconfig lo0 inet 127.0.0.1/8

さて、このままでは仮想環境とその外との通信ができません。 ある仮想環境に、ホスト環境から NIC を持っていきたい時には、 ifconfig vnet を使います。

# ifconfig em1 vnet 1

こうすると、ホスト環境にあった em1 が消えて、JID が 1 の環境に現れます。 ひとつの物理 NIC を複数の仮想環境に割り当てることはできません (後述するようにブリッジすることは可能)。ifconfig を使って、物理 NIC をどの仮想環境に割り振るかを決めましょう。

こうやって割り振られた NIC は、それぞれが独立したネットワークスタックに所属します。なので、1, 2, 3 のそれぞれの仮想環境で NIC を 1 個ずつ持たせた場合、ルーティングテーブル等が相互に干渉することはありません。2 個ずつ割り当てて、それぞれの環境でルーティングデーモンを動かす、なんてことも可能です。

仮想環境で作業したい時は、jexec でシェルを起動すると楽です。

# jexec 1 /bin/tcsh

「試したいけど、そんなにたくさん NIC がないよ」という人も多いでしょうから、もうひとつ、特別なインタフェースを紹介しておきます。 上記の例では、仮想環境間や、ホスト環境と仮想環境の間の通信が一切できません。ネットワークスタックが独立しているので、まあ、当たり前の話です。

ただ、仮想環境の間や、ホスト環境と仮想環境の間で通信したい、 というケースは多々あります。FreeBSD では、そういった用途のために epair というインタフェースが用意されてます。

とりあえずつくってみましょう。

# ifconfig epair0 create
epair0a: Ethernet address: 02:c0:24:00:04:0a
epair0b: Ethernet address: 02:c0:24:00:07:0b
epair0a

epair0 という名前で create すると、2 個のインタフェースが (クローニングで) つくられます。どちらも Ethernet っぽい 挙動をする疑似インタフェースなのですが、 両方が同じリンクに接続されているという、特別な機能を持っています。 実装としては、L2 レベルでバックツーバックでつながっている (うまい日本語の表現が分からん) というものです。

で、この互いに接続された NIC ペア、実は仮想環境間を橋渡しすることが可能です。具体的には、ifconfig vnet を使って、片方だけ仮想環境に移動させます。

# ifconfig epair0b vnet 1

そうすると、epair0a がホスト環境、epair0b が仮想環境 1 にある状態になり、 そのインタフェースを通じて、相互に通信ができるわけです。 リンクには 2 つのインタフェースしか存在しないので、 ポイントツーポイントのトンネルのようなものだと考えて良いでしょう。

epair は、ブリッジすることも可能です。ホスト環境の NIC (ここでは em0 としましょう) が、全部の仮想環境で見えるようにしたければ、次のような方法が使えます。

# ifconfig bridge0 create
# ifconfig epair0 create
# ifconfig epair1 create
# ifconfig epair2 create
# ifconfig epair0b vnet 1
# ifconfig epair1b vnet 2
# ifconfig epair2b vnet 3
# ifconfig bridge0 addm em0 addm epair0a addm epair1a addm epair2a

これで、ホスト環境では epair[012]a が見えて、各仮想環境では対応する相方が見えます。さらにホスト環境で epair[012]a と em0 を全部ブリッジしているので、仮想環境にあるインタフェースが、em0 と同じネットワークハブにつながっているような状況になるわけです。

あとは、epair[N]b (N=0,1,2)にアドレスを割り振るなりして、独立した環境を活用することができます。

また、なにも外のインタフェースとつながないと使えないわけではありません。 epair を使うと、たとえば [ホストA]-[ルータB]-[サーバC] というネットワーク構成を、1 台のマシンの中だけで実現することができます。

# jail -c vnet host.hostname=hostA path=/ persist
# jail -c vnet host.hostname=routerB path=/ persist
# jail -c vnet host.hostname=serverC path=/ persist
# jexec 1 ifconfig lo0 inet 127.0.0.1/8
# jexec 2 ifconfig lo0 inet 127.0.0.1/8
# jexec 3 ifconfig lo0 inet 127.0.0.1/8
# ifconfig epair0 create
# ifconfig epair0a vnet 1
# ifconfig epair0b vnet 2
# ifconfig epair1 create
# ifconfig epair1a vnet 2
# ifconfig epair1b vnet 3
# jexec 1 ifconfig epair0a inet 10.0.0.1/24
# jexec 2 ifconfig epair0b inet 10.0.0.254/24
# jexec 2 ifconfig epair1a inet 10.1.0.254/24
# jexec 3 ifconfig epair1b inet 10.1.0.1/24
# jexec 1 route add default 10.0.0.254
# jexec 3 route add default 10.1.0.254
# jexec 2 sysctl net.inet.ip.forwarding=1

なかなか複雑になってきましたが、個々のマシンで設定するのと 基本的には変わりません。ホストA は ルータ B と 10.0.0.0/24 のネットワークでつながってます。ルータ B とサーバ C は、10.1.0.0/24 です。ホストA とサーバC からは、ルータの先のネットワークが見えないので、デフォルト経路をルータB に設定しています。

これで、jexec 1 /bin/tcsh や jexec 3 /bin/tcsh を使って仮想環境に入り、ウェブサーバなどのデーモンを起動すれば、仮想環境間の通信を使って、3台のマシンがあるかのような状況をつくることができます。ちゃんと ping もとおります。

# jexec 1 ping -c 3 10.1.0.1
PING 10.1.0.1 (10.1.0.1): 56 data bytes
64 bytes from 10.1.0.1: icmp_seq=0 ttl=63 time=0.110 ms
64 bytes from 10.1.0.1: icmp_seq=1 ttl=63 time=0.066 ms
64 bytes from 10.1.0.1: icmp_seq=2 ttl=63 time=0.057 ms

--- 10.1.0.1 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.057/0.078/0.110/0.023 ms

ルーティングテーブルは、それぞれ次のようになります。

# jexec 1 netstat -nrf inet
Routing tables

Internet:
Destination        Gateway            Flags    Refs      Use  Netif Expire
default            10.0.0.254         UGS         0       21 epair0
10.0.0.0/24        link#2             U           0        0 epair0
10.0.0.1           link#2             UHS         0        3    lo0
127.0.0.1          link#1             UH          0        9    lo0

# jexec 2 netstat -nrf inet
Routing tables

Internet:
Destination        Gateway            Flags    Refs      Use  Netif Expire
10.0.0.0/24        link#2             U           0       21 epair0
10.0.0.254         link#2             UHS         0        0    lo0
10.1.0.0/24        link#3             U           0        6 epair1
10.1.0.254         link#3             UHS         0        0    lo0
127.0.0.1          link#1             UH          0        0    lo0

# jexec 3 netstat -nrf inet
Routing tables

Internet:
Destination        Gateway            Flags    Refs      Use  Netif Expire
default            10.1.0.254         UGS         0        6 epair1
10.1.0.0/24        link#2             U           0        0 epair1
10.1.0.1           link#2             UHS         0        0    lo0
127.0.0.1          link#1             UH          0        0    lo0

epair を使うと、前述のとおりネットワークが 1:1 接続になってしまうので、 あまり柔軟性がないように思えるかも知れませんが、 そんなことはありません。 現実世界でハブがやっているのと全く同じように、 ブリッジを使って複数の epair を L2 レベルでつなげれば、 現実に NIC とハブで構成しているような複雑な接続も、ちゃんと再現できます。

こういう構成は、もちろん VMWare などのアプリケーションでも実現できるのですが、 vimage は 1 個の仮想環境をつくるのに 200Kb 程度しかメモリを必要としないので、すごく手軽につくれます。Jail をベースにしているので、 path=/ のコマンドラインを変更すれば、 それぞれで見えるディスク領域を分離することもできますし、 ps コマンドで見えるプロセスも、ちゃんと分離されています。 各仮想環境の root ユーザは、仮想環境から抜け出すことができないようになっていて、ユーザ管理は仮想環境単位で行なうことが可能です。

FreeBSD 8.0 には、この機能が入る予定です。 ユーザに見える違いとしては、いちばん大きな目玉です。 いろいろと面白い応用ができるので、試してみると良いでしょう。

Saturday, September 12

* still alive

思い出したように更新。

* 通話料が定額の TV 電話環境

家族が使っていたTV電話の構成を変更するために、 いろいろと調査と実験。

通話したい 2 地点は両方とも NTT 東日本。接続は片方がBフレッツ、 もう片方がフレッツADSL。この組み合せだと、フレッツフォン (VP-1000) とフレッツドットネットを使って、定額料金で通話することができる。

今まではこれで実際に使っていたのだけど、 ADSL 回線は立地条件が悪い影響で、速度が出ていなかった。 TV電話は最低速度の 128kbps で何とか接続できるので、がまんして使っていたというもの。 そもそも、光回線のサービスがない地域だったので、 TV電話が定額で通話できる前述の選択肢を使うには、 残念ながらフレッツADSLくらいしかない。 電話がかかってくると PPP のセッションが一時的に切れてしまうなどの 不安定さもあったので、どうにかならないかな、とずっと考えていた。

今年に入って、フレッツ光ネクストのサービスが使えるようになったので、 フレッツADSLからの乗り換えを決定。しかし、 調べてみるとフレッツドットネットはNGNではサービスしてないことが判明。 代替サービスとして、ひかり電話の TV 電話サービスが提供されているが、 こちらは通話料が 15円/3分。今まで定額だったことを考えると、 ADSL からの乗り換えで増える接続料にこれが加算されて、 かなり高くついてしまう。

乗り換えて安定で高速な回線にするか、このままで通話料を安く済ませるか。 ちょっと悩んだ後、フレッツフォンのTV電話通話が、 インターネット経由で可能かどうかを実験してみた。

接続は、TV電話端末(A)→ひかり電話ルータ→インターネット→ルータ→TV電話端末(B)という構成。 技術的には、ひかり電話ルータの LAN と、端末(B)の LAN をトンネルで接続して、 端末(B)がひかり電話ルータの LAN にあるかのように見せかける。 両方とも、LAN にトンネル構築用の PC を用意して FreeBSD を入れ、 トンネル構築には L2TP over UDP を使った。 PC は安価で小型なものとして、EPSON NP11-V を選択。NIC が 1 個しかないことを除けば、非常に使いやすい。 停電からの復帰時には電源 ON での復帰が可能で、FreeBSD のインストールもトラブルなし。 若干非力なところがあるものの、今回の用途には十分すぎる。

L2TP over UDP は、サーバ・クライアントの両方に MPD5 を使っている。 両方の LAN は NAPT ルータの下なので、単純なトンネルは使えない。 L2TP リクエストをトンネル構築用 PC に回送する設定を端末(A)側のルータに入れて、 ルータの IPv4 グローバルアドレスが変化したときに DNS に再登録するように設定。 端末(B)側の PC からは、ホスト名を使ってトンネルを張る。

これで、両方のルータの LAN 側の IPv4 ネットワークがトンネルでつながった状態になり、 端末(B)を端末(A)と同じルータに登録し、内線通話ができるようになった。 ひかり電話は、最初にデフォルトルートのアドレスに対して HTTP で登録情報のやりとりをするので、その通信を遮断しないように注意が必要。 それが終れば、ふつうの SIP 端末として機能する。

まずはBフレッツとイーアクセスのADSLで実験。40 時間ほど連続通話実験をして、 動作を確認してから、実際の地点に設置して 10 時間くらい試験。 機械の操作ができる人が自分しかおらず、2 地点間は 300km 以上離れているので、 失敗するとかなり悲しい。

インターネットを経由すると通信品質が落ちるかと心配だったけれど、 実際に使ってみる限りでは、問題はまったくなし。現在は、これにもう 1 地点、 まったく同じ構成でトンネルをつくり、 3 つの地点間で通話できるようにして運用し始めて、1 カ月くらい経過。 トンネルが必ず L2TP サーバを経由するため、ネットワーク帯域の利用は効率的とは言えないが、 1 本の通話で高々 2Mbps 程度しか使わないし、端末数も少ないので許容できる範囲。 非固定 IPv4 アドレスを使っていて、NAPT ルータをいくつも飛び越えなければならず、 停電などの発生時にもリカバリが自動的にできないといけない、という条件を考えると、 そこそこ使えるものになってるのではと思う。

ただ、不満もいくつか。

まず、自宅で使っているひかり電話ルータ RT-200KI が貧弱で、 設定をちょっと変えるたびに再起動しなくちゃならない。 そのせいで、SIP 関係の設定をいろいろいじるのが大変。 ちなみに、光ネクストの契約時に設置されたPR-S300NEは、 だいぶ改善されていた。

DNS で L2TP の接続先を指定している方式だと、 インターネットからアクセス可能で動的更新可能な DNS サーバがあることが必須になる。また、MPD5 はアドレス解決を起動時にしかやってくれないので、 L2TP サーバ側が停電になるなどして IP アドレスが変わり、 DNS に登録されているホスト名が更新されても、 古い IP アドレスに自動再接続し続けてしまう可能性がある。 現在は、DNS に登録されている IP アドレスが変更されたかどうかを、 各地点のトンネル構築用 PC でモニタリングして、変更されたら MPD5 を再起動するようにして回避している。

この問題は、OCN IPv6 等を使って固定 IPv6 アドレスを割り振って、 IPv6 で通信させれば回避できる可能性はある。実際、VP-1000 のフレッツドットネット経由の利用は、 IPv6 で通信が行なわれている。 その場合、ひかり電話ルータから RA で受け取ったアドレスを使い、 SIP の通信を行なう。 残念ながら調査する時間が十分にとれなかったので、IPv6 を使うケースについては、 まだ試していない。

この構成で自由度が低いのは、ひかり電話ルータに依存しているところ。 たとえば、外からひかり電話の番号に電話をかけると、 トンネル先のTV電話機すべての呼出ベルが鳴ってしまう。 それを回避しようとひかり電話を無効にすると、内線通話機能も停止してしまう。 どうせ使っていない番号なので困りはしないのだけど、 呼出ベルが鳴る可能性があるというのは、ちょっと気持ちが悪い。

‥‥で、SIP 端末なんだから、自分で SIP のサーバを構築することもできるはずだ、と、 IP電話関連の調査をしてみた。

VP-1000 は SIP も H.323 も両方サポートしている。 そして、IP-PBX としては、SIP なら Asterisk, H.323 なら GnuGK が比較的簡単に使える。 試してみたところ、 2 台の VP-1000 を Asterisk で構築した SIP サーバに登録して相互に通話する、 ならびに GnuGK で構築した H.323 gatekeeper に登録して相互に通話する、 のいずれも、実際に動作を確認できた。SIP の場合、ひかり電話は HTTP で設定情報を受け取るため、HTTP サーバを用意しておく必要があるものの、 tcpdump か何かで起動時のやりとりを眺めるだけで、 すぐに分かるような簡単なプロトコルなので、 さくっと CGI スクリプトを書く程度で対応可能。

Asterisk を GnuGK に登録し、SIP で接続した VP-1000 から、 H.323 で接続した VP-1000 に通話することも、問題なくできた。 逆は試してないが、機能的にはできるはず。Asterisk は H.323 の機能を持っているものの、gatekeeper としての機能はまだ不十分で、 単体で両方のプロトコルをサポートするのは難しいもよう。

ただし、Asterisk を使うと映像送受信はできない。これは、 VP-1000 が SIP モード時に RTP で流す映像 codec が MP4V-ES (RFC 3016) で、Asterisk がそれに完全に対応していないのが原因。 H.323 モードでは、映像の送受信はできるものの、 帯域が少ないのか、映像品質は下がるようだ。

ということで、今回のTV電話が主目的である以上、PBX をソフトウェアで置き換えるのはちょっと難しい、という結論に。 設定次第で番号をいろいろコントロールできたり、 外線との連係ができるというのは、すごく魅力的なのだけど‥‥。

H.323 モードは、HTTP を使ったひかり電話プロトコルに依存しない、 映像もちゃんと送れるという利点がある一方で、 サーバが異常停止したときの VP-1000 側のリカバリが怪しい。 なので、H.323 に切り替えて GnuGK を使うという方法も、 必要な水準を満たしていない感じ。

結局、しばらくはひかり電話ルータに頼った現状の構成を維持することに決定。 今回の調査で、こういう目的の実現に何が必要なのかがだいたい理解できたので、 もうちょっと発展させられないか考え中。 とりあえず、自前の電話会議システムが欲しいと思っていたので、 それには応用できそう。

感想はこちらまで (内容は匿名のメールで送られます)

コメント:

注: お返事が必要な場合は直接メールください。 ただし、確実にお返事するかどうかはわかりませんのであしからず。