<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>仙石浩明の日記 &#187; システム構築・運用</title>
	<atom:link href="http://www.gcd.org/blog/category/system/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.gcd.org/blog</link>
	<description>CTO兼プログラマ兼システム管理者の視点から</description>
	<lastBuildDate>Wed, 01 Feb 2012 00:44:57 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>OpenVZ な ServersMan@VPS で独自 OS を動かしてみた</title>
		<link>http://www.gcd.org/blog/2011/07/842/</link>
		<comments>http://www.gcd.org/blog/2011/07/842/#comments</comments>
		<pubDate>Sun, 31 Jul 2011 01:48:25 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[IPv6]]></category>
		<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=842</guid>
		<description><![CDATA[国内に (自宅以外で) IPv6 が使えるサーバが欲しかったので、 ServersMan@VPS を使ってみた。 ServersMan@VPS は仮想化プラットフォームが OpenVZ なので今まで敬遠していたのだが、  [...]]]></description>
			<content:encoded><![CDATA[<p>
国内に (自宅以外で) 
<a href="http://www.gcd.org/blog/2011/07/820/#more-820">IPv6 が使えるサーバが欲しかった</a>ので、
<a href="http://dream.jp/vps/">ServersMan@VPS</a> を使ってみた。
ServersMan@VPS は仮想化プラットフォームが
<a href="http://wiki.openvz.org/Main_Page">OpenVZ</a> 
なので今まで敬遠していたのだが、
<a href="http://www.saases.jp/vps/">Osukini サーバ</a> 
(<a href="http://xen.org/">Xen</a>) も、
<a href="http://vps.sakura.ad.jp/">さくらの VPS</a> 
(<a href="http://www.linux-kvm.org/page/Main_Page">KVM</a>) も、
いまのところネイティブな IPv6 はサポートしていない
(さくらの IPv6 は <a href="http://research.sakura.ad.jp/6rd-trial/">6rd</a>) 
ので、
やむなく契約してみた次第。
</p>
<p>
Xen や KVM などの完全仮想化と異なり、
OpenVZ はホストOS のカーネルがゲスト OS のカーネルとしても使われる。
つまり VPS (Virtual Private Server) 
サービスのユーザが別のカーネルを立ち上げることができない
(ServersMan@VPS Perfect は完全仮想化だが月額 
3150円と高いのでここでは考えない)。
OS は CentOS, Debian, ubuntu から選ぶことになる。
</p>
<p>
しかしながら、
私が個人的に管理しているサーバは、
全て OS を独自の
「<a href="http://www.gcd.org/blog/2007/09/129/#mydistribution">my distribution</a>」
(便宜上ここでは GCD OS と呼ぶ)
に統一していて、
全サーバでディスクの内容を同期させている。
つまりある特定のサーバ固有の設定情報も、
全サーバが共有している。
共有していないのは各サーバのホスト名と、
秘密鍵などごく一部の情報だけ。
だからサーバが壊れた場合も、
新しいマシンを用意して他のサーバからディスク内容を丸ごとコピーするだけで済む。
</p>
<p>
私個人で管理しているサーバ (もちろん会社で運用しているサーバは除く) は、
仮想環境も含めると 20台ほどになるので、
異なる OS はできれば管理したくない。
ServersMan@VPS で提供されるカーネルを使うのは
(OpenVZ の仕組み上) 仕方がないとして、
ディスクの内容は GCD OS と入れ替えることにした。
</p>
<p>
稼働中のサーバをいじるとき重要なのが、
トラブった時に
「元に戻せるか?」
ということ。
元に戻せるなら多少の失敗を恐れることはない。
ほとんどの VPS サービスは、
最悪の事態に陥っても初期化すれば元通りになるので、
操作をミスっても一からやり直せばいいのだが、
OS を入れ替えようとする場合は
(当然) 新しい OS 一式を送り込む必要があり、
初期化するとこれが消えてしまうので再度転送する羽目になる。
GCD OS は最小セットで 7GB 近くあるので、
何度も転送を繰り返したりすると 
VPS サービスの契約ネットワーク帯域を使いきってしまう。
</p>
<p>
幸い、
ServersMan@VPS に帯域制限は無いが、
一日に 何十GB も転送していたら、
きっと管理者に睨まれるはず。
そこで、
どんなに失敗しても、
再起動すれば ssh でログインできる状態に戻るような 
OS 入れ替え手順を考えてみた。
ssh でログインできさえすれば後は何とでも修復できる。
逆に言うと ServersMan@VPS のようなコンソールが提供されない 
VPS サービスでは ssh でログインできなくなると万事休す、
残された復旧手段は初期化しかない。
</p>
<p>
まず ServersMan@VPS Standard プランを契約 (月額 980円) して、
Ubuntu(64bit) の最小構成 (シンプルセット) を選択。
ssh でログインして 
netstat でソケットを開いているデーモンを調べ、
片っ端から dpkg --purge (アンインストール) する。
もちろん sshd (ssh サーバ) だけは purge してはいけない。
syslog や cron などフツーは決して purge しないデーモンも遠慮無く purge する。
ps したとき sshd のプロセスのみが表示されるような状態になるまで 
purge しまくる。
で、
一通り purge し終わったら念のため再起動してみる。
もしここで ssh でログインできなくなってしまっていたら、
初期化して振出しに戻る。
</p>
<p>
この時、
sshd が 22番ポートで listen している場合は、
GCD OS が起動する sshd と衝突するので、
22番ポート以外に変えておく。
幸い ServersMan@VPS の場合は sshd が初めから 3843番ポートで 
listen する設定になっていた (なぜ?) ので、
そのままにしておく。
</p>
<p>
次に、
デーモン類以外のパッケージも、
残しておくとディスクの肥やしになるだけなので、
できるだけ purge する。
とはいっても、
多くのパッケージが数MB 以下で容量的には誤差の範囲なので、
無理に purge して再起不能状態に陥るリスクを冒すより、
ディスクの肥やしにしておいた方がマシ。
で、
一通り purge し終わったら念のため再起動してみる。
もしここで ssh でログインできなかったら、
初期化して振出しに戻る。
</p>
<p>
こうして
netstat -nap しても ps axf しても 
sshd 以外のプロセスが一切残っていない状態になったら、
いよいよ GCD OS 一式を送り込む。
</p>
<pre class="terminal-scroll">
senri:/ # mirror -v -r /usr/local/gcd -e 'ssh -p 3843' 'core64[183.181.54.38]' > /tmp/serversman &amp;
</pre>
<p>
mirror というのはサーバ間で GCD OS の同期を行なうためのスクリプト。
64bit (x86_64) の最小セットをコピーするために引数として 
「core64」 
を指定した。
このスクリプトは、
同期すべきファイルのリストを作成して 
<a href="http://rsync.samba.org/">rsync</a> を呼び出す。
「-e 'ssh -p 3843'」
オプションは、
そのまま rsync に渡される。
このコマンド列で GCD OS 最小セット約 7GB が 
VPS の /usr/local/gcd ディレクトリ以下へ丸ごとコピーされる。
7GB の転送にどのくらいかかるかと思っていたら、
わずか 10分弱で終わってしまった。
100Mbps 以上の帯域ということになる。
結構すごい。
</p>
<p>
ホスト名を chiyoda.gcd.org に設定し、
このサーバ専用の秘密鍵を作成すれば GCD OS のインストールが完了。
あとは、
/usr/local/gcd 以下へ chroot して GCD OS を起動するだけ。
次のような GCD OS 起動スクリプト /etc/init.d/chroot を書いてみた。
</p>
<pre class="code">
#!/bin/sh
root=`echo $0 | sed -e 's@/etc/init.d/chroot$@@'`
if [ ! -d $root ]; then
   echo "Can't find root: $root"
   exit 1
fi
mount -obind /lib/modules $root/boot/lib/modules
chroot $root sh &lt;&lt;EOF
mount -a
svscanboot &amp;
/etc/rc.d/rc.M
EOF
</pre>
<p>
このスクリプトも GCD OS 一式をコピーするときに
/usr/local/gcd/etc/init.d/chroot へコピーされる。
で、/usr/local/gcd/etc/init.d/chroot を、
とりあえず手動で実行。
手動で実行するところがミソで、
もしこのスクリプトにバグがあって異常事態に陥っても、
再起動すれば元に戻る。
</p>
<p>
上記 /usr/local/gcd/etc/init.d/chroot は、
chroot /usr/local/gcd sh を実行して、
chroot 環境下で 
<a href="http://cr.yp.to/daemontools/svscanboot.html">svscanboot</a> と
/etc/rc.d/rc.M を実行する。
svscanboot は 
<a href="http://cr.yp.to/daemontools.html">daemontools</a> の起動スクリプト。
GCD OS のほとんどのデーモン類は daemontools の管理下で起動される。
一方 /etc/rc.d/rc.M は、
GCD OS のブートスクリプトで、
ファイアウォールなどネットワークまわりの設定や、
個々のサーバ特有の設定、
および一部のデーモン類の起動を行なう。
</p>
<p>
普通の Linux OS だと init から直接起動されるデーモンもあるが、
GCD OS の場合 init が起動するのは
/etc/rc.d/rc.S と /etc/rc.d/rc.M および svscanboot だけ。
/etc/rc.d/rc.S は、
OpenVZ 環境下では不要な処理ばかりなので実行する必要はない。
</p>
<p>
こうして chroot 環境下で GCD OS が起動したら、
chroot /usr/local/gcd sh などと実行して GCD OS 環境へ入ることができる。
各種設定が正しく機能しているか、
デーモン類が正しく動いているか確認する。
</p>
<p>
ServersMan@VPS で使われているカーネルが、
<a href="http://wiki.openvz.org/Download/kernel/rhel5/028stab069.6">2.6.18-194.3.1.el5.028stab069.6</a> と、
かなり古い (2.6.18 などという 5年も昔のカーネルを使い続けないでほしい ＞ RHEL) 
ので、
GCD OS をきちんと動かすには問題があった。
特に困ったのが、
iptables の owner モジュールが使えない点:
</p>
<pre class="terminal-scroll">
chiyoda:/ # uname -rv
2.6.18-194.3.1.el5.028stab069.6 #1 SMP Wed May 26 18:31:05 MSD 2010
chiyoda:/ # iptables -t nat -j REDIRECT -p udp --dport 53 --to-port 2053 -A dnscache.lo -s 127.0.0.1 -d 127.0.0.1 -m owner ! --uid-owner Gdnscache
iptables: No chain/target/match by that name.
</pre>
<p>
このエラーは、
カーネルに 
<a href="http://cateee.net/lkddb/web-lkddb/NETFILTER_XT_MATCH_OWNER.html">CONFIG_NETFILTER_XT_MATCH_OWNER</a> の設定がないのが原因。
NETFILTER_XT_MATCH_OWNER が導入されたのは 2.6.25 以降なので、
そもそも元から 2.6.18 には存在しない。
</p>
<p>
なぜ --uid-owner が必要かと言えば、
GCD OS では tinydns と dnscache を、
<a href="http://www.gcd.org/blog/2006/07/87/">同じ IP アドレスで動かす</a>ことが基本になっているから。
つまり、
通常の名前解決に 127.0.0.1 の dnscache を利用するのだが、
dnscache (Gdnscache 権限で動作) が 127.0.0.1 に問合わせる時に限り 
<a href="http://www.gcd.org/blog/2010/03/573/">mydns</a> が返事をするようにしたい。
</p>
<p>
仕方がないので、
iptables に --uid-owner を指定してエラーになる場合は、
--uid-owner 抜きで iptables を再実行するように修正した:
</p>
<pre class="code">
nsredirect="-t nat -j REDIRECT --dport 53 --to-port $PORT"
chain=dnscache.lo
iptables -t nat -N $chain 2&gt;/dev/null || iptables -t nat -F $chain
nsinner="$nsredirect -A $chain -s 127.0.0.1 -d 127.0.0.1 \
	-m owner ! --uid-owner Gdnscache"
iptables -p udp $nsinner
if [ $? -ne 0 ]; then
    nsinner="$nsredirect -A $chain -s 127.0.0.1 -d 127.0.0.1"
    iptables -p udp $nsinner
fi
iptables -p tcp $nsinner
</pre>
<p>
このような修正を、
chiyoda.gcd.org だけでなく、
GCD OS をインストールしている全サーバで一斉に行なう点がミソ。
サーバごとにファイルの内容が微妙に異なっていたりしたら、
GCD OS を使う意味がない。
</p>
<p>
当然、
--uid-owner 抜きで iptables を実行した場合は、
dnscache が 127.0.0.1 の mydns に問合わせをすることができないが、
127.0.0.1 以外、
つまり 183.181.54.38 あるいは 2001:2e8:634:0:2:1:0:2a ならば 
mydns に問合わせることができるので問題無い。
</p>
<p>
じゃ、
なんのために、
わざわざ --uid-owner を使って 127.0.0.1 の 
mydns に問合わせられるようになっているかというと、
127.0.0.1 以外の IP アドレスが動的に変わるサーバ (ノートPC など) 
も想定しているから。
GCD OS は VirtualBox や Xen などの完全仮想化環境だけでなく、
<a href="http://www.colinux.org/">coLinux</a> や今回の OpenVZ など、
仮想化環境としてはやや異質なものもサポートしている。
</p>
<p>
以上のような細かい修正を行なっていって、
GCD OS が問題無く立ち上がるようになったら、
/usr/local/gcd/etc/init.d/chroot の呼び出しを
(ubuntu の) /etc/rc.local に追加して、
VPS の起動時に自動的に GCD OS が起動されるようにする。
</p>
<p>
GCD OS が正しく立ち上がれば、
外部から 22番ポートに対して ssh ログインして GCD OS が使える。
22番ポートでログインできることが確認できたら、
3843番ポートの sshd は止めてもよい。
<a href="http://www.gcd.org/blog/2007/09/132/">chroot 環境からはいつでも脱出できる</a>ので、
3843番ポートを止めても本来の
(chroot する前の) ubuntu 環境にアクセスすることが可能:
</p>
<pre class="terminal">
senri:~ # ssh chiyoda.gcd.org
Enter passphrase for key '/root/.ssh/id_rsa':
Last login: Sat Jul 30 09:14:54 2011 from 2409:82:5fff:0:5542:d84e:971a:9656
Linux 2.6.18-194.3.1.el5.028stab069.6.
chiyoda:~ # ls -F /					<span style="color:red">↓ GCD OS</span>
bin@   dev/  ftp/   lib/    proc/  run/   sys/  usr/
boot/  etc/  home/  lib64@  root/  sbin@  tmp/  var/
chiyoda:/ # chroot_escape /bin/bash			<span style="color:red">← chroot から脱出</span>
groups: cannot find name for group ID 11
groups: cannot find name for group ID 14
root@chiyoda:/# ls -F --color=never			<span style="color:red">↓ ubuntu</span>
aquota.group@  boot/  fastboot  lib32/  mnt/   sbin/     sys/  var/
aquota.user@   dev/   home/     lib64@  proc/  selinux/  tmp/
bin/           etc/   lib/      media/  root/  srv/      usr/
root@chiyoda:/# cat /etc/debian_version
squeeze/sid
root@chiyoda:/# lsb_release -r
Release:	10.10
root@chiyoda:/# exit
chiyoda:~ # 						<span style="color:red">↓ GCD OS</span>
</pre>
<p>
「chroot_escape /bin/bash」
が、
<a href="http://www.gcd.org/blog/2007/09/132/">chroot 環境から脱出するコマンド</a>。
脱出して、
「本来の」 
root 環境 (この例では ubuntu) 下で、
引数のコマンド
「/bin/bash」
を実行する。
この bash を使って ubuntu の操作ができて、
exit すると元の GCD OS へ戻る。
まるで chroot 下の GCD OS の方が 「主」 で、
本来の root が 「従」 のように見える ;-)。
</p>
<p>
なお、
chroot_escape コマンド実行時の
「groups: cannot find name for group ID 〜」
というエラーは、
GCD OS の /etc/group と ubuntu の /etc/group が異なるため。
つまり GCD OS の root は ID 11 と 14 のグループに属しているが、
ubuntu には ID が 11 と 14 のグループが存在しないため、
このようなエラーが表示される。
</p>
<p>
ここまでやるなら、
chroot といわず root に GCD OS をインストールしてしまえば? 
という声が聞こえてきそうであるが、
ServersMan@VPS ではコンソールが利用できないため、
トラブったときのために何らかの
「バックドア」
は残しておきたい。
GCD OS がどのような状況に陥っても、
3843番ポートの sshd 
(init から起動されるので kill しても再起動する)
にログインできれば、
ubuntu 環境で GCD OS の修復が可能。
</p>
<p>
というわけで一週間ほど ServersMan@VPS Standard プランを使っているが、
意外に (失礼!) 使えるので驚いた。
実は、
OpenVZ な 512MB ということであまり期待していなかった。
OpenVZ は swap を使えないので、
メモリ 512MB だと、
ちょっと重い処理をさせるだけですぐ OOM Killer が動き出すのだろうと思っていた。
ところが、
</p>
<pre class="terminal">
chiyoda:~ $ free
             total       used       free     shared    buffers     cached
Mem:       2097152     425020    1672132          0          0          0
-/+ buffers/cache:     425020    1672132
Swap:            0          0          0
</pre>
<p>
512MB というのは実メモリ? の割当量のようで、
見かけ上は 2GB のメモリがある。
OpenVZ な VPS サービスによっては、
これをメモリ 2GB と言い張るところもあるんじゃないかと思うので、
ServersMan@VPS は良心的。
</p>
<p>
どのくらいのパフォーマンスなのか、
<a href="http://www.gcd.org/blog/">この日記</a> (WordPress を使用)
の処理時間を測ってみる。
senri.gcd.org から ApacheBench でアクセスすると:
</p>
<pre class="terminal">
senri:~ $ /usr/apache2/bin/ab -n 10 http://chiyoda.gcd.org/blog/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
	...
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        6    7   0.3      7       7
Processing:   992 1116  94.1   1139    1259
Waiting:      305  403  78.3    380     561
Total:        998 1122  94.1   1146    1265
</pre>
<p>
これを、
<a href="http://www.gcd.org/blog/2010/02/544/">Linode Xen 512MB プラン</a>
(fremont.gcd.org)
と比較する。
fremont.gcd.org は米国西海岸にある VPS なので、
同じく<a href="http://www.gcd.org/blog/2010/06/590/">西海岸にある prgmr.gcd.org</a> 
から ApacheBench でアクセスすると:
</p>
<pre class="terminal">
prgmr:~ $ /usr/apache2/bin/ab -n 10 http://fremont.gcd.org/blog/
	...
              min  mean[+/-sd] median   max
Connect:        2    3   0.1      3       3
Processing:   996 1081  73.8   1084    1197
Waiting:      354  391  30.5    388     450
Total:        999 1084  73.8   1086    1200
</pre>
<p>
両者は、
ほとんど同等のパフォーマンスであることが分かる。
Linode Xen 512MB プランは、
ディスク 20GB で月額 $19.95 (約 1600円)
なので、
ServersMan@VPS Standard プラン (ディスク 30GB, 月額 980円) 
の方がコストパフォーマンスが良い。
</p>
<p>
もちろん、
ServersMan@VPS Standard には、
カーネルのバージョンが古すぎ、という問題点があるし、
メモリ 512MB を超える部分のパフォーマンスは、
ホストOS 側の負荷状況に左右されると思われるので、
単純に比較すべきではない。
</p>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2011/07/842/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>フレッツ 光ネクストの IPv6 IPoE 接続 (ネイティブ方式) を使ってみた</title>
		<link>http://www.gcd.org/blog/2011/07/820/</link>
		<comments>http://www.gcd.org/blog/2011/07/820/#comments</comments>
		<pubDate>Sun, 24 Jul 2011 09:33:16 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[IPv6]]></category>
		<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=820</guid>
		<description><![CDATA[IPv6 閉域網であることを今までさんざん dis られてきたフレッツ網 (NGN, 次世代ネットワーク) が、 ついに 7月21日からインターネットに接続できるようになった。 これでもう 「NGN と IPv6 インタ [...]]]></description>
			<content:encoded><![CDATA[<p>
IPv6 閉域網であることを今までさんざん dis られてきたフレッツ網 
(NGN, 次世代ネットワーク) が、
ついに 7月21日から<a href="http://www.ntt-east.co.jp/release/detail/20110719_01.html">インターネットに接続できるようになった</a>。
これでもう
「<a href="http://itpro.nikkeibp.co.jp/article/OPINION/20080227/294865/">NGN と IPv6 インターネットは併用できない</a>」
などとは言わせない。
私は IPv6 を既に <a href="http://www.gcd.org/blog/2011/06/812/">PPPoE 接続で使っている</a>のだが、
トンネル方式 (PPPoE) よりネイティブ方式 (IPoE) のほうがいいにきまってる、
ということで<a href="https://plus.google.com/115826285346738411970/posts/7zBdLThjQDp">さっそく申し込んでみた</a>。
</p>
<p>
<a href="http://www.flets-east.jp/">NGN のサービス情報サイト</a>
(NGN からでないとアクセスできない) のページで
「サービス申込受付」
をクリック。
「お客様ID」 
と 
「アクセスキー」
を入力してログインすると、
「<a href="http://flets.com/v6option/">フレッツ・v6オプション</a>」
を申し込むことができる (このページから申し込むと無料)。
</p>
<p>
申込み後、
NGN から流れてくる IPv6 ルータ広告 (RA, Router Advertisement)
を tcpdump で監視していたら、
34分経過した頃にプレフィックスが突然変わった。
<a href="http://www.gcd.org/blog/2009/11/180/">NGN の契約</a>以来、
今まで一度も変わったことがなかったプレフィックス 2408:82:5fff:86a::/64 が、
2408:282:5fff::/64 になった。
これはきっとインターネットと通信できるプレフィックスに違いないと思って、
<a href="http://fremont.gcd.org/test">他のサイト</a>から ping6 を打ってみた。
が、
NGN からは RA 以外は何も流れてこない。
うーん。
ちなみに RA の送信元 (NGN のエッジ・ルータ) は 
fe80::21e:13ff:fec2:69c2 のまま変わらず。
</p>
<p>
その後 1時間ほど放置してみたが何も状況が変化しなかったので、
「フレッツ・v6オプション」
って何? と思い直して 
(サービス内容をよく確認せずに申し込んでしまっていた)、
あらためて
<a href="http://flets.com/faq/service/090800.html">FAQ</a> を見ると、
</p>
<dl class="dialog">
<dt><b>　Q</b></dt><dd>
「フレッツ・v6オプション」を利用してインターネットへの接続はできますか？
</dd>
<dt><b>　A</b></dt><dd>
「フレッツ・v6オプション」
は、NTT東日本が構築するNGN内での通信が可能ですが、
「フレッツ・v6オプション」
のみではインターネットへの接続はできません。
インターネットへの接続には、
別途プロバイダサービスをお申し込みいただく必要があります。
</dd>
</dl>
<p>
インターネットへ接続するには、
フレッツ・v6オプションとプロバイダ契約の両方が必要らしい。
では、
どのプロバイダの、
どんなサービスを申し込めばいいんだろう?
と思って、
<a href="http://flets.com/next/ipv6_ipoe/isp.html">フレッツ 光ネクスト IPv6 IPoE対応プロバイダ</a>を調べてみると、
神奈川県だと現時点で対応しているのは 
<a href="http://www.iij.ad.jp/service/fletslineup.html">IIJmio</a>
だけだった (神奈川県だけでなく他県も同様)。
IIJ は今まで契約したことがなかったが、
他に選択肢が無いのであれば仕方がない。
さくっとクレジットカード番号を入力して
<a href="https://www.iijmio.jp/guide/outline/nbd/?l=0w149a">IIJmio FiberAccess/NF</a> を契約した (月額 2100円)。
これで半固定 IPv6 アドレスによる IPoE サービスと、
動的割当て IPv4 アドレスによる PPPoE サービスが利用できる。
</p>
<p>
Ether 上に IP を流すのはごくごく普通のことなので、
あらたまって
「IPoE」
(IP over Ether)
と表現されると何だか妙な感じ。
「半固定」
というのも微妙な表現だが、
注意書きには
「お客様の移転、フレッツ回線の品目変更やNTTのメンテナンスにより、
変更になる場合があります」
と書いてあるので、
普通に使ってる限りプレフィックスが変わることは無さそう。
</p>
<blockquote>
mio FiberAccess/NFサービスは、
<a href="http://www.mfeed.co.jp/">インターネットマルチフィード株式会社</a>が提供する
「<a href="http://www.mfeed.co.jp/transix/index.html">transix (トランジックス)</a>」
サービスを利用して、
NTT東西の
「インターネット（IPv6 IPoE）接続」
に対応したIPv6接続を提供します。
IPv6接続に加えて、
PPPoE接続方式によるIPv4接続もあわせて提供するため、
お客様は一つのサービスでIPv6、
IPv4の両方の接続環境を利用することができます。 
<div align="right">IIJmio <a href="https://www.iijmio.jp/guide/outline/nbd/?l=0w149a">FiberAccess/NF概要</a> から引用</div>
</blockquote>
<p>
IPv6 接続は全て transix が担っていて、
IIJmio はネットワーク的には何の役割も果たしていない。
IIJmio がやってるのは課金などユーザ管理関連だけ。
プロバイダである IIJmio が絡むから、
IPv4 接続も提供するなどという抱き合わせ商法になる。
動的割当の IPv4 PPPoE 接続サービスなんて要らないから、
もう少し安いプランがあればいいのにと思う。
</p>
<p>
本来、
ネイティブ方式の IPv6 接続サービスは、
NTT東西だけで提供するのが自然な形だった。
ところが、
NGN を持っていて地域独占な NTT東西が接続サービスまで提供してしまっては、
他のプロバイダの出る幕がなくなると猛反発されて、
<a href="http://www.bbix.net/">BBIX</a>, 
<a href="http://www.jpne.co.jp/">JPNE</a>, 
<a href="http://www.mfeed.co.jp/">インターネットマルチフィード</a>
の三社が接続サービスを提供することになった。
なぜ三社だけかといえば、
プロバイダを増やすと経路情報の処理量が膨大になって
NGN の各ルータの処理能力の限界を超えてしまうから。
</p>
<p>
ところが、
三社だけでは少なすぎると他のプロバイダ達が反対したので、
その他大勢の中小プロバイダにも参入の余地を無理矢理作ったということなのだろう。
でも、
やってることは単なるユーザ管理なので、
通信事業者じゃなくてもできる簡単なお仕事。
この期に及んで業界保護みたいなことはやめてほしい。
インターネット接続サービスは既にコモディティ化しているのだから、
体力のないところは淘汰されるべき。
</p>
<p>
FiberAccess/NF を契約してから 1時間が経過したとき、
NGN から流れてくる RA のプレフィックスが 2409:82:5fff::/64 に変わった。
今度こそ疎通したかと思い、
senri.gcd.org に IPv6 アドレス 2409:82:5fff::3c20:55dc を割当てて、
<a href="http://fremont.gcd.org/test">他のサイト</a>から
2409:82:5fff::3c20:55dc に対して ping6 を打ってみる。
すると無事、NGN IPoE 経由で senri.gcd.org に ICMPv6 パケットが届いた。
</p>
<p>
ただし、
まだ senri.gcd.org の routing 設定を行なっていないので、
返りパケットは PPPoE 経由で OCN 側へ行ってしまう 
(おそらく OCN 内部で捨てられる)。
そこで、
とりあえずの対策として NGN から届いたパケットは NGN へ返すように、
policy routing rule を設定した:
</p>
<pre class="terminal">
senri:/ # ip -6 rule add from 2409:82:5fff::/64 table 100 pref 25600
senri:/ # ip -6 route add default table 100 via fe80::21e:13ff:fec2:69c2 dev eth1
</pre>
<p>
つまり、
送信元アドレスが 2409:82:5fff::/64 なパケットは
routing table 100 を参照するようにして、
routing table 100 において default route を、
fe80::21e:13ff:fec2:69c2 (NGN のエッジ・ルータ) へ向ける。
</p>
<p>
これで ping に対して応答できるようになった。
</p>
<pre class="terminal">
fremont:~ $ ping6 -c 3 2409:82:5fff::3c20:55dc
PING 2409:82:5fff::3c20:55dc(2409:82:5fff::3c20:55dc) 56 data bytes
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=1 ttl=49 time=122 ms
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=2 ttl=49 time=122 ms
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=3 ttl=49 time=122 ms

--- 2409:82:5fff::3c20:55dc ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 122.479/122.526/122.591/0.289 ms
fremont:~ $ tcpspray 2409:82:5fff::3c20:55dc
Transmitted 102400 bytes in 0.491628 seconds (203.406 kbytes/s)
</pre>
<p>
RTT (Round Trip Time, 往復所要時間) が 122ms もかかってるのは、
<a href="http://www.gcd.org/blog/2010/02/544/">fremont.gcd.org が米国の西海岸にある</a>ため。
2400:400d:100::3c20:55dc (OCN の PPPoE 接続) 宛の場合↓ と比べると、
transix は RTT で 50ms ほど速く、
帯域 (tcpspray による簡易測定) で倍くらい広い。
</p>
<pre class="terminal">
fremont:~ $ ping6 -c 3 2400:400d:100::3c20:55dc
PING 2400:400d:100::3c20:55dc(2400:400d:100::3c20:55dc) 56 data bytes
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=1 ttl=49 time=174 ms
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=2 ttl=49 time=174 ms
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=3 ttl=49 time=174 ms

--- 2400:400d:100::3c20:55dc ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 174.046/174.386/174.593/0.539 ms

fremont:~ $ tcpspray 2400:400d:100::3c20:55dc
Transmitted 102400 bytes in 0.905523 seconds (110.433 kbytes/s)
</pre>
<p>
参考までに IPv4 60.32.85.216 (OCN の PPPoE 接続) の場合も測ってみた。
すると RTT も帯域も transix と同程度だった。
つまり OCN の IPv6 PPPoE だけが突出して遅く、
帯域も狭い。
</p>
<pre class="terminal">
fremont:~ $ ping -c 3 60.32.85.216
PING 60.32.85.216 (60.32.85.216) 56(84) bytes of data.
64 bytes from 60.32.85.216: icmp_req=1 ttl=51 time=130 ms
64 bytes from 60.32.85.216: icmp_req=2 ttl=51 time=130 ms
64 bytes from 60.32.85.216: icmp_req=3 ttl=51 time=129 ms

--- 60.32.85.216 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 129.377/129.903/130.202/0.559 ms

fremont:~ $ tcpspray 60.32.85.216
Transmitted 102400 bytes in 0.518052 seconds (193.031 kbytes/s)
</pre>
<p>
米国西海岸から transix までの IPv6 の経路はこんな感じ:
</p>
<pre class="terminal">
fremont:~ $ tracepath6 2409:82:5fff::3c20:55dc
 1?: [LOCALHOST]                        0.021ms pmtu 1500
 1:  2600:3c01:ffff:0:ca4c:75ff:fef5:d63f                  0.558ms
 1:  2600:3c01:ffff:0:ca4c:75ff:fef5:d63f                  0.480ms
 2:  10gigabitethernet2-3.core1.fmt1.he.net                3.149ms
 3:  10gigabitethernet1-2.core1.sjc2.he.net               11.042ms
 4:  equi6ix.sv.iij.com                                    1.834ms asymm  5
 5:  sjc002bf00.iij.net                                    9.384ms asymm  7
 6:  2001:48b0:bb00:8016::71                             120.588ms asymm  7
 7:  tky009bb11.IIJ.Net                                  120.730ms asymm  8
 8:  tky009ip50.IIJ.Net                                  121.048ms
 9:  2001:240:bb5c:1008::cafe                            120.365ms
10:  2404:8e00:feed:101::2                               121.852ms
11:  no reply
12:  no reply
13:  no reply
14:  no reply
15:  no reply
16:  2409:82:5fff::3c20:55dc                             127.409ms reached
     Resume: pmtu 1500 hops 16 back 49
</pre>
<p>
「IIJmio はネットワーク的には何の役割も果たしていない」
が、
日米間の回線は IIJ が担っているようだ (あれっ? ^^;)。
日本に上陸後 (6 以降) はほとんど遅延していない。
</p>
<p>
OCN の PPPoE の場合だと、こんな感じ:
</p>
<pre class="terminal">
fremont:~ $ tracepath6 2400:400d:100::3c20:55dc
 1?: [LOCALHOST]                        0.047ms pmtu 1500
 1:  2600:3c01:ffff:0:ca4c:75ff:fef5:d63f                  3.935ms
 1:  2600:3c01:ffff:0:ca4c:75ff:fef5:d63f                  0.852ms
 2:  10gigabitethernet2-3.core1.fmt1.he.net                7.978ms
 3:  10gigabitethernet1-2.core1.sjc2.he.net                2.532ms
 4:  xe-0.equinix.snjsca04.us.bb.gin.ntt.net               2.018ms asymm  5
 5:  as-1.r21.osakjp01.jp.bb.gin.ntt.net                 168.074ms asymm 11
 6:  ae-0.ocn.osakjp01.jp.bb.gin.ntt.net                 167.792ms asymm 14
 7:  2001:380:8060:6::1                                  159.446ms asymm 13
 8:  2001:380:8170:4::1                                  167.630ms asymm 14
 9:  2001:380:8030:16::1                                 168.401ms asymm 15
10:  2001:380:8110:d::1                                  170.344ms asymm 14
11:  2001:380:8110:f::2                                  178.503ms asymm 13
12:  2001:380:8130:11::13                                178.131ms asymm 13
13:  2001:380:8270:8::2                                  172.448ms
14:  2001:380:4d:101::2                                  179.036ms
15:  2001:380:4d:182::2                                  181.317ms
16:  no reply
17:  2001:380:4d:181::2                                  173.127ms pmtu 1454
17:  senri.v6.gcd.org                                    183.023ms reached
     Resume: pmtu 1454 hops 17 back 49
</pre>
<p>
日米間に 160ms もかかっている上に、
日本に上陸後 (5 以降) も 15ms ほど遅延がある。
なぜこんなに遅いのだろう?
また、PPPoE なので mtu が 1454 になっている。
</p>
<p>
同じ OCN の PPPoE でも、
IPv4 だと遅くない (というか transix より若干速い) ので、
OCN の IPv6 PPPoE 接続サービスには、
なにか問題がありそう。
まあ、
追加料金無しのサービスなので、
IPv4 のオマケ的な位置づけなのかも?
</p>
<pre class="terminal">
fremont:~ $ tracepath 60.32.85.216
 1:  fremont.gcd.org                                       0.169ms pmtu 1500
 1:  184.105.143.85                                        1.702ms
 1:  184.105.143.85                                        0.418ms
 2:  10gigabitethernet2-3.core1.fmt1.he.net                0.665ms
 3:  10gigabitethernet1-1.core1.pao1.he.net                8.907ms
 4:  sjo-bb1-link.telia.net                                1.297ms asymm  5
 5:  verio-119529-sjo-bb1.telia.net                        4.568ms
 6:  ae-8.r20.snjsca04.us.bb.gin.ntt.net                   1.922ms
 7:  as-2.r20.tokyjp01.jp.bb.gin.ntt.net                 112.093ms asymm  8
 8:  ae-1.ocn.tokyjp01.jp.bb.gin.ntt.net                 119.692ms asymm 11
 9:  60.37.27.137                                        120.641ms asymm 10
10:  60.37.55.158                                        119.549ms asymm 11
11:  122.1.173.238                                       121.243ms
12:  118.23.5.78                                         128.853ms asymm 14
13:  no reply
14:  118.23.8.9                                          128.712ms pmtu 1454
14:  gcd.org                                             123.788ms reached
     Resume: pmtu 1454 hops 14 back 52
</pre>
<span id="more-820"></span>
<p>
7月26日追記:
</p>
<p>
西海岸から ping6 を打つのは 2階から目薬もびっくりなので、
<a href="http://dream.jp/vps/">国内の IPv6 が使える VPS</a> 
を急遽<a href="http://chiyoda.gcd.org/test">契約して</a> (2ヵ月無料キャンペーン)、
ping6 を打ってみた。
</p>
<pre class="terminal">
chiyoda:~ $ ping6 -c 3 senri
PING senri(2409:82:5fff::3c20:55dc) 56 data bytes
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=1 ttl=49 time=6.96 ms
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=2 ttl=49 time=6.72 ms
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=3 ttl=49 time=6.51 ms

--- senri ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 6.515/6.734/6.969/0.208 ms

chiyoda:~ $ tcpspray -n 10000 2409:82:5fff::3c20:55dc
Transmitted 10240000 bytes in 0.864344 seconds (11569.468 kbytes/s)
</pre>
<p>
さすがに国内からだと速くて広い。
93Mbps 出れば普通の用途には充分。
OCN PPPoE の IPv6 および IPv4 でも測ってみたが、
有意な差は見られない:
</p>
<pre class="terminal">
chiyoda:~ $ ping6 -c 3 2400:400d:100::3c20:55dc
PING 2400:400d:100::3c20:55dc(2400:400d:100::3c20:55dc) 56 data bytes
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=1 ttl=54 time=8.84 ms
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=2 ttl=54 time=6.58 ms
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=3 ttl=54 time=6.46 ms

--- 2400:400d:100::3c20:55dc ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 6.465/7.298/8.843/1.097 ms

chiyoda:~ $ tcpspray -n 10000 2400:400d:100::3c20:55dc
Transmitted 10240000 bytes in 0.844327 seconds (11843.752 kbytes/s)

chiyoda:~ $ ping -c 3 60.32.85.216
PING 60.32.85.216 (60.32.85.216) 56(84) bytes of data.
64 bytes from 60.32.85.216: icmp_req=1 ttl=53 time=6.62 ms
64 bytes from 60.32.85.216: icmp_req=2 ttl=53 time=6.64 ms
64 bytes from 60.32.85.216: icmp_req=3 ttl=53 time=6.95 ms

--- 60.32.85.216 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 6.623/6.740/6.955/0.152 ms

chiyoda:~ $ tcpspray -n 10000 60.32.85.216
Transmitted 10240000 bytes in 0.842140 seconds (11874.510 kbytes/s)
</pre>
<p>
経路は、
それぞれ以下のような感じ:
</p>
<p>
transix IPv6 IPoE:
</p>
<pre class="terminal">
chiyoda:~ $ tracepath6 2409:82:5fff::3c20:55dc
 1?: [LOCALHOST]                        0.016ms pmtu 1500
 1:  2001:2e8:634:0:2:2:0:1                                0.054ms 
 1:  2001:2e8:634:0:2:2:0:1                                0.038ms 
 2:  2001:2e8:22:202::2                                    1.192ms asymm  4 
 3:  2001:2e8:20::22:11                                    1.466ms asymm  5 
 4:  2001:7fa:7:1::2497:1                                 53.299ms asymm  6 
 5:  tky008bf01.IIJ.Net                                    3.417ms asymm  6 
 6:  tky009bb10.IIJ.Net                                    2.622ms asymm  8 
 7:  tky009ip50.IIJ.Net                                    2.763ms asymm  8 
 8:  2001:240:bb5c:1008::cafe                              3.188ms asymm  9 
 9:  2404:8e00:feed:101::2                                 3.911ms asymm 10 
10:  no reply
11:  no reply
12:  no reply
13:  no reply
14:  no reply
15:  2409:82:5fff::3c20:55dc                               8.107ms reached
     Resume: pmtu 1500 hops 15 back 49 
</pre>
<p>
OCN IPv6 PPPoE:
</p>
<pre class="terminal">
chiyoda:~ $ tracepath6 2400:400d:100::3c20:55dc
 1?: [LOCALHOST]                        0.019ms pmtu 1500
 1:  2001:2e8:634:0:2:2:0:1                                0.058ms 
 1:  2001:2e8:634:0:2:2:0:1                                0.037ms 
 2:  2001:2e8:22:202::2                                    2.505ms asymm  4 
 3:  2001:2e8:20::22:11                                    2.464ms asymm  5 
 4:  2001:380:0:2516::1                                    3.111ms asymm  6 
 5:  2001:380:8280:f::1                                   19.584ms asymm  7 
 6:  2001:380:8150:f::12                                   3.486ms asymm  8 
 7:  2001:380:8270:7::2                                    5.817ms asymm  8 
 8:  2001:380:4d:100::2                                    4.504ms asymm  9 
 9:  2001:380:4d:181::2                                   14.276ms asymm 10 
10:  no reply
11:  2001:380:4d:181::2                                    7.015ms pmtu 1454
11:  senri.v6.gcd.org                                      7.457ms reached
     Resume: pmtu 1454 hops 11 back 54 
</pre>
<p>
OCN IPv4 PPPoE:
</p>
<pre class="terminal">
chiyoda:~ $ tracepath 60.32.85.216
 1:  v-183-181-54-38.ub-freebit.net                        0.117ms pmtu 1500
 1:  v-183-181-55-247.ub-freebit.net                       0.040ms 
 1:  v-183-181-55-247.ub-freebit.net                       0.022ms 
 2:  202.216.248.129                                       0.515ms asymm  3 
 3:  no reply
 4:  oi-gate-1.otemachi4.dti.ad.jp                         1.899ms asymm  6 
 5:  122.28.177.109                                        2.830ms asymm  7 
 6:  118.23.146.121                                        8.064ms asymm  8 
 7:  118.23.168.8                                          3.123ms asymm  9 
 8:  60.37.55.154                                          5.095ms asymm  9 
 9:  122.1.173.238                                         4.176ms asymm 10 
10:  118.23.5.74                                           5.482ms asymm 12 
11:  no reply
12:  118.23.8.9                                            5.328ms pmtu 1454
12:  gcd.org                                               7.036ms reached
     Resume: pmtu 1454 hops 12 back 53 
</pre>

<p>
「国際区間で遅延が大きめなのは、
OCNから fremont.gcd.org への帰りのパケットが香港経由になってるため」
というコメントを頂いたので、
返りの経路も調べてみた。
確かに、
hkix.net 経由になっていて、
東京-香港間に 50ms ほどかかっている:
</p>
<pre class="terminal-scroll">
senri:/ # traceroute6 -s 2400:400d:100::3c20:55dc fremont.gcd.org
traceroute to fremont.gcd.org (2600:3c01::f03c:91ff:fe96:f285) from 2400:400d:100::3c20:55dc, 30 hops max, 24 byte packets
 1  2001:380:4d:1ff::3 (2001:380:4d:1ff::3)  4.876 ms  3.907 ms  4.369 ms
 2  2001:380:4d:181::1 (2001:380:4d:181::1)  3.104 ms  2.888 ms  3.556 ms
 3  2001:380:4d:100::1 (2001:380:4d:100::1)  5.911 ms  6.002 ms  5.581 ms
 4  2001:380:8270:7::1 (2001:380:8270:7::1)  4.072 ms  4.156 ms  3.969 ms
 5  2001:380:8150:f::18 (2001:380:8150:f::18)  4.622 ms  4.689 ms  4.584 ms
 6  ae-5.r20.tokyjp01.jp.bb.gin.ntt.net (2001:218:0:5000::d)  4.54 ms  4.172 ms  4.577 ms
 7  ae-2.r25.tokyjp01.jp.bb.gin.ntt.net (2001:218:0:2000::1c6)  5.063 ms  4.27 ms  4.511 ms
 8  as-3.r21.newthk02.hk.bb.gin.ntt.net (2001:218:0:2000::13e)  56.208 ms  56.149 ms  55.959 ms
 9  xe-2-1.r01.newthk01.hk.bb.gin.ntt.net (2001:218:0:2000::159)  101.451 ms  57.473 ms  57.487 ms
10  ae-3.a02.newthk01.hk.ra.gin.ntt.net (2001:218:0:6000::156)  56.489 ms  56.368 ms  107.274 ms
11  hurricaneelectric-RGE.hkix.net (2001:7fa:0:1::ca28:a19e)  57.228 ms  57.492 ms  57.619 ms
12  gige-g3-7.core1.lax1.he.net (2001:470:0:16b::1)  174.14 ms  173.967 ms  174.26 ms
13  10gigabitethernet7-4.core1.fmt2.he.net (2001:470:0:18d::1)  180.178 ms  188.238 ms  181.149 ms
14  gige-g4-18.core1.fmt1.he.net (2001:470:0:2d::1)  173.357 ms  173.099 ms  173.85 ms
15  2001:470:1:1db::2 (2001:470:1:1db::2)  173.973 ms  173.424 ms  178.249 ms
16  2600:3c01::f03c:91ff:fe96:f285 (2600:3c01::f03c:91ff:fe96:f285)  174.287 ms  174.162 ms  174.175 ms
</pre>
<p>
8月24日追記:
</p>
<p>
「fremont.gcd.org と senri.gcd.org 間のIPv6の遅延は改善されたんじゃないか」
というコメントを頂いたので、
再度調べてみた:
</p>
<pre class="terminal-scroll">
senri:/ # traceroute6 -s 2400:400d:100::3c20:55dc fremont.gcd.org
traceroute to fremont.gcd.org (2600:3c01::f03c:91ff:fe96:f285) from 2400:400d:100::3c20:55dc, 30 hops max, 24 byte packets
 1  asao.v6.gcd.org (2400:400d:100::3c20:55dd)  0.239 ms  0.152 ms  0.14 ms
 2  2001:380:4d:1ff::3 (2001:380:4d:1ff::3)  4.61 ms  4.696 ms  3.957 ms
 3  2001:380:4d:181::1 (2001:380:4d:181::1)  3.445 ms  3.73 ms  3.347 ms
 4  2001:380:4d:100::1 (2001:380:4d:100::1)  5.994 ms  5.893 ms  5.813 ms
 5  2001:380:8270:7::1 (2001:380:8270:7::1)  3.727 ms  3.58 ms  3.82 ms
 6  2001:380:8150:f::18 (2001:380:8150:f::18)  4.51 ms  4.119 ms  4.769 ms
 7  ae-5.r20.tokyjp01.jp.bb.gin.ntt.net (2001:218:0:5000::d)  4.353 ms  4.446 ms  4.526 ms
 8  ae-3.r20.osakjp01.jp.bb.gin.ntt.net (2001:218:0:2000::da)  14.744 ms  14.429 ms  14.338 ms
 9  as-2.r20.snjsca04.us.bb.gin.ntt.net (2001:218:0:2000::7d)  122.714 ms  122.792 ms  122.84 ms
10  ae-0.r21.snjsca04.us.bb.gin.ntt.net (2001:418:0:2000::66)  130.233 ms  130.342 ms  176.712 ms
11  10gigabitethernet2-3.core1.sjc2.he.net (2001:504:0:1::6939:1)  132.712 ms  122.997 ms  122.804 ms
12  10gigabitethernet1-1.core1.fmt1.he.net (2001:470:0:2f::1)  132.042 ms  141.087 ms  132.982 ms
13  2001:470:1:1db::2 (2001:470:1:1db::2)  124.032 ms  124.21 ms  124.141 ms
14  2600:3c01::f03c:91ff:fe96:f285 (2600:3c01::f03c:91ff:fe96:f285)  125.249 ms  124.642 ms  125.253 ms
</pre>
<p>
確かに、
ntt.net から直接 he.net へ行く経路に変わっていて、
香港に迂回していた時と比べ、
50ms ほど速くなっている。
1hop目が asao になっているのは、
asao と senri で冗長構成になっていて、
前回は senri が master だったが現在は asao が master に切り替わっているため。
</p>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2011/07/820/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>フレッツ 光ネクストの IPv6 PPPoE 接続を OCN 光アクセスで使ってみた</title>
		<link>http://www.gcd.org/blog/2011/06/812/</link>
		<comments>http://www.gcd.org/blog/2011/06/812/#comments</comments>
		<pubDate>Mon, 20 Jun 2011 00:08:04 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[IPv6]]></category>
		<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=812</guid>
		<description><![CDATA[6月1日から NTT東日本のフレッツ 光ネクストにおいて IPv6 PPPoE 接続の提供が始まった。 私は OCN光アクセスを契約しているが、 幸い OCN (NTTコミュニケーションズ) も、 NTT東日本に合わせて [...]]]></description>
			<content:encoded><![CDATA[<p>
6月1日から NTT東日本の<a href="http://flets.com/next/">フレッツ 光ネクスト</a>において
<a href="http://www.ntt-east.co.jp/release/1105/110526c.html">IPv6 PPPoE 接続の提供が始まった</a>。
私は <a href="http://www.gcd.org/blog/2009/11/180/">OCN光アクセスを契約している</a>が、
幸い OCN (NTTコミュニケーションズ) も、
NTT東日本に合わせて<a href="http://www.ntt.com/release/monthNEWS/detail/20110526.html">順次対応を開始</a>ということなので、
さっそく OCN へ電話で問合わせてみたら、
申込書をメールで送るので記入押印の上 FAX で送り返して欲しい、とのこと。
</p>
<p>
いまどき Web で申し込めないなんて、
と思いつつ 8ページにもわたる申込書
(いつも感じるが OCN の申込書は無駄にページ数が多い *_*)
を FAX で送付。
すると翌日電話がかかってきて、
開通日は 10日後などとおっしゃる。
それじゃ 
<a href="https://twitter.com/gcd_org/status/78613974916153344">World IPv6 Day</a>
に間に合わないじゃんと思ったが、
どんな変更でも申込みから 7営業日は最低でもかかるらしいので仕方がない。
なお、
工事費および月額使用料は無料。
</p>
<p>
OCN には元々月額 315円の 
「<a href="http://www.ocn.ne.jp/ipv6/service/index.html">OCN IPv6</a>」
というサービスがあるが、
OCN IPv6 はフレッツ網に PPPoE によるトンネルを張って 
IPv4 を通し (OCN フレッツ光)、
その IPv4 上に L2TP (Layer 2 Tunneling Protocol) によるトンネルを張って
PPP セッションを通し、
その PPP 上に IPv6 を通すという屋上屋を架す方式だったのに対し、
6月1日から始まった
「IPv6インターネット接続」
はフレッツ網に PPPoE によるトンネルを張って IPv6 を通す方式。
</p>
<p>
つまり OCN フレッツ光の IPv4 の部分を 
IPv6 でそのまま置き換えたシンプルな方式。
もちろんフレッツ網に IPv6 を直接通す
「ネイティブ方式」
が一番シンプルだが、
ネイティブ方式に関しては
「平成23年7月を目途に提供を予定」
ということなので、
どのようなサービスが始まるのか今のところ不明
(もう来週には 7月が始まってしまうのだが)。
7/24追記: <a href="http://www.gcd.org/blog/2011/07/820/">ネイティブ方式サービスが始まった！</a>
</p>
<p>
開通日の 2日前に郵便で届いた
「ご利用内容のご案内」
の欄外の注釈に、
</p>
<blockquote>
IPv6 でインターネットに接続する際、
認証ID の @ 右側を
&ldquo;@bizf.ocn.ne.jp&rdquo;
の場合は
&ldquo;@bizf6.ocn.ne.jp&rdquo;
に、
&ldquo;@bizd.ocn.ne.jp&rdquo;
の場合は
&ldquo;@bizd6.ocn.ne.jp&rdquo;
に変更してご利用下さい。
</blockquote>
<p>
と書いてあった。
IPv6 での接続に関する説明はこの部分だけなので、
あとは推測するしかない
(サポートに接続方法を聞いても、
単に <a href="http://www.ntt-east.co.jp/release/1105/110526b.html">IPv6トンネル対応ルータ</a>を購入してくれと言われる)。
</p>
<p>
とりあえず、
ふだん IPv4 で接続するときに使ってる PPPoE スクリプト
(<a href="http://www.roaringpenguin.com/products/pppoe">RP-PPPoE</a>)
を、
認証ID を &ldquo;xxxx@bizf.ocn.ne.jp&rdquo; から 
&ldquo;xxxx@bizf6.ocn.ne.jp&rdquo;
に変更して走らせてみる:
</p>
<pre class="terminal-scroll">
20:39:13 senri pppd[16587]: Plugin /etc/ppp/plugins/rp-pppoe.so loaded.
20:39:13 senri pppd[16587]: RP-PPPoE plugin version 3.10 compiled against pppd 2.4.5
20:39:13 senri pppd[16587]: pppd 2.4.5 started by root, uid 0
20:39:13 senri pppd[16587]: PPP session is 6394 (0x18fa)
20:39:13 senri pppd[16587]: Connected to 00:1e:13:c2:69:c2 via interface eth1
20:39:13 senri pppd[16587]: Using interface ppp0
20:39:13 senri pppd[16587]: Connect: ppp0 <--> eth1
20:39:13 senri pppd[16587]: Couldn't increase MTU to 1500
20:39:13 senri pppd[16587]: Couldn't increase MRU to 1500
20:39:13 senri pppd[16587]: PAP authentication succeeded
20:39:13 senri pppd[16587]: peer from calling number 00:1E:13:C2:69:C2 authorized
20:39:13 senri pppd[16587]: local  LL address <span style="color:green">fe80::fd61:ff9b:eb97:1f93</span>
20:39:13 senri pppd[16587]: remote LL address fe80::0090:1a00:41a3:d3f3
</pre>
<p>
PPP は 1 つ以上のネットワーク層プロトコルを選択できるので、
IPCP (IP Control Protocol) と IPv6CP (IPv6 Control Protocol) の両方が流れてくるのかと予想したのだが、
流れてきたのは IPv6CP のみだった。
つまり &ldquo;@bizf.ocn.ne.jp&rdquo; で IPv4 用、
&ldquo;@bizf6.ocn.ne.jp&rdquo; で IPv6 用、
計 2本の PPPoE セッションを張ることになる。
</p>
<p>
上記ログから分かる通り
Link Local とはいえ IPv6 なアドレス <span style="color:green">fe80::fd61:ff9b:eb97:1f93</span>
が割り振られたのだから、
IPv6 で通信できるはず。
試しに PPP の対向サーバへ ping を打ってみると、
ちゃんと応答が返ってきた:
</p>
<pre class="terminal">
senri:~ $ ping6 -c 3 fe80::0090:1a00:41a3:d3f3%ppp0<span style="color:red">↓</span>
PING fe80::90:1a00:41a3:d3f3%ppp0 (fe80::90:1a00:41a3:d3f3%ppp0): 48 data bytes
56 bytes from fe80::90:1a00:41a3:d3f3%ppp0: icmp_seq=0 ttl=255 time=3.936 ms
56 bytes from fe80::90:1a00:41a3:d3f3%ppp0: icmp_seq=1 ttl=255 time=4.050 ms
56 bytes from fe80::90:1a00:41a3:d3f3%ppp0: icmp_seq=2 ttl=255 time=4.176 ms
--- fe80::0090:1a00:41a3:d3f3%ppp0 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 3.936/4.054/4.176/0.098 ms
senri:~ $ 
</pre>
<p>
じゃ、
Global なアドレス
(固定アドレス契約なのでプレフィックスが
「ご利用内容のご案内」
に書かれていた)
を付けて routing 設定を行なえば Global に通信できそうと思い、
試してみると...
</p>
<pre class="terminal">
senri:/ # ifconfig eth0 add 2400:400d:100::1/56<span style="color:red">↓</span>
senri:/ # ip -6 route add default via fe80::0090:1a00:41a3:d3f3 dev ppp0<span style="color:red">↓</span>
senri:/ # ping6 www.kame.net<span style="color:red">↓</span>
PING www.kame.net (2001:200:dff:fff1:216:3eff:feb1:44d7): 48 data bytes
<span style="color:red">^C</span>--- www.kame.net ping statistics ---
15 packets transmitted, 0 packets received, 100% packet loss
senri:/ # 
</pre>
<p>
う〜ん、ダメか。
tcpdump を使って ppp0 に届く IPv6 パケットを監視してみたが、
OCN へ送信したパケットのみが表示され、
OCN 側からは何のパケットも届かない。
</p>
<p>
しかも、
<a href="http://fremont.gcd.org/test">他のサイト</a>から 
2400:400d:100::1 に対して ping6 を打ってみても何も届かない。
仮にこちらの設定に何か間違いがあったとしても、
OCN 側で 2400:400d:100::1 宛のパケットを routing していれば、
パケット自体は流れてきそうな気がするのだが...
少なくとも IPv4 の場合なら、
こちらの IP アドレス設定が間違っていても、
受け取れないだけでパケット自体は流れてくる。
</p>
<p>
ひょっとして、
「ご利用内容のご案内」に書かれていた
「2400:400d:100::」
というプレフィックスが間違っているんじゃ? と、
途方に暮れる。
</p>
<span id="more-812"></span>
<p>
仕方がないので
「<a href="http://www.ntt-east.co.jp/info-st/mutial/ngn/index.html">次世代ネットワークに関する情報</a>」
で提供されている仕様書にざっと目を通してみる。
すると、
<a href="http://www.ntt-east.co.jp/info-st/mutial/ngn/110601tunnel_uni.pdf">NGN IPv6 ISP接続＜トンネル方式＞UNI仕様書</a>の 7ページに、
IPv6 通信開始までの通信シーケンス図があった:
</p>
<a href="http://www.ntt-east.co.jp/info-st/mutial/ngn/110601tunnel_uni.pdf#page=7"><img src="http://www.gcd.org/sengoku/picts/IPv6_sequence.png" width="510" height="540" alt="IPv6 sequence diagram" /></a>
<p>
現在は IPv6CP まで完了している段階だから、
次は DHCPv6-PD Prefix 付与フェーズということになる。
この時まで、
DHCP (Dynamic Host Configuration Protocol) は自動設定のための仕掛けだから、
固定プレフィックスの場合は PD (Prefix Delegation, プレフィックス委譲) 
してもらうまでもないと思い込んでいた。
少なくとも IPv4 の場合は、
DHCP クライアントを動かさずに勝手に IP アドレスを割当てても通信できる。
</p>
<p>
OCN 側から一切パケットが流れてこないということは、
プレフィックス委譲してもらわなければ通信できない、
ということになる。
なぜ OCN は、
そんな不便な仕様にしているのか?
と疑問に思いつつも、
<a href="http://sourceforge.net/projects/wide-dhcpv6/">WIDE-DHCPv6</a>
をインストールして dhcp6c (DHCPv6 クライアント) を実行してみる:
</p>
<pre class="terminal">
senri:/ # cat /tmp/dhcp6c.conf<span style="color:red">↓</span>
interface ppp0 {
	send ia-pd 0;
};
id-assoc pd 0{
	prefix-interface eth0 {
		sla-len 0;
	};
};
senri:/ # dhcp6c -c /tmp/dhcp6c.conf ppp0<span style="color:red">↓</span>
</pre>
<p>
「send ia-pd 0」
が solicit message (要請メッセージ) を 
OCN のルータ (上記シーケンス図では 「IP通信網」) へ送信するための設定。
「ia-pd」
というのは Identity Association for Prefix Delegation 
(プレフィックス委譲ID) の意味で、
複数の委譲を行ないたいときにそれぞれを区別するための ID らしいが、
今回は一つだけなのでデフォルトの 0 を指定。
</p>
<p>
「id-assoc pd 0」
がプレフィックス委譲ID が 0 のもの (今回は一つだけなので 0 しかない) 
に関する設定で、
「prefix-interface eth0」
と指定することによって、
OCN から委譲されたプレフィックスを 
eth0 インタフェースに割当てる。
</p>
<p>
dhcp6c を実行しつつ tcpdump で ppp0 での通信を監視してみると:
</p>
<pre class="terminal-scroll">
senri:/ # tcpdump -i ppp0<span style="color:red">↓</span>
tcpdump: WARNING: ppp0: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ppp0, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
23:38:43.826300 IP6 <span style="color:green">fe80::fd61:ff9b:eb97:1f93</span>.546 > ff02::1:2.547: dhcp6 solicit
23:38:43.831744 IP6 fe80::90:1a00:41a3:d3f3.547 > <span style="color:green">fe80::fd61:ff9b:eb97:1f93</span>.546: dhcp6 advertise
23:38:44.871958 IP6 <span style="color:green">fe80::fd61:ff9b:eb97:1f93</span>.546 > ff02::1:2.547: dhcp6 solicit
23:38:44.876597 IP6 fe80::90:1a00:41a3:d3f3.547 > <span style="color:green">fe80::fd61:ff9b:eb97:1f93</span>.546: dhcp6 advertise
</pre>
<p>
無事、
solicit message が OCN へ送られ、
シーケンス図通り OCN から advertise が返ってきていることが分かる。
IPv6 のアドレスは桁数が多くて目がチラチラする (^^; ので、
こちら側
(シーケンス図では 「端末機器」) のアドレス 
<span style="color:green">fe80::fd61:ff9b:eb97:1f93</span> 
を<span style="color:green">緑色</span>で表した。
</p>
<p>
シーケンス図によると advertise を受け取った後、
端末機器は Request を送らなければならないのだが、
なぜか 1秒待って solicit を再送している。
これが今回唯一のハマったところ。
原因を探るために DHCPv6 の仕組みを一通り調べる羽目になった。
</p>
<p>
原因は分かってみれば単純で、
単に ip6tables で advertise パケットを落とす設定になっていただけ。
そのため dhcp6c が advertise パケットを受け取れず、
solicit の再送を繰り返していた。
Link Local からの UDP 546番ポート宛のパケットを受け入れるよう設定したら、
無事 DHCPv6-PD のシーケンスも完了して IPv6 通信を開始することができた。
</p>
<p>
より正確に言えば、元々
「-m conntrack --ctstate ESTABLISHED,RELATED」
などと送信パケットと関連があるパケットは受け入れる設定にしていたが、
DHCPv6 の solicit は ff02::1:2 宛、
つまり全ルーターアドレス宛のマルチキャストであるのに対し、
OCN から返ってくる advertise は (当然) ユニキャストであるため、
関連があるパケットとは見なされず落としてしまっていた。
</p>
<p>
以下のような ipv6-up / ipv6-down スクリプト
(IPv6 接続/切断時に 
<a href="http://ppp.samba.org/ppp/download.html">pppd</a> が呼び出すスクリプト)
を書いて、
dhcp6c および <a href="http://www.litech.org/radvd/">radvd</a> が自動的に起動/終了するようにした。
</p>
<pre class="code">
#!/bin/sh
PATH=/usr/bin:/usr/sbin:/bin:/sbin
. /etc/rc.d/hostname
command=$0
interface=$1
device=$2
speed=$3
local=$4
remote=$5
ipparam=$6
echo `date "+%F %T"` "$0" "$@" &gt;&gt; /var/log/pppd-ip6.log
case $command in
    *ipv6-up)
	updown="up"
	cat &gt; /var/run/dhcp6c.$interface.conf &lt;&lt;EOF
interface $interface {
	send ia-pd 0;
};
id-assoc pd 0{
	prefix-interface $INNER_DEV {
		sla-len 0;
	};
};
EOF
	dhcp6c -c /var/run/dhcp6c.$interface.conf -p /var/run/dhcp6c.$interface.pid $interface
	;;
    *ipv6-down)
	updown="down"
	if [ -f /var/run/dhcp6c.$interface.pid ]; then
	    kill `cat /var/run/dhcp6c.$interface.pid`
	fi
	;;
    *)
	exit 1
esac
case $ipparam in
    ngn6)
	if [ $updown = "up" ]; then
	    ip -6 addr add 2400:400d:100::1 dev $INNER_DEV
	    ip -6 route add default via $remote dev $interface
	    radvd -C /usr/etc/radvd.conf
	else
	    ip -6 addr del 2400:400d:100::1 dev $INNER_DEV
	    if [ -f /var/run/radvd.pid ]; then
		kill `cat /var/run/radvd.pid`
	    fi
	fi
	;;
    *)
	exit 1
esac
</pre>
<p>
dhcp6c によって自動的に割当てられる IPv6 アドレスの他に、
対外サービス用として
<a href="http://[2400:400d:100::1]/test">2400:400d:100::1</a>
も割当てるようにしている。
</p>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2011/06/812/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Osukiniサーバを使ってみた 〜 仮想コンソールが提供されない VPS で独自OS をインストールする方法</title>
		<link>http://www.gcd.org/blog/2011/03/772/</link>
		<comments>http://www.gcd.org/blog/2011/03/772/#comments</comments>
		<pubDate>Sun, 06 Mar 2011 23:36:38 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=772</guid>
		<description><![CDATA[今まで米国の VPS を使っていたが、 いつのまにか日本の VPS もリーズナブルな値段になってきたらしい。 「さくらのVPS」 がメモリ 512MB で月額 980円、 「Osukini サーバ」 ST プラン がメモ [...]]]></description>
			<content:encoded><![CDATA[<p>
今まで<a href="http://www.gcd.org/blog/2010/06/590/">米国の VPS</a> を使っていたが、
いつのまにか<a href="http://tanaka.sakura.ad.jp/2011/02/vps-sakura-saases-serversman.html">日本の VPS もリーズナブルな値段になってきた</a>らしい。
「<a href="http://vps.sakura.ad.jp/">さくらのVPS</a>」 
がメモリ 512MB で月額 980円、
「<a href="http://www.saases.jp/vps/">Osukini サーバ</a>」 ST プラン
がメモリ 1GB, ディスク 100GB で月額 980円。
「さくらのVPS」
でも充分安いが、
「Osukini サーバ」
の安さは目を疑うレベル。
これはもう試してみるしかない。
</p>
<blockquote>
実は、
最初は
「さくらのVPS」 
を申し込んだのだが、
<a href="http://twitter.com/gcd_org/status/42815980136697856">rootパスワードが見つからない事件</a>でイカってキャンセルしてしまった。
申し込んだ直後に VPS が起動できたので、
さくら++ と思いつつ root 
でログインしようとしたらパスワードが分からず Web じゅう探しまわる羽目に。
万策尽きてサポートに電話したら 15分以上待たされ、
しかも担当者には root と言っても話が通じず、
別の番号にかけなおせと言われる始末。
後ほど届いた
「仮登録完了のお知らせ」
メールに root パスワードが書いてあったのだが、
メールが届く前に 
VPS のコントロールパネルをいじれて、
起動までできてしまう現在の仕様はいかがなものか。
</blockquote>
<p>
Osukini サーバの場合
OS は CentOS, Ubuntu, Debian から選べるが、
「<a href="http://www.gcd.org/blog/2007/09/129/#mydistribution">my distribution</a>」
以外は使う気が起きないので、
どうやって私の
「独自OS」
をインストールするか、
方法を考えてみた。
もちろん、
Osukini サーバのサポートの範囲外なので、
インストールする方法だけでなく、
トラブった時の復旧方法を考えておくことが重要。
</p>
<p>
Osukini サーバでは
仮想コンソール (hvc console, リモートコンソール) が提供されない。
OS の外部から操作する手段がほとんどなく、
ブートスクリプトの設定ミスなどで 
OS にリモートログインできなくなってしまったら最後、
OS の再インストールしか復旧手段はない (と思う)。
しかも、
(サポートの範囲外である)
独自OS の再インストールを支援してもらえるはずはないので、
せっかくインストールした独自OS は跡形もなく消えてしまう。
</p>
<blockquote>
再起動すれば復旧できる一時的な障害はここでは考えない。
設定ファイルなどを不適切な内容に書き換えてしまった結果、
再起動してもリモートログインできない状態が続くケースのみを考える。
実際、
サーバを運用しているとそういうトラブルはよくある。
ネットワーク設定を間違えて通信不能になるだけで、
他は全く正常でもリモートログインはできなくなるし、
あるいは sshd の設定を間違えて 
sshd が起動しなくなっただけでもリモートログインできなくなる。<br />
ネットワークが全く使えなくても、
sshd が立ち上がらなくなってしまっても、
OS 自体が正常に起動していれば
init から起動される getty は生きていることが多いので、
仮想コンソールさえ使えれば、
大抵はログインできて修復できる。
</blockquote>
<p>
他の VPS, 
例えば
<a href="http://www.gcd.org/blog/2010/02/544/">Linode</a> や 
<a href="http://www.gcd.org/blog/2010/06/590/">prgmr</a> などでは、
仮想コンソールが利用できる。
さらに、
両者とも <a href="http://wiki.xensource.com/xenwiki/PvGrub">GRUB</a> 
が使えるので、
複数 OS をインストールしておいて切り替えて使うこと
(いわゆる
「デュアルブート」)
ができる。
つまり一つの OS 上で何かトラブルが起きて OS が立ち上げ不能になっても、
別の OS (いわゆるレスキュー用 OS) を立ち上げて、
トラブった OS を修復することができる。
</p>
<p>
リアルサーバでは、
(仮想ではなく物理的な) コンソールが利用できる (当たり前)。
さらに、
トラブった時はレスキュー用の CD-ROM / 
USBメモリからブートして復旧作業を行なったりする。
VPS でもリアルサーバでも、
イザというときに備えて、
通常使う OS とは別に、
修復作業を行なうためのレスキュー用の OS を用意しておくことが極めて重要。
</p>
<p>
ところが、
レスキュー用 OS が一切利用できないばかりか、
仮想コンソールから操作することすらできないのが Osukiniサーバ。
トラブルに対して極めて脆弱であると言わざるを得ない。
</p>
<a href="http://www.gcd.org/sengoku/picts/Osukini.jpg"><img src="http://www.gcd.org/sengoku/picts/Osukini.jpg" width="343" height="408" class="pict-right" alt="Osukini Server Control Panel" /></a>
<p>
VPS にとって仮想コンソールは必須の機能だと思っていたので、
Osukiniサーバのコントロールパネルを見て驚いた。
「再起動」
「停止」
などの操作と、
OS の初期化しかできない
(値段相応? でも、
仮想コンソール機能は
Xen 標準なのに...)。
</p>
<p>
OS の初期化をすれば、
全てが消えてしまう。
いわば、
クリーンインストールしかできない CD-ROM からブートするようなもの。
トラブったからといって、
即クリーンインストールしたいという人がどれだけいると言うのか?
</p>
<p>
嘆いていても仕方がないので、
どうやってレスキュー用 OS 相当のことを実現するか考えてみる。
レスキュー用 OS を立ち上げることさえできれば、
独自 OS をインストールすることも可能になる。
こんなこともあろうかと、
2年前 (2008年10月) に作っておいた 
<a href="http://sourceforge.jp/cvs/view/stone/initramfs/ptyd.c?view=log">ptyd</a> (Pseudo TTY Daemon) が役に立ちそう。
</p>
<span id="more-772"></span>
<p>
ptyd はリモートログインを可能にする簡易サーバ。
<a href="http://www.gcd.org/blog/2007/09/129/">initramfs</a> の中に入れておいて、
リモートからのメンテナンスを実現する目的で作った。
特長はそのサイズで、
わずか 12KB 〜 17KB しかない。
<a href="http://www.openssh.com/">sshd</a> (400KB 以上) あるいは 
<a href="http://matt.ucc.asn.au/dropbear/dropbear.html">dropbear</a>
(100KB 程度)
では initramfs に入れるには大きすぎるので作ってみた次第。
今まで (仮想)コンソールが使えない状況に陥ったことがなかったので、
ptyd の出番はほとんどなかったのだが、
Osukiniサーバのおかげで日の目を見ることとなった。
</p>
<p>
initramfs というのは Linux OS 起動時に、
root ファイルシステムをマウントするために一時的に起動される簡易OS のこと。
マウントしたボリュームの /sbin/init へ処理を引き継いで短い役割を終える。
(昔の Unix を知ってる) 古い人には 「ミニルート」 (mini root)
と言った方が通りがよいかも。
initramfs は 2MB 程度と極小であるにも関わらず
OS としての機能は一通り備えているので、
「レスキュー用」
として使えなくもない
(そもそも昔のレスキュー用ディスクはフロッピーディスク一枚に収まった)。
ptyd があれば、
initramfs がリモートからログイン可能なレスキュー用 OS になるというわけ。
</p>
<pre class="terminal">
senri:~ $ ls -l ptyd
-rwxr-xr-x 1 sengoku user 17200 Mar  5 09:20 ptyd
senri:~ $ ./ptyd
@(#) $Id: ptyd.c,v 1.2 2011/03/03 10:29:43 hiroaki_sengoku Exp $
Usage: ptyd &lt;opt&gt;.. &lt;prog&gt; &lt;arg&gt;..
opt: -p &lt;port&gt;  ; listen port
     -c &lt;sec&gt;   ; wait &lt;sec&gt; for connection
     -i &lt;sec&gt;   ; &lt;sec&gt; before terminate idle connection
     -a &lt;file&gt;  ; read secret from &lt;file&gt;
     -d &lt;level&gt; ; debug level
senri:~ $ cat /tmp/SEED 
abcdefg
senri:~ $ ./ptyd -p 1234 -a /tmp/SEED /bin/sh
</pre>
<p>
などと ptyd を起動すると、
1234番ポート (-p オプションで指定, デフォルトは 9000番) で listen する。
放っておくと 10秒 (-c オプションで変更可能) でタイムアウトする。
-a オプションは今回追加した機能で、
リモートログインを受付ける際に認証を要求する。
「-a /tmp/SEED」
と指定することによりパスワードを /tmp/SEED から読み込む。
</p>
<p>
試しにリモートからログインしてみる:
</p>
<pre class="terminal">
asao:~ $ telnet senri 1234
Trying 192.168.1.15...
Connected to senri.
Escape character is '^]'.
1299285953@1234:
</pre>
<p>
認証方法はチャレンジ&amp;レスポンス方式 (APOP などと同じ)。
プロンプト
「1299285953@1234:」
にパスワード (上記 /tmp/SEED の内容) を追加した文字列の 
MD5 値を入力すればよい。
例えば /tmp/SEED の内容が
「abcdefg〈改行〉」
であるなら、
</p>
<pre class="terminal">
asao:~ $ echo '1299285953@1234:abcdefg' | openssl md5
ee5478287007435a75dd875e0e56270a
</pre>
<p>
なので、
「ee5478287007435a75dd875e0e56270a」
を入力する。
認証が成功すると、
ptyd は引数のコマンド
(上記の例では /bin/sh)
を実行し、
その標準入出力を疑似端末経由 (ptyd の名前の由来) 
でリモートからアクセスできるようにする。
つまり上記の例だと ptyd を実行したマシン (senri) 上で実行した /bin/sh を、
telnet を実行したマシン (asao) から操作できる。
</p>
<p>
initramfs の中の <a href="http://sourceforge.jp/cvs/view/stone/initramfs/init?view=log">/init スクリプトで ptyd を呼ぶ</a>ようにしておけば、
ブート時にレスキュー用 OS に切り替えることができる。
つまり何かトラブルが起きて (再起動しても) 
VPS にリモートログインできなくなってしまったとしても、
コントロールパネルで
「再起動」
あるいは
「強制停止」
ボタンを押し、
再起動中に ptyd がタイムアウトするまえにリモートからログインすればよい。
ログインしなければ ptyd がタイムアウト (デフォルトは 10秒) して、
OS が起動する。
</p>
<p>
具体的には以下のような感じ。
ptyd が listen するタイミングをとらえるために ping を打っておく。
再起動にともなってパケットが途絶えた後 (icmp_seq=6 以降)、
再びパケットが返ってくるようになった (icmp_seq=17 以降)
ことを確認してから、
1234番ポートに対して telnet を試みる。
</p>
<pre class="terminal">
senri:~ $ ping meguro
PING meguro.gcd.org (163.43.176.212): 48 data bytes
56 bytes from 163.43.176.212: icmp_seq=0 ttl=52 time=6.447 ms
56 bytes from 163.43.176.212: icmp_seq=1 ttl=52 time=6.303 ms
56 bytes from 163.43.176.212: icmp_seq=2 ttl=52 time=8.697 ms
56 bytes from 163.43.176.212: icmp_seq=3 ttl=52 time=8.132 ms
56 bytes from 163.43.176.212: icmp_seq=4 ttl=52 time=6.226 ms
56 bytes from 163.43.176.212: icmp_seq=5 ttl=52 time=7.810 ms

56 bytes from 163.43.176.212: icmp_seq=17 ttl=52 time=9.881 ms
56 bytes from 163.43.176.212: icmp_seq=18 ttl=52 time=5.948 ms
  C-c --- meguro.gcd.org ping statistics ---
19 packets transmitted, 8 packets received, 57% packet loss
round-trip min/avg/max/stddev = 5.948/7.431/9.881/1.329 ms
senri:~ $ telnet meguro 1234
Trying 163.43.176.212...
Connected to meguro.
Escape character is '^]'.
1299285953@1234:ee5478287007435a75dd875e0e56270a
# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/xvda3           112292896   6574080 100062332   6% /mnt
# cat /proc/mounts
rootfs / rootfs rw 0 0
none /proc proc rw,relatime 0 0
none /sys sysfs rw,relatime 0 0
none /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0
/dev/xvda3 /mnt ext3 ro,relatime,errors=continue,barrier=0,data=writeback 0 0
# ls --color=never /mnt
bin         home        lost+found  proc        srv         var
boot        initrd.img  media       root        sys         vmlinuz
dev         lib         mnt         sbin        tmp
etc         lib64       opt         selinux     usr
</pre>
<p>
ptyd のプロンプト
「1299285953@1234:」
に対して、
MD5 を算出して正しいレスポンス
「ee5478287007435a75dd875e0e56270a」
を入力することにより無事ログインできた。
</p>
<p>
「#」
は initramfs の /bin/sh のプロンプト。
通常起動時に root ディレクトリとなるボリューム /dev/xvda3 が 
/mnt ディレクトリにマウントされている。
このボリュームには、
Osukiniサーバ契約時にインストールしてもらった 
Debian squeeze がインストールされている。
</p>
<p>
この Debian を
/mnt/mnt/saases ディレクトリ以下に待避:
</p>
<pre class="terminal">
# mount -oremount,rw null /mnt
# mkdir /mnt/mnt/saases
# cd /mnt
# mv -i [a-i]* lib* media [o-z]* mnt/saases/
# ls --color=never 
lost+found  mnt
# 
</pre>
<p>
この Debian を使う予定はないので、
待避せずに消しちゃってもいいのだが、
まあディスク容量が余ってるのでとりあえず保存しておくということで。
</p>
<p>
そして、
あらかじめ /mnt/mnt/root にコピーしておいた
my distribution 一式を、
/mnt (つまり /dev/xvda3 ボリュームの root ディレクトリ) へ展開:
</p>
<pre class="terminal">
# cd mnt/root
# ls --color=never -la
drwxr-xr-x   14 0        0            4096 Mar  2 11:27 .
drwxr-xr-x    4 0        0            4096 Mar  4 11:09 ..
lrwxrwxrwx    1 0        0               7 May  3  2008 bin -> usr/bin
drwxr-xr-x    4 0        0            4096 Jan 12 10:03 boot
drwxr-xr-x    2 0        0            4096 Mar  2 10:21 dev
drwxr-xr-x   35 0        0            4096 Mar  1 10:34 etc
drwxr-xr-x    3 0        0            4096 Feb 22 05:59 home
drwxr-xr-x    3 0        0            4096 Dec 12 15:19 lib
lrwxrwxrwx    1 0        0               3 Oct 11  2009 lib64 -> lib
drwxr-xr-x    3 0        0            4096 Mar  2 11:27 mnt
drwxr-xr-x    2 0        0            4096 Mar  2 10:20 proc
drwxr-xr-x    3 0        0            4096 Mar  4 01:09 root
lrwxrwxrwx    1 0        0               8 Oct  9  2009 sbin -> usr/sbin
drwxr-xr-x    2 0        0            4096 Mar  2 10:21 sys
drwxrwxrwt    3 0        0            4096 Mar  4 00:57 tmp
drwxr-xr-x   15 0        0            4096 Feb 28 11:52 usr
drwxr-xr-x   12 0        0            4096 Jan 31 02:01 var
# mv -i * /mnt/
mv: overwrite '/mnt/mnt'? n
# mount -oremount,ro null /mnt
# exit
Connection closed by foreign host.
senri:~ $ 
</pre>
<p>
以上で、
Debian squeeze と、
my distribution との入れ替えが完了。
exit すれば ptyd が終了して、
/initramfs のブートシーケンスが再開し、
my distribution が起動する。
</p>
<pre class="terminal">
senri:~ $ ssh meguro
Last login: Sat Mar  5 22:41:49 2011 from gcd.org
Linux 2.6.35.11-x86_64.
meguro:~ $ df
Filesystem           1K-blocks      Used Available Use% Mounted on
rootfs               112292896  21192732  85443680  20% /
/dev/xvda3           112292896  21192732  85443680  20% /
none                    511188        20    511168   1% /dev
none                    511188         0    511188   0% /dev/shm
/dev/xvda3           112292896  21192732  85443680  20% /usr/bin/.suid
/dev/xvda3           112292896  21192732  85443680  20% /var/root/usr/bin/.suid
meguro:~ $ free
             total       used       free     shared    buffers     cached
Mem:       1022380    1005408      16972          0     195024     235496
-/+ buffers/cache:     574888     447492
Swap:      2104508        572    2103936
meguro:~ $
</pre>
<p>
広〜い (^^)。
これが月額 980円なのだからスゴイ。
</p>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2011/03/772/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Linode から prgmr.com へ乗り換えてみた</title>
		<link>http://www.gcd.org/blog/2010/06/590/</link>
		<comments>http://www.gcd.org/blog/2010/06/590/#comments</comments>
		<pubDate>Thu, 17 Jun 2010 00:44:22 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=590</guid>
		<description><![CDATA[今年 2月から使っている Linode (VPS, 仮想サーバ) は、 月額 $19.95 の最小プラン Linode 360 だと RAM 360MB なので WordPress を動かす (Apache+PHP+My [...]]]></description>
			<content:encoded><![CDATA[<p>
今年 
<a href="http://www.gcd.org/blog/2010/02/544/">2月から使っている Linode</a>
(VPS, 仮想サーバ) は、
月額 $19.95 の最小プラン Linode 360 だと RAM 360MB なので
<a href="http://www.gcd.org/blog/2010/01/494/">WordPress</a> 
を動かす
(Apache+PHP+MySQL)
には若干足らない (稀に 
<a href="http://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%A9%E3%83%83%E3%82%B7%E3%83%B3%E3%82%B0">スラッシング</a> 状態に陥って無反応になる)。
もちろん、
一つ上のプラン Linode 540 (RAM 540MB) にすればいいのであるが、
これだと月額 $29.95 でちょっと高い
(私の感覚だと月額 $20 あたりに心理的な壁がある)。
</p>
<p>
<a href="http://prgmr.com/xen/">prgmr.com</a> ならば、
RAM 512MB, ディスク 12GB が月額 $12 (年一括払いなら $115.2) 
で済むので乗り換えてみた。
Linode のようなユーザフレンドリーな Web ユーザインタフェースは無いし、
時々新規申込みの受付を中止したりする
(今も RAM 1024MB 以上のプランは受付けていない)
が、
逆に言えば
Web インタフェースが不要な人 (含む私) にとっては 
prgmr.com のシンプルさがむしろ望ましく、
レスキュー用のブートイメージなど必要最低限のものは揃っているので、
不便と感じることはない。
また、
いったん申込みが完了してしまえば受付の一時停止は関係ないわけで、
むしろサーバリソースに見合ったユーザ数しか受付けない姿勢は好感が持てる。
</p>
<p>
Web から Signup すると、
以下のようなメールが届く。
</p>
<pre class="code">
you ordered a xen vps, 512MiB ram, 12GiB Disk 80 GiB transfer Xen VPS,  $12/month username sengoku 

Before I can set you up, I need to know what distro you would like and an OpenSSH format public key (on a *NIX, run ssh-keygen  and send me either the id_dsa.pub or id_rsa.pub file in an attachment)    

on putty/windows, see

http://www.unixwiz.net/techtips/putty-openssh.html#keypair

and scroll down to the screen shot where it says 'openssh format public key for pasting into authorized_keys' - that is what I need.

thanks. 
</pre>
<p>
OpenSSH の公開鍵くらい Web から登録させればいいのにと思いつつ、
「I need to know what distro you would like」
などと聞いてくるということは多少の融通は聞くのかと思い、
パーティションの切り方をついでにリクエストしてみた (6/4 15:46)。
つまり、
<a href="http://www.gcd.org/blog/2007/09/129/">自分で作った Linux distro</a>
(配布していないから
「distribution」 と呼ぶのは正確性に欠けるのだが、
それなら何て呼んだらいいのだろう?)
をインストールしたいから、
最小のパーティション (例えば 2GB) に 
ubuntu をインストールしておいてもらえるか？
などとリクエストしてみた:
</p>
<pre class="code">
Hi,

At Fri, 04 Jun 2010 05:48:05 +0000,
support@prgmr.com wrote:

> Before I can set you up, I need to know what distro you would like and
> an OpenSSH format public key (on a *NIX, run ssh-keygen and send me
> either the id_dsa.pub or id_rsa.pub file in an attachment)

I'd like to use my own linux-based image that is running on
fremont.gcd.org (linode) now.  Is it possible to make two partitions
(plus swap partition) ?  One is for a normal ubuntu, and the other is
for my own image.  I'd like to switch these two OSs by pv-grub.

Please minimize the ubuntu partition in order to maximize my own image.
For example, 2GiB for ubuntu, 1GiB for swap, and 9GiB for me.

I've attached my public key id_dsa.pub at the end of this mail.


#9860.
http://www.gcd.org/sengoku/		Hiroaki Sengoku &lt;sengoku@gcd.org&gt;
</pre>
<p>
すると、
4日後の 6/8 09:03 に返事が来た。
リクエストはあっさり無視されて、
ディスク一杯に ubuntu をインストールした状態の VPS が提供されただけ。
個別対応しないのであれば、
自動化したほうがいいような。
</p>
<pre class="code">
Your vps is setup now, your host server is whetstone.prgmr.com.
Login with your username and ssh key and follow the instructions
at <a href="http://book.xen.prgmr.com/mediawiki/index.php/Quickstart">http://book.xen.prgmr.com/mediawiki/index.php/Quickstart</a>
For more information about repartitioning, see
<a href="http://book.xen.prgmr.com/mediawiki/index.php/Repartition">http://book.xen.prgmr.com/mediawiki/index.php/Repartition</a>
The default root password for the vps is password, and you
should receive a bill soon by email. If you have any questions
please email support@prgmr.com. Thanks very much.
Nick Schmalenberger
</pre>
<p>
言われた通り ssh でログインしてみる:
</p>
<pre class="terminal">
senri:/home/sengoku % ssh whetstone.prgmr.com
The authenticity of host 'whetstone.prgmr.com (65.49.73.105)' can't be established.
RSA key fingerprint is 97:52:07:d4:52:8d:c0:cc:13:c7:fb:5d:5d:1b:ab:b4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'whetstone.prgmr.com,65.49.73.105' (RSA) to the list
of known hosts.
Name                                        ID   Mem VCPUs      State   Time(s)
sengoku                                    xxx   512     1     -b----     11.5

Options for sengoku
1. console
2. create/start
3. shutdown
4. destroy/hard shutdown
5. reboot
6. swap i386/amd64 bootloaders (pvgrub)
7. exit
press the number> 
</pre>
<p>
「6. swap i386/amd64 bootloaders (pvgrub)」
が目を引くが、
PV-GRUB は DomainU 側で実行されるので、
PV-GRUB にて 32bit/64bit の両方のカーネルを立ち上げることができず、
PV-GRUB を実行する前にどちらか一方に決めておく必要がある、
ということのようだ。
デフォルトは (残念なことに) 32bit になっていて、
セットアップしてもらった ubuntu も 32bit 版だった。
</p>
<p>
「1」
を入力して VPS のコンソールを表示させてみる:
</p>
<pre class="terminal">
press the number> 1

Ubuntu 10.04 LTS sengoku.xen.prgmr.com hvc0

sengoku.xen.prgmr.com login: root
Password:
Last login: Mon May 31 23:51:42 UTC 2010 on hvc0
Linux sengoku.xen.prgmr.com 2.6.32-22-generic-pae #33-Ubuntu SMP Wed Apr 28 14:57:29 UTC 2010 i686 GNU/Linux
Ubuntu 10.04 LTS

Welcome to Ubuntu!
 * Documentation:  https://help.ubuntu.com/
root@sengoku:~# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/xvda1            11812024   1068124  10143876  10% /
none                    249488       136    249352   1% /dev
none                    253744         0    253744   0% /dev/shm
none                    253744        28    253716   1% /var/run
none                    253744         0    253744   0% /var/lock
none                    253744         0    253744   0% /lib/init/rw
none                  11812024   1068124  10143876  10% /var/lib/ureadahead/debugfs
root@sengoku:~# 
</pre>
<p>
前掲のメールに書いてあった通り、
ユーザ 「root」
パスワード 「password」
でログインできた。
初めてログインしたのに
「Last login: Mon May 31 23:51:42 UTC 2010 on hvc0」
などと出ているが、
この ubuntu イメージを作ったとき試しに root 
でログインしたのが残ってしまったのだと思われる。
新規ユーザのセットアップのたびに、
5/31 に作ったイメージを使いまわしているのだろう。
</p>
<p>
ディスク 12GB のプランのはずなのに 1GB ほど足りないのは 
swap パーティションがあるから？と思い、
fdisk で確認してみる:
</p>
<pre class="terminal">
root@sengoku:~# fdisk -l /dev/xvda

Disk /dev/xvda: 12.8 GB, 12884901888 bytes
255 heads, 63 sectors/track, 1566 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot      Start         End      Blocks   Id  System
/dev/xvda1               1        1494    12000523+  83  Linux
root@sengoku:~# 
</pre>
<p>
シリンダの最大値は 1566 なので、
72 (=1566-1494) シリンダが使われずに余っている。
試しにパーティションを割当ててみたら普通に使えた。
</p>
<p>
自前 distro 10GB, swap 1GB, ubuntu 1GB といった切り分けかたにするため、
レスキュー用のカーネル 「CentOS 5.5 rescue」 をブートしてみる:
</p>
<pre class="terminal">
    GNU GRUB  version 0.97  (65536K lower / 0K upper memory)

 +-------------------------------------------------------------------------+
 | user bootloader configuration                                           |  
 | CentOS 5.5 rescue (2.6.18-194.3.1.el5xen)                               |
 | CentOS 5.5 installer                                                    |
 | NetBSD 5.0.2 installer                                                  |
 | Ubuntu 10.04 LTS installer                                              |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |  
 +-------------------------------------------------------------------------+
    Use the ^ and v keys to select which entry is highlighted.
    Press enter to boot the selected OS, 'e' to edit the
    commands before booting, or 'c' for a command-line.
</pre>
<p>
二行目の 
「CentOS 5.5 rescue (2.6.18-194.3.1.el5xen)」
を選択してブート。
root でログイン (パスワードは無し) して
fdisk を使ってパーティションを切り直した。
せっかくセットアップしてもらった Ubuntu も、
3分と使わずにお別れ。
</p>
<pre class="terminal">
CentOS release 5.5 (Final)
Kernel 2.6.18-194.3.1.el5xen on an x86_64

sengoku.xen.prgmr.com login: root
[root@sengoku ~]# 
	...
[root@sengoku ~]# fdisk -l /dev/xvda

Disk /dev/xvda: 12.8 GB, 12884901888 bytes
255 heads, 63 sectors/track, 1566 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot      Start         End      Blocks   Id  System
/dev/xvda1               1        1306    10490413+  83  Linux
/dev/xvda2            1307        1438     1060290   82  Linux swap / Solaris
/dev/xvda3            1439        1566     1028160   83  Linux
[root@sengoku ~]# mount /dev/xvda1 /mnt
[root@sengoku ~]# 
</pre>
<p>
私は個人で管理している Linux マシン 
(自宅と職場と VPS 合わせて十数台, 
もちろん商用サービスを行なっているサーバではない)
のディスクの内容を、
コマンド一発で同一に保てるようにしている
(つまりマスタで変更/追加/削除したファイルを各マシンへコピー/各マシンで削除)。
</p>
<p>
もちろん、
各マシンの性能や 32bit/64bit などの CPU 種別、
ディスクの容量に応じて違う部分もあるわけで、
この同期コマンドは同期先の各マシンに合わせてコピーしないファイルのリストを生成した上で 
rsync を実行している。
なので、
新しくマシンを増やすときもマスタ上で同期コマンドを実行するだけで、
インストールが完了するはずだが...
</p>
<span id="more-590"></span>
<pre class="terminal">
senri.gcd.org:/ # mirror -v -r /mnt 'core64[sengoku.xen.prgmr.com]'
BEGIN core64[sengoku.xen.prgmr.com] Tue Jun  8 13:59:14 JST 2010
type: core64
target: sengoku.xen.prgmr.com
rsync --relative -a --numeric-ids --delete -v /bin /etc /lib /sbin /boot /lib64 /usr32 sengoku.xen.prgmr.com:/mnt/
bash: rsync: command not found
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: remote command not found (code 127) at io.c(600) [sender=3.0.6]
senri.gcd.org:/ # 
</pre>
<p>
ありゃ、レスキュー用のディスクには rsync がインストールされていないのか...
</p>
<p>
レスキュー用のディスクは (当然) 書き込みができないので、
rsync をインストールするのも
(tmpfs をマウントすれば可能だが) 
面倒。
そこで先に /dev/xvda3 パーティション (1GB) に 
ubuntu をインストールすることにする。
幸い /dev/xvdf で CentOS, Debian, Ubuntu, NetBSD 
それぞれのディスクを tar+gzip で固めたもの (tar ball) が提供されているので、
64bit 版 ubuntu と思われる ubuntu64.tar.gz を /dev/xvda3 へ展開:
</p>
<pre class="terminal">
[root@sengoku ~]# mkdir /tmp/xvdf
[root@sengoku ~]# mount /dev/xvdf /tmp/xvdf
mount: block device /dev/xvdf is write-protected, mounting read-only
[root@sengoku ~]# ls -ln /tmp/xvdf
total 1295052
-rw-r--r-- 1 500 500 242130294 May 17 18:10 centos32.tar.gz
-rw-r--r-- 1 500 500 264319597 May 17 16:25 centos64.tar.gz
-rw-r--r-- 1   0   0 184512292 Jun  3 23:30 debian32.tar.gz
-rw-r--r-- 1   0   0 185773340 Jun  3 11:35 debian64.tar.gz
drwx------ 2   0   0     16384 May 24 04:08 lost+found
-rw-r--r-- 1 500 500   4999925 Feb 28 00:46 netbsd64.tar.gz
-rw-r--r-- 1   0   0 219621325 Jun  3 11:30 ubuntu32.tar.gz
-rw-r--r-- 1   0   0 223411583 Jun  3 11:30 ubuntu64.tar.gz
[root@sengoku ~]# mkdir /tmp/xvda3
[root@sengoku ~]# mount /dev/xvda3 /tmp/xvda3
[root@sengoku ~]# tar xzf /tmp/xvdf/ubuntu64.tar.gz -C /tmp/xvda3
[root@sengoku ~]# vi /tmp/xvda3/boot/grub/menu.lst
</pre>
<p>
この tar ball は /dev/xvda1 に展開することを想定しているらしく、
/boot/grub/menu.lst に (hd0,0) と書いてあるので全て (hd0,2) に置換。
/dev/xvda1 にも /boot/grub/menu.lst を置いて以下の行を追加し、
/dev/xvda3 の menu.lst も起動できるようにしておく。
</p>
<pre class="code">
title  Ubuntu
root (hd0,2)
configfile /boot/grub/menu.lst
</pre>
<p>
前述した 
「swap i386/amd64 bootloaders (pvgrub)」
を選択して PV-GRUB を 64bit に切り替えておいて、
このインストールしたばかりの Ubuntu を立ち上げる。
そして前述した mirror コマンドをマスタで実行すれば、
/dev/xvda1 への自前 distro のインストールが完了する。
</p>
<p>
もちろん、
ホスト名などの設定ファイルや /etc/fstab など各マシンで固有のファイル、
/var/log, /tmp などのディレクトリは
mirror コマンドではコピー対象外だが、
これらのファイルやディレクトリが存在しない場合は、
ブートスクリプトにおいて勝手に生成する仕掛けになっている。
</p>
<blockquote>
<a href="http://book.xen.prgmr.com/mediawiki/index.php/Using_a_kernel.org_kernel_under_xen">Using a kernel.org kernel under xen</a> に書かれているように、
Linux カーネルを Xen のゲスト OS として動かすには
Paravirtualized guest support (CONFIG_PARAVIRT_GUEST) 
を有効にしておく必要がある。<br />
また、xen_blkfront.ko (CONFIG_XEN_BLKDEV_FRONTEND) と 
xen_netfront.ko (CONFIG_XEN_NETDEV_FRONTEND) の両モジュールも必要。
もちろんカーネルに組み込んでおいてもいいが、
私は実サーバと仮想サーバの両方で同じカーネルを使いたいので、
モジュールにしている。
</blockquote>
<p>
これで /dev/xvda1 からブートすれば、
使いなれた自前 distro 環境が立ち上がるはず...
</p>
<pre class="terminal">
root (hd0,0)
 Filesystem type is ext2fs, partition type 0x83
kernel /boot/linuz-2.6.31.13-x86_64 root=/dev/xvda1
initrd /boot/initz-2.6.31.13-x86_64

ERROR Invalid kernel: xc_dom_probe_bzimage_kernel unable to LZMA decompress kernel

xc_dom_bzimageloader.c:393: panic: xc_dom_probe_bzimage_kernel unable to LZMA decompress kernel
ERROR Invalid kernel: xc_dom_find_loader: no loader found

xc_dom_core.c:517: panic: xc_dom_find_loader: no loader found
xc_dom_parse_image returned -1

Error 9: Unknown boot failure

Press any key to continue...
</pre>
<p>
うっ、
LZMA 圧縮したカーネル (CONFIG_KERNEL_LZMA) には対応していないのか。
Linode の場合は非圧縮のカーネルのみブートできたのだが、
prgmr.com の場合は gzip 圧縮したカーネル (CONFIG_KERNEL_GZIP) 
のみブートできるようだ。
VPS ごとに起動できるカーネルの種類が異なっていて面倒。
</p>
<p>
gzip 圧縮したカーネル 
「linuz-2.6.31.13-gz-x86_64」
を用意して再度挑戦:
</p>
<pre class="terminal">
root (hd0,0)
 Filesystem type is ext2fs, partition type 0x83
kernel /boot/linuz-2.6.31.13-gz-x86_64 root=/dev/xvda1
initrd /boot/initz-2.6.31.13-x86_64

close blk: backend at /local/domain/0/backend/vbd/xxx/51712
close blk: backend at /local/domain/0/backend/vbd/xxx/51776
close blk: backend at /local/domain/0/backend/vbd/xxx/51792
[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Linux version 2.6.31.13-x86_64 (sengoku@senri.gcd.org) (gcc version 4.3.4 (GCC) ) #1 SMP Tue Apr 13 17:48:44 JST 2010
[    0.000000] Command line: root=/dev/xvda1
	...
[    0.003333] installing Xen timer for CPU 0
[    0.003333] Calibrating delay loop (skipped), value calculated using timer frequency.. 4003.20 BogoMIPS (lpj=6669120)
	...

Welcome to Linux 2.6.31.13-x86_64.

none.gcd.org login:
</pre>
<p>
無事ブートした。
</p>
<p>
あとは Linode の時と同様に、
自宅サーバ <a href="http://gw.gcd.org/test">gw.gcd.org</a> 
との間にトンネルを設置したり、 
gw.gcd.org 上の MySQL から VPS 上の MySQL 
に対してレプリケーションを行なうようにしたり、
<a href="http://www.gcd.org/blog/2010/03/573/">MyDNS</a> を動かしたり、
ns4.gcd.org を 65.49.73.116 へ向けたり、
等々... 
</p>
<p>
最後に、 
<a href="http://www.gcd.org/test">www.gcd.org</a> の 
IP アドレスとして VPS の IP アドレス 65.49.73.116 を追加した。
</p>
<p>
すなわち、
いま読者の皆さんが読んでるこのページは、 
1/2 の確率で prgmr.com の VPS が送信したページということになる。
すでに一週間近くこの状態を続けているが、
Linode 360 のときよりはメモリに余裕がある:
</p>
<pre class="terminal">
prgmr:/home/sengoku % free
             total       used       free     shared    buffers     cached
Mem:        534796     501456      33340          0      35568     114452
-/+ buffers/cache:     351436     183360
Swap:      1060280        556    1059724
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/06/590/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Linux サーバ同士をシリアルケーブルでつなぐには寡黙な getty が必要</title>
		<link>http://www.gcd.org/blog/2010/04/579/</link>
		<comments>http://www.gcd.org/blog/2010/04/579/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 22:48:14 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=579</guid>
		<description><![CDATA[私の自宅 LAN (GCD) の対外ルータである 2台の Linux サーバ (senri と asao) は、 双方のシリアルポート (つまり DSUB9ピンの RS-232C) 同士がつながっている。 いまさら RS [...]]]></description>
			<content:encoded><![CDATA[<p>
私の<a href="http://gw.gcd.org/Welcome.ja.html">自宅 LAN (GCD)</a> 
の対外ルータである 
2台の Linux サーバ 
(<a href="http://www.gcd.org/sengoku/machine.ja.html#SENRI">senri</a> と 
<a href="http://www.gcd.org/sengoku/machine.ja.html#ASAO">asao</a>) は、
双方のシリアルポート (つまり DSUB9ピンの 
<a href="http://ja.wikipedia.org/wiki/RS-232">RS-232C</a>) 
同士がつながっている。
いまさら RS-232C ? と思う人が大半だと思うが、
最後の命綱としての安心感は測り知れない。
</p>
<p>
つまりサーバをリモートから (ネットワーク経由で ssh ログインして) 
いじっていると、
ちょっとしたミスで操作不能に陥ってしまうことがある。
例えば NIC を down してしまった、
iptables で DROP する設定にしてしまった、
sshd を殺してしまった、
等々。
あるいは、
リモートから再起動を行なう場合、
起動スクリプトのほんの些細な設定ミスで、
リモートからは操作不能になってしまう。
</p>
<p>
こんなとき、
2台のサーバのシリアルポート同士を
RS-232C クロスケーブル (シリアルケーブル) で接続しておけば、
生きている方のサーバからシリアルケーブル経由でログインできて、
リモートからは操作不能に陥ったサーバのネットワーク設定を修復したり、
sshd を起動し直したり、
あるいは再起動の場合であれば、
<a href="http://www.gnu.org/software/grub/index.html">GRUB</a> 
を操作することができる
(<a href="http://www.gcd.org/blog/2008/03/151/">ML115</a> 
などであれば BIOS 設定までいじれる)。
</p>
<p>
<a href="http://www.klab.jp/dsas/">職場のサーバ群</a>は、
もちろん <a href="http://www.intel.com/design/servers/ipmi/">IPMI</a> を利用していて大抵の操作は 
IPMI でできてしまうのでシリアルケーブルの出番はあまりないのだが、
それでもやはり最後の命綱がある安心感は何ものにも代えがたい。
しかも数百円のケーブル一本で済むのだから、
掛けておくべき保険だろう。
</p>
<p>
シリアルケーブル経由でサーバにログインするには、
そのサーバの /dev/ttyS0 (シリアルポート COM1 の場合) で 
<a href="http://www.gcd.org/sengoku/docs/NikkeiLinux01-03.pdf">getty</a> 
を動かしておく必要がある。
例えば GCD の場合 <a href="http://mgetty.greenie.net/">mgetty</a> を使っているので、
/etc/inittab に
</p>
<pre class="code">
# Dialin lines
s0:25:respawn:/usr/sbin/mgetty ttyS0
</pre>
<p>
などと設定している。
シリアルケーブルでつながった 2台の Linux サーバ senri と asao
において、
asao から senri へシリアルケーブル経由でアクセスすると、
senri のシリアルコンソールが表示される:
</p>
<pre class="terminal">
asao:/home/sengoku % cu ttyS0
Connected.

Welcome to Linux 2.6.31.13-x86_64.


senri.gcd.org!login: root
otp-md5 299 se5047 ext
Response or Password:
Last login: (null) on /dev/ttyS0

senri.gcd.org:/root # tty
/dev/ttyS0
senri.gcd.org:/root # 
</pre>
<p>
たとえ senri のネットワーク機能が全滅していたとしても、
mgetty は /sbin/init から直接起動されるので影響を受けない。
シリアルコンソールから root でログインして復旧作業を行なうことができる。
逆に、
asao のネットワーク機能が全滅したときは、
senri へ ssh ログインした上で
asao へシリアルケーブル経由でアクセスすればよい。
ネットワーク機能が麻痺する可能性があるような危険な作業を
senri と asao の両方で同時に行なったりしないようにすれば、
何が起きてもどちらかは生きていることが保証できる。
</p>
<p>
さて、
両サーバで mgetty が動いているときは以上で問題無いのだが、
どちらかのサーバを再起動するときなど、
mgetty 以外のものが動くときは状況が変わってくる。
</p>
<span id="more-579"></span>
<p>
例えば asao を再起動すると、
まず <a href="http://www.gnu.org/software/grub/grub-2.en.html">GRUB2</a> が動く。
</p>
<blockquote>
以前は <a href="http://www.gnu.org/software/grub/grub-legacy.en.html">GRUB Legacy</a> を使っていたのだが、
ハードディスクを入れ替えたとき
(数年ごとに 2〜3倍の容量のハードディスクと交換している)
勢い余って 
root パーティションまで ext4 にしてしまい、
GRUB Legacy だと ext4 をサポートしていないことに気付き、
慌てて GRUB2 をインストールした。
</blockquote>
<p>
GRUB2 では、
/boot/grub/grub.cfg に
</p>
<pre class="code">
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
terminal_input serial console
terminal_output serial console
</pre>
<p>
などと設定しておくことにより、
VGA コンソール (/dev/tty1) とシリアルコンソール (/dev/ttyS0) の両方で 
GRUB の操作 (つまり起動するパーティション/カーネルを選択する等) 
を行なうことができる。
つまり VGA コンソールで操作しているときも、
同じ画面がシリアルコンソールでも出力され、
途中からシリアルコンソールで操作することも可能。
</p>
<p>
GRUB Legacy では、
最初にキー入力があった側のコンソールでの操作が有効になる仕様だった。
最初に VGA コンソール (つまり PC に直接接続された PS/2 or USB キーボード) 
からキー入力を行なえば、
以後キーボードからの操作が有効になり、
最初にシリアルコンソールからキー入力を行なえば、
以後シリアルケーブル経由の操作が有効になる。
</p>
<p>
一見 GRUB2 の仕様のほうが便利のように見えるが、
二つの Linux サーバをシリアルケーブルでつないでいると、
GRUB2 の仕様は少し困る。
つまり以下のような GRUB2 
のメニュー画面が常にシリアルケーブルへも出力されてしまい、
それが相手サーバの mgetty への入力として扱われてしまうので、
mgetty が余計な動きをする。
</p>
<pre class="terminal">

                             GNU GRUB  version 1.97

 +--------------------------------------------------------------------------+
 |GNU/Linux 2.6.31.13-x86_64                                                |
 |GNU/Linux 2.6.31.13-i386                                                  |
 |GNU/Linux 2.6.31.12-x86_64                                                |
 |GNU/Linux 2.6.31.12-i386                                                  |
 |GNU/Linux 2.6.31.11-x86_64                                                |
 |GNU/Linux 2.6.31.11-i386                                                  |
 |GNU/Linux 2.6.30.9-x86_64                                                 |
 |GNU/Linux 2.6.30.9-i386                                                   |
 |GNU/Linux 2.6.27.34-x86_64                                                |
 |GNU/Linux 2.6.27.34-i386                                                  |
 |Memtest86+                                                                |
 +--------------------------------------------------------------------------+

      Use the ^ and v keys to select which entry is highlighted.
      Press enter to boot the selected OS, 'e' to edit the commands before
      booting or 'c' for a command-line.


</pre>
<p>
すなわち mgetty は何かキー入力があると、
前述したような 「Welcome to Linux ...」 
メッセージとログインプロンプト 「senri.gcd.org!login: 」 を出力する。
この出力がそのまま相手サーバの GRUB2 への入力として扱われてしまう。
</p>
<p>
つまり 「Welcome to Linux ...」 メッセージは前と後ろに
「改行コード」 が出力されるので、
GRUB2 へ改行が入力されてしまう
(改行コード以外のアルファベットが GRUB2 によってコマンドとして認識されると、
もっと面倒なことになる)。
すなわち VGA コンソールに表示された GRUB2 メニューにおいて、
どの OS を起動しようか選ぼうとする間もなく、
改行キーが (シリアルコンソールから) 
入力されたことになってしまってデフォルトの OS が勝手に起動してしまう。
</p>
<p>
一方、
GRUB Legacy の場合は、
何かキー入力があるまでシリアルケーブル側へは何も出力されない
(正確に言うと 「Press any key to continue.」 というメッセージのみ出力される)。
相手サーバの mgetty が何か出力すると破綻してしまうが、
以下のような簡単なパッチを GRUB Legacy にあてることにより、
「any key」 ではなく mgetty が出力しないコードを入力したときのみ
シリアルコンソールへ切り替えるようにできる。
以下のパッチでは ^G (コントロールキーを押しながら G)
をシリアルコンソールで入力したときのみ、
GRUB Legacy のメニュー画面がシリアルコンソールに表示されるようにしている。
</p>
<pre class="code">
diff -ur grub-0.97.org/stage2/builtins.c grub-0.97/stage2/builtins.c
--- grub-0.97.org/stage2/builtins.c	2005-02-16 06:58:23.000000000 +0900
+++ grub-0.97/stage2/builtins.c	2009-04-06 13:31:02.288106510 +0900
@@ -4208,10 +4208,13 @@
 		{
 		  if (term_table[i].checkkey () >= 0)
 		    {
-		      (void) term_table[i].getkey ();
-		      default_term = i;
-		      
-		      goto end;
+                      int term_key_code;
+                      term_key_code = term_table[i].getkey();
+		      if( ( (char)term_key_code ) == 7 )
+                      {
+		      	default_term = i;
+		      	goto end;
+                      }
 		    }
 		}
 	    }
@@ -4227,7 +4230,7 @@
 		    if (term_bitmap &amp; (1 &lt;&lt; i))
 		      {
 			current_term = term_table + i;
-			grub_printf ("\rPress any key to continue.\n");
+			grub_printf ("\rPress Ctrl+G key to continue.\n");
 		      }
 		  
 		  /* Restore CURRENT_TERM.  */
</pre>
<p>
GRUB2 でも同様のパッチを作ろうと思ったのだが、
GRUB2 は GRUB Legacy と違って、
シリアルと VGA を切り替える仕組みにはなっておらず、
GRUB Legacy の時のような一行パッチでは済みそうにない。
</p>
<p>
また、
GRUB2 はまだ開発途上ということもあり、
バージョンが上がるたびにパッチを作るのも大変そうである。
GRUB2 以外のブートローダに乗り換える可能性もあるかもしれない。
一方 mgetty は既に枯れたソフトウェアと言ってよい。
そこで mgetty にパッチをあてて、
mgetty が 「Welcome to Linux ...」 
メッセージとログインプロンプトを出力するのを抑制することにした。
mgetty が寡黙になりさえすれば、
相手サーバで GRUB2 だろうがその他のブートローダだろうが、
何が動いていても問題は起きない。
</p>
<p>
シリアルコンソールから ^G を入力したときのみ、
mgetty が 「Welcome to Linux ...」 
メッセージとログインプロンプトを出力し、
^G が入力されない限りは何も出力しないようにするパッチを作ってみた:
</p>
<pre class="code">
--- mgetty.c.org	2006-01-02 02:13:18.000000000 +0900
+++ mgetty.c	2010-04-17 14:20:54.149714734 +0900
@@ -706,6 +706,11 @@
 	       
 	    if ( c_bool(direct_line) )			/* no RING needed */
 	    {
+		char ch;
+		signal(SIGALRM, sig_goodbye);
+		alarm(ring_chat_timeout);
+		int r = mdm_read_byte(STDIN, &amp;ch);
+		if (r <= 0 || ch != 0x07) break;	/* ^G */
 		mg_get_ctty( STDIN, devname );		/* get controll.tty */
 		mgetty_state = St_get_login;
 		break;
</pre>
<p>
このパッチにより、
^G が入力された時
(ch != 0x07 が不成立) のみ状態 「St_get_login」 へ遷移する。
つまりシリアルコンソールへログインプロンプトを出力する。
^G 以外が入力されると、
上記パッチ中の if 文の条件が成立し、
状態変数 mgetty_state が更新されず、
状態 「St_wait_for_RINGs」 に留まる。
つまり何も出力されない。
</p>
<blockquote>
蛇足だが、
状態 「St_wait_for_RINGs」 というのは、
mgetty がモデムからの 「RING」 送信を待っている状態。
モデムに (公衆回線から) 着呼要求が届いたとき 
(いわゆる電話機が鳴ってる状態)、
モデムは RS-232C 経由で PC に対して 「RING」 を一定間隔で送信する。
mgetty は RING の回数を数えて指定回数を超えれば、 
「ATA」 コマンドをモデムに送信して着呼する (いわゆる受話器を取った状態)。
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/04/579/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>本ブログのサーバを海外レンタルサーバ Linode を使って冗長化・地域分散してみた</title>
		<link>http://www.gcd.org/blog/2010/02/544/</link>
		<comments>http://www.gcd.org/blog/2010/02/544/#comments</comments>
		<pubDate>Wed, 24 Feb 2010 23:58:39 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=544</guid>
		<description><![CDATA[前回、 前々回に書いたように、 このブログ 「仙石浩明の日記」 (および 「仙石浩明CTO の日記」) は、 私の自宅にある PC サーバで動かしている。 2台の PC からなる冗長構成になっていて、 10年以上の安定稼 [...]]]></description>
			<content:encoded><![CDATA[<p>
<a href="http://www.gcd.org/blog/2010/02/540/">前回</a>、
<a href="http://www.gcd.org/blog/2010/01/494/">前々回</a>に書いたように、
このブログ 「仙石浩明の日記」 
(および 
「<a href="http://sengoku.blog.klab.org/">仙石浩明CTO の日記</a>」) は、
私の自宅にある <a href="http://gw.gcd.org/">PC サーバ</a>で動かしている。
2台の PC からなる冗長構成になっていて、
10年以上の安定稼働実績がある 
(両サーバ共に停止したことは皆無)
が、
回線が (家庭用の) フレッツ光ネクストなので、
インターネットとの接続が切れてしまう可能性は皆無ではない。
実際、
<a href="http://www.gcd.org/blog/2009/11/180/">Bフレッツからフレッツ光ネクストへ切り替えた</a>ときは 40分間ほど切れてしまった。
一応<a href="http://www.gcd.org/blog/2006/11/105/">バックアップ回線</a>として 
ADSL 回線も契約しているが、
こちらは固定 IP アドレスを割当てていないので WWW サービス用としては使いにくい。
</p>
<p>
短時間とはいえブログが見えないようなことがあると、
たまたまそのタイミングで (検索エンジン等で見つけて) 訪れた人が、
ブログが既に閉鎖してしまったものと勘違いしてしまうかもしれない。
ここは一つ自宅以外の場所にもサーバを立てて、
回線断の影響を受けないようにしたいところ。
海外のサーバなら地域分散もできてなおよい。
</p>
<p>
道楽で運営している自宅サーバにそこまで可用性を求めるのは、
やりすぎの感もあるが、
近頃流行りの VPS (Virtual Private Server, 仮想専有サーバ) は、
月額 $20 (初期費用無し) 程度で利用できるらしい。
$20 なら試しに使ってみるのも悪くない。
</p>
<table class="ruler">
<tr><th>サービス<br />プラン名</th>
<th>メモリ<br />ディスク</th><th>帯域<br />追加額/単位</th>
<th>月額<br />年額</th><th>データセンタの<br />場所</th></tr>
<tr><th><a href="http://serveraxis.com/vps.php">ServerAxis</a> Xen<br />
VS000.5G-0025.0GP</th>
<td>512MB<br />25GB</td><td>0GB<br />$.05/GB</td><td>$15<br />$180</td>
<td><a href="http://serveraxis.com/inf.php">Chicago</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AA%E3%83%8E%E3%82%A4%E5%B7%9E">IL</a></td></tr>
<tr><th><a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode</a><br />
Xen 360</th>
<td>360MB<br />16GB</td><td>200GB<br />$10/100GB</td><td>$19.95<br />$215.46</td>
<td><a href="http://www.linode.com/wiki/index.php/Network#Fremont.2C_California">Fremont</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a><br />
<a href="http://www.linode.com/wiki/index.php/Network#Dallas.2C_Texas">Dallas</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a> <a href="http://www.linode.com/wiki/index.php/Network">...</a></tr>
<tr><th><a href="http://vpslink.com/xen-vps/">VPSLink</a><br />
Xen VPS Link-3</th>
<td>256MB<br />10GB</td><td>300GB<br />$.50/GB</td><td>$19.95<br />$201.12</td>
<td><a href="http://www.spry.com/company/datacenter/">Seattle</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%AF%E3%82%B7%E3%83%B3%E3%83%88%E3%83%B3%E5%B7%9E">WA</a><br />
New York, <a href="http://ja.wikipedia.org/wiki/%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A8%E3%83%BC%E3%82%AF%E5%B7%9E">NY</a></td></tr>
<tr><th><a href="http://www.slicehost.com/">slicehost</a><br />
Xen VPS 256 slice
</th>
<td>256MB<br />10GB</td><td>150GB<br />$.30/GB</td><td>$20<br />$216</td>
<td><a href="http://www.slicehost.com/questions/#datacenters">Dallas</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a><br />
St. Louis, <a href="http://ja.wikipedia.org/wiki/%E3%83%9F%E3%82%BA%E3%83%BC%E3%83%AA%E5%B7%9E">MO</a></td></tr>
</table>
<p>
VPSLink 以外は最安のプランで比較した。
VPSLink には、
メモリ 64MB で月額 $7.95 の Link-1 プラン、
メモリ 128MB で月額 $13.95 の Link-2 プランもあるが、
256MB 未満のメモリではできることも限られてしまうので比較対象から外した。
</p>
<p>
月額基本料だけを比較すると、
ServerAxis が格安に見えるが、
これは帯域課金が含まれていないためで、
例えば 100GB (送受信の合計バイト数) 使うと $5 加算されて $20 と他社並になる。
</p>
<p>
データセンタの場所 (というかネットワーク上での位置) 
によって遅延時間が変わってくるので、
どこのデータセンタを使っているかも重要。
例えば同じ Linode でもデータセンタによって、
日本からアクセスしたときの
RTT (Round Trip Time, 往復にかかる時間, ミリ秒) が倍以上変わってくる。
それぞれ 10回ずつ ping を打って、
RTT の最小/平均/最大を比較してみる:
</p>
<table class="ruler">
<tr><th>データセンタの場所</th><th>ホスト名</th>
<th>最小</th><th>平均</th><th>最大</th></tr>
<tr><td>Fremont, California</td><td>fremont1.linode.com</td>
<td>121.341</td><td>132.299</td><td>146.939</td></tr>
<tr><td>Dallas, Texas</td><td>dallas1.linode.com</td>
<td>171.049</td><td>173.152</td><td>174.905</td></tr>
<tr><td>Atlanta, Georgia</td><td>atlanta1.linode.com</td>
<td>183.184</td><td>185.938</td><td>190.174</td></tr>
<tr><td>Newark, New Jersey</td><td>newark1.linode.com</td>
<td>200.629</td><td>203.053</td><td>204.755</td></tr>
<tr><td>London, England</td><td>london1.linode.com</td>
<td>269.324</td><td>273.499</td><td>276.822</td></tr>
</table>
<p>
米国西海岸だと RTT は 130msec 程度で済むが、
東海岸は 200msec を超えてしまい、
英国はもっと遠い。
西海岸にデータセンタがあるのは Linode と VPSLink だが、
<a href="http://www.linode.com/wiki/index.php/Network">Linode がデータセンタの情報を詳細に公開している</a>のに対し、
VPSLink は、
<a href="http://www.spry.com/">Spry Hosting</a> のシアトルのデータセンタを使用している、
ということ<a href="http://vpslink.com/about/network-datacenter/pictures/">以上の情報を公開していない</a> 
(しかもこのページは 2006年8月の内容のまま)
のが少し気になる。
</p>
<p>
サポートしている Linux ディストリビューションは、
各社だいたい共通で、
CentOS, Debian, Gentoo, Ubuntu の各バージョンが利用できるが、
<a href="http://vpslink.com/ubuntu-vps/">VPSLink Ubuntu Plan のページ</a>に 
Ubuntu 9.10 Karmic が書かれていない点も、
ちょっと気になった
(ちゃんとポリシーを持って 9.10 をサポートしないのならいいのだが、
どうもそうではなく単に運用の手を抜いているだけのような気配がする)。
Web 界隈での評判も Linode のほうがよさげだったので、
Linode を使ってみることに決めた。
</p>
<p>
サポートしているディストリビューションのバージョンが古くてもアップグレードできるのだろうが、
帯域で課金されるので最初から新しいバージョンをインストールできる方が好ましい。
</p>
<table class="ruler">
<tr><th>サービス</th>
<th>CentOS</th><th>Debian</th><th>Gentoo</th><th>Ubuntu</th>
<th>Fedora</th><th>その他</th></tr>
<tr><th><a href="http://serveraxis.com/vps.php">ServerAxis</a></th>
<td>5.4</td><td>5.0</td><td>2008.0</td><td>9.10</td><td>-</td>
<td>Mandriva SUSE</td>
</tr>
<tr><th><a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode</a></th>
<td>5.3</td><td>5.0</td><td>2008.0</td><td>9.10</td><td>11</td>
<td>Arch SUSE Slack</td>
</tr>
<tr><th><a href="http://vpslink.com/xen-vps/">VPSLink</a></th>
<td>5</td><td>5</td><td>O</td><td>9.04</td><td>11</td>
<td>Arch Slack</td>
</tr>
<tr><th><a href="http://www.slicehost.com/">slicehost</a></th>
<td>5.4</td><td>5.0</td><td>2008.0</td><td>9.10</td><td>12</td>
<td>Arch RedHat</td>
</tr>
</table>
<p>
おそらく各社ともカーネルも入れ替えることができるのだと思うが、
Linode 以外は使っていないので未確認。
<a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode</a> の場合は pv_ops (paravirtualization) 
を有効にしておけば任意のカーネルを利用できる。
したがって (帯域課金さえ気にしなければ ;-) 
任意のディストリビューションの利用 (あるいは完全なカスタマイズ) が可能
(私はまだそこまではやっていない)。
</p>
<p>
なお、VPS サービスは探すと<a href="http://www.webhostingtalk.com/forumdisplay.php?f=104">いろいろある</a>ようだ。
前述した 4社と比べると公開している情報が不十分
(特に Virtuozzo/OpenVZ な VPS は実際のパフォーマンスを予想しにくく、
実地に使ってみないとなんとも...)
なので私は比較検討対象から外したが、
ダメ元で使ってみるのも面白いかもしれない:
</p>
<table class="ruler">
<tr><th>サービス<br />プラン名</th>
<th>メモリ<br />ディスク</th><th>帯域<br />追加額/単位</th>
<th>月額<br />年額</th><th>データセンタの<br />場所</th></tr>

<tr><th><a href="http://www.splitserv.com/portal/xen.htm">SplitServ</a><br />
Xen 1024</th>
<td>1024MB<br />15GB</td><td>200GBs<br />$8/400GB</td><td>$14.95<br />$149.50</td>
<td><a href="http://www.splitserv.com/portal/network.htm">Los Angeles</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a><br />Kansas City, <a href="http://ja.wikipedia.org/wiki/%E3%83%9F%E3%82%BA%E3%83%BC%E3%83%AA%E5%B7%9E">MO</a></td></tr>
<tr><th><a href="http://jp.webk.net/service/vps.html">WebKeepers</a><br />
Virtuozzo ライト</th>
<td>512MB<br />50GB</td><td>?</td><td align="right">1280円<br />11760円</td>
<td>?</td></tr>
<tr><th><a href="http://www.photonvps.com/xen.html">PhotonVPS</a><br />
Xen VPS WARP 1</th>
<td>512MB<br />35GB</td><td>500GB<br />?</td><td>$16.95<br />$203.40</td>
<td><a href="http://www.photonvps.com/networks.html">Los Angeles</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://knownhost.com/vps_packages.html">KnownHost</a><br />
Virtuozzo VS2</th>
<td>512MB<br />30GB</td><td>750GB<br />$40/100GB</td><td>$35<br />$400</td>
<td><a href="http://knownhost.com/network.html">Los Angeles</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a><br />
<a href="http://www.colo4dallas.com/">Dallas</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a></td></tr>
<tr><th><a href="https://www.burst.net/linvps.shtml">BurstNet</a><br />
VPS PACKAGE #1</th>
<td>512MB<br />20GB</td><td>1000GB<br />$25/200GB</td><td>$5.95<br />$71.40</td>
<td><a href="https://www.burst.net/network.shtml">Scranton</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%B3%E3%82%B7%E3%83%AB%E3%83%99%E3%83%8B%E3%82%A2%E5%B7%9E">PA</a></td></tr>
<tr><th><a href="http://vpsnoc.com/Unmanaged_VPS.html">VPS NOC</a><br />
OpenVZ Bronze</th>
<td>512 MB<br />20 GB</td><td>400 GB<br />?</td><td>$12.95<br />$155.40</td>
<td><a href="http://vpsnoc.com/networkfacility.html">Kansas City</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%9F%E3%82%BA%E3%83%BC%E3%83%AA%E5%B7%9E">MO</a></td></tr>

<tr><th><a href="http://corenetworks.net/virtual/">CoreNetworks</a><br />CoreMR</th>
<td>512MB<br />12GB</td><td>500GB<br />?</td><td>$19.95<br />$239.40</td>
<td>?</td></tr>
<tr><th><a href="http://prgmr.com/xen/">prgmr.com</a><br />Xen VPS</th>
<td>512MB<br />12GB</td><td>80GB<br />?</td><td>$12<br />$115.20</td>
<td><a href="http://prgmr.com/san-jose-co-location.html">San Jose</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://hostcadet.com/vps.php">HostCadet</a><br />
VPS Micro</th>
<td>512MB<br />10GB</td><td>400GB<br />?</td><td>$9.99<br />$119.88</td>
<td><a href="http://hostcadet.com/network.php">Colorado Springs</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%AD%E3%83%A9%E3%83%89%E5%B7%9E">CO</a></td></tr>
<tr><th><a href="http://thenynoc.com/vps.html">NY NOC</a> OpenVZ<br />
Super VPS #1</th>
<td>512MB<br />10GB</td><td>1000GB<br />?</td><td>$10.00<br />$120</td>
<td><a href="http://thenynoc.com/network.html">Chicago</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AA%E3%83%8E%E3%82%A4%E5%B7%9E">IL</a><br />North Bergen, <a href="http://ja.wikipedia.org/wiki/%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC%E3%82%B8%E3%83%BC%E5%B7%9E">NJ</a></td></tr>
<tr><th><a href="http://arpnetworks.com/vps">ARP Networks</a><br />
KVM/QEMU</th>
<td>512MB<br />10GB</td><td>100GB<br />?</td><td>$15<br />$180</td>
<td><a href="http://support.arpnetworks.com/faqs/main/ip-address-for-ping-tests">Los Angeles</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>

<tr><th><a href="http://virtuallydedicated.com/xen.html">VirtuallyDedicated</a><br />
Xen VX384</th>
<td>384MB<br />24GB</td><td>300GB<br />?</td><td>$10.99<br />$131.88</td>
<td><a href="http://virtuallydedicated.com/about.html">Chicago</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AA%E3%83%8E%E3%82%A4%E5%B7%9E">IL</a><br />
Scranton, <a href="http://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%B3%E3%82%B7%E3%83%AB%E3%83%99%E3%83%8B%E3%82%A2%E5%B7%9E">PA</a></td></tr>
<tr><th><a href="http://www.grokthis.net/Hosting/vps/">GrokThis.net</a><br />
Xen VPS</th>
<td>320MB<br />16GB</td><td>160GB<br />?</td><td>$20<br />$200</td>
<td>Philadelphia, <a href="http://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%B3%E3%82%B7%E3%83%AB%E3%83%99%E3%83%8B%E3%82%A2%E5%B7%9E">PA</a></td></tr>

<tr><th><a href="http://www.dmehosting.com/budget-linux-vps.php">DMEHosting</a><br />
OpenVZ VPS1</th>
<td>256MB<br />25GB</td><td>1000GB<br />$10/200GB</td><td>$5.95<br />$71.40</td>
<td><a href="http://www.dmehosting.com/clients/announcements.php?id=19">Denver</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%AD%E3%83%A9%E3%83%89%E5%B7%9E">CO</a><br />
Chicago, <a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AA%E3%83%8E%E3%82%A4%E5%B7%9E">IL</a>
</td></tr>
<tr><th><a href="https://www.quantact.com/openvz-plans.shtml">Quantact</a><br />
OpenVZ VS1</th>
<td>256MB<br />15GB</td><td>300GB<br />$1/GB</td><td>$14.99<br />$179.88</td>
<td>Santa Rosa, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://www.rackspacecloud.com/cloud_hosting_products/servers/pricing">RackspaceCloud</a><br />
Xen CloudServers</th>
<td>256MB<br />10GB</td><td>0GB<br />$.08-$.22/GB</td><td>$10.95<br />$131.40</td>
<td><a href="http://www.rackspacecloud.com/blog/2009/07/10/dallas-forth-worth-data-center-update/">Dallas</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a></td></tr>
<tr><th><a href="http://www.xennode.net/pricing.html">XENnode</a><br />
XEN 256</th>
<td>256MB<br />10GB</td><td>200GB<br />?</td><td>$14.99<br />$143.99</td>
<td>Dallas, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a></td></tr>
<tr><th><a href="http://www.virtual-dedicated.net/index.shtml">Datarealm</a> Xen<br />
PowerVPS</th>
<td>256MB<br />10GB</td><td>1Mbps<br />Unmetered</td><td>$19.95<br />$215.52</td>
<td>?</td></tr>
<tr><th><a href="http://www.vr.org/vps-hosting-plans/plans-and-pricing/">HostVirtual</a> Xen<br />
XV0 Server</th>
<td>256MB<br />10GB</td><td>250GB<br />?</td><td>$14.95<br />$179.40</td>
<td><a href="http://status.vr.org/">San Jose</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://rackunlimited.com/vps.html">RackUnlimited</a><br />
Xen RVPS-Starter</th>
<td>256MB<br />10GB</td><td>100GB<br />?</td><td>$8<br />$96</td>
<td><a href="http://rackunlimited.com/about.html">Asheville</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%8E%E3%83%BC%E3%82%B9%E3%82%AB%E3%83%AD%E3%83%A9%E3%82%A4%E3%83%8A%E5%B7%9E">NC</a></td></tr>
<tr><th><a href="http://quickweb.co.nz/xen-vps-linux-plans">QuickWeb</a><br />
Xen Lite</th>
<td>256MB<br />5GB</td><td>240GB<br />?</td><td>$17.95<br />$215.40</td>
<td>San Jose, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://www.quickvps.net/xen/">QuickVPS</a><br />
Xen プラン1</th>
<td>256MB<br />10GB</td><td>50GB<br />?</td><td align="right">2300円<br />21600円</td>
<td>日本国内</td></tr>
<tr><th><a href="http://www.quillhost.com/openvz-vps.php">QuillHost</a><br />
OpenVZ Standard</th>
<td>256MB<br />8GB</td><td>150GB<br />?</td><td>$9.99<br />$119.88</td>
<td>Washington, <a href="http://ja.wikipedia.org/wiki/%E3%83%AF%E3%82%B7%E3%83%B3%E3%83%88%E3%83%B3D.C.">DC</a><br />
Charlotte, <a href="http://ja.wikipedia.org/wiki/%E3%83%8E%E3%83%BC%E3%82%B9%E3%82%AB%E3%83%AD%E3%83%A9%E3%82%A4%E3%83%8A%E5%B7%9E">NC</a></td></tr>
</table>
<p>
と、いうわけで、
Linode 360 を契約してみた。
</p>
<span id="more-544"></span>
<p>
<a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode のページ</a>で、
Linode 360 プランを選んで
「Sign Up Now!」をクリック。
氏名、住所、クレジットカード番号を入力し、
月々払い (Month to month) を選んで申し込むと、
$9.27 の注文書が表示された。
最初の月は月割りで月末までの分を注文するということのようだ
(ということは短期間試してみたいときは月末近くに申し込めばよい?)。
</p>
<blockquote>
米ドル支払いなので、
私が米国に滞在するとき使っている米銀のデビットカード 
(SSN がないとクレジットカードを発行してもらうのは難しい)
を試しに使ってみたら、
何の問題もなく普通に使えた
(もちろんクレジットカードならどこの国のカードでも普通に使えるはず)。
日本のデビットカード (<a href="http://ja.wikipedia.org/wiki/%E3%83%87%E3%83%93%E3%83%83%E3%83%88%E3%82%AB%E3%83%BC%E3%83%89#.E3.82.B8.E3.82.A7.E3.82.A4.E3.83.87.E3.83.93.E3.83.83.E3.83.88">J-Debit</a>) はキャッシュカードなので取扱店でしか使えず、
J-Debit が使える店の大半はクレジットカードも使えるので、
J-Debit を使うのは家電量販店くらいのものだが、
米国のデビットカードは (決裁までの期間が短い点以外は) 
クレジットカードと同等なので便利。
</blockquote>
<p>
2/17 13:18 に「Complete Order」 
ボタンを押して注文を確定すると、
「Payment Receipt」 がメールで届き、
1分後に 「Linode Account Activation」 メールが届いた (13:19)。
</p>
<p>
そのメールに書いてあった
<a href="https://www.linode.com/members/">Member's area</a> にログインすると、
データセンタの選択画面が表示されたので、
迷わず米国西海岸の Fremont, CA を選ぶ。
パーティションの設定をして、
インストールするディストリビューションとして 
「Ubuntu 9.10 64bit」 を選び、
root のパスワードの設定すると、
インストールが始まって 25秒ほどで完了 (13:22)。
</p>
<p>
注文してからここまで約5分。
</p>
<p>
ステータス表示が 「Powered Off」 になっているので、
「Boot」 ボタンを押す (13:29) と
8秒で起動が完了し、
ステータス表示が 「Running」 になった。
試しに手元の PC から ssh でログインしてみる (13:31)。
先ほど設定した root のパスワードを入力すると、
無事ログインできた。
</p>
<p>
注文してからここまで 15分かかっていない。<br />
あっけないほど簡単に VPS が使える状態になった。
</p>
<p>
次に、
ssh 公開鍵を設定して鍵認証で ssh ログインできることを確認した上で、
sshd_config を修正して sshd に HUP シグナルを送って、
パスワード認証ではログイン出来ないように設定。
続いて、
csh, tcsh, emacs, screen, stone, dbndns, daemontools, apache2, php5,
mysql-server, mysql-client, php5-mysql 
などを apt-get で次々インストールしていく。
実は stone を apt-get で入れたのは初めての経験。
ちゃんと最新版 
<a href="http://packages.ubuntu.com/ja/karmic/stone">stone 2.3e</a> 
がインストールされてちょっと感動 (*^_^*)。
ありがとうございます ＞ 北目さま。
</p>
<p>
最後に
chsh コマンドでシェルを /bin/csh (私の愛用シェルは今でも csh ^^;) に変更して、
私のホームディレクトリの環境一式を手元の PC から rsync で送り込んで、
emacs を起動すれば、
私の作業環境一式が VPS 上でも使えるようになる。
</p>
<p>
注文してからここまで 1時間かかっていない。
</p>
<p>
Linode では逆引きを設定させてくれるので、
私の VPS の IP アドレス 74.207.241.21 の逆引きを
<a href="http://fremont.gcd.org/test">fremont.gcd.org</a> に設定した。
あとは、
dnscache を動かして外部のキャッシュサーバに頼らず名前を引けるようにしたり、
私の自宅サーバ <a href="http://gw.gcd.org/test">gw.gcd.org</a> 
との間にトンネルを設置したり、
gw.gcd.org 上の MySQL からローカルの MySQL 
に対してレプリケーションを行なうようにしたり、
等々...
</p>
<blockquote>
もちろん、
Linode でもキャッシュサーバを提供しているので、
自前のキャッシュサーバを VPS 上で動かす必要は必ずしもないが、
名前引きができないことはあらゆるトラブルの元なので、
可能な限り自前で名前を引きに行くようにしたい。
なお、VPS は自身の IP アドレスを DHCP サーバに問合わせて設定しているので、
/etc/resolv.conf を直接書き換えても 
DHCP クライアントによって書き戻されてしまう。
そこで /etc/dhcp3/dhclient.conf に
「prepend domain-name-servers 127.0.0.1;」 を追加する
(元々コメントアウトされているので先頭の # を取り除く)。
</blockquote>
<p>
普通の ubuntu マシンをいじってるのと何ら変わらず、
しかも ssh でログインしたときのレスポンスも、
日本にあるサーバへアクセスしたときと同程度に速いので普通に使える。
apt-get しまくったので WordPress を動かす準備はすぐ整った。
VPS 上の MySQL のアカウントは、
gw.gcd.org とは (当然) 異なるパスワードを設定し、
WordPress からローカルの (つまり VPS 上の) MySQL へつなぐよう設定する。
これで (万一) gw.gcd.org 側が落ちても VPS 側だけでブログを表示できる。
</p>
<p>
一点注意すべきなのは、
レプリケーションは gw.gcd.org から fremont.gcd.org への一方通行なので、
VPS 上の MySQL へ何を書込んでもそれは gw.gcd.org へは反映されないという点。
したがってブログへコメントを書込むときは、
fremont.gcd.org ではなく gw.gcd.org 側へアクセスするように、
ブログの URL http://www.gcd.org/blog/ とは別に、
コメントやトラックバックの URL として 
http://feedback.gcd.org/blog/ を用意した
(feedback.gcd.org は gw.gcd.org のエイリアス)。
</p>
<blockquote>
もちろん双方向レプリケーションを行なうことも可能だが、
Linode の仮想環境に万一脆弱性があった場合に備えて
Linode 側で何が起きても構わないように一方通行にしておく方が無難だろう。
さらに、
Linode 側からレプリケーション用の 
ID 以外で gw.gcd.org の MySQL をアクセスしようとしても、
たとえ<a href="http://www.gcd.org/blog/2006/04/334/">パスワードが一致しても通信を遮断する仕掛け</a>を
gw.gcd.org 側で組み込んである。
</blockquote>
<p>
そして WordPress が設定するクッキーのドメインを
gcd.org に変更して、
feedback.gcd.org なページ (コメント書込み、トラックバック受付) 
で設定したクッキーを
www.gcd.org なページで参照できるようにする。
wordpress/wp-config.php に以下のコードを入れておけばよい:
</p>
<pre class="code">
if ( !defined('COOKIE_DOMAIN') )
	define('COOKIE_DOMAIN', "gcd.org");
</pre>
<p>
最後に、
www.gcd.org の IP アドレスとして VPS の IP アドレス 74.207.241.21 を追加した。
</p>
<pre class="terminal">
% dnsqr a www.gcd.org
1 www.gcd.org:
61 bytes, 1+2+0+0 records, response, noerror
query: 1 www.gcd.org
answer: www.gcd.org 12657 A 74.207.241.21
answer: www.gcd.org 12657 A 60.32.85.216
</pre>
<p>
すなわち、
いま読者の皆さんが読んでるこのページは、
1/2 の確率で Linode の VPS が送信したページということになる。
すでに一週間近くこの状態を続けていて、
今のところ 360MB のメモリで一応足りているが、
アクセスが増えてくるとどうなるか少し心許無い。
</p>
<pre class="terminal">
fremont:/home/sengoku % free
             total       used       free     shared    buffers     cached
Mem:        351508     264236      87272          0       4444      35316
-/+ buffers/cache:     224476     127032
Swap:       262136      39656     222480
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/02/544/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CTO日記も livedoorブログから WordPress へ引越しました  (URL は変更なし)</title>
		<link>http://www.gcd.org/blog/2010/02/540/</link>
		<comments>http://www.gcd.org/blog/2010/02/540/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 22:46:28 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>
		<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=540</guid>
		<description><![CDATA[「仙石浩明の日記」 に続いて、 「仙石浩明CTO の日記」 も先週末に livedoorブログから自宅サーバへ引っ越した (つまりネームサーバの設定を変更して切替。有料プランの解約はこれから)。 もともと両ブログは相互に [...]]]></description>
			<content:encoded><![CDATA[<p>
「仙石浩明の日記」 に続いて、
「仙石浩明CTO の日記」 も先週末に 
livedoorブログから<a href="http://www.gcd.org/">自宅サーバ</a>へ引っ越した 
(つまりネームサーバの設定を変更して切替。有料プランの解約はこれから)。
もともと両ブログは相互にリンクを張って密接に連係していたので、
引越を機会に両者を統合した。
</p>
<p>
統合といっても両ブログは微妙(?)に読者層が異なると思われるし、
何よりページの体裁が大きく変わってしまっては読者の方々を戸惑わせてしまうので、
CTO日記を 
「<a href="http://www.gcd.org/blog/">仙石浩明の日記</a>」 
の<a href="http://www.gcd.org/blog/category/cto/">一カテゴリ</a>という位置付けにして、
かつページの体裁は 
<a href="http://ja.wordpress.org/">WordPress</a> 
のテーマを切り替えることによって、
どちらのブログもあまり大きな変化がないようにしている。
</p>
<p>
「仙石浩明CTO の日記」 
<a href="http://sengoku.blog.klab.org/">http://sengoku.blog.klab.org/</a>
をアクセスすると、
次のような PHP スクリプトを走らせた上で、
WordPress を呼び出す (末尾の require 文):
</p>
<pre class="code">
&lt;?php
$new = NULL;
if ($_SERVER['REQUEST_URI'] == "/") {
    $new = "/blog/category/cto/";
} elseif ($_SERVER['REQUEST_URI'] == "/feed/") {
    $new = "/blog/category/cto/feed/";
} elseif (preg_match('@^/\d+/\d+/\d+/@',
		     $_SERVER['REQUEST_URI'], $matches)) {
    $new = $_SERVER['REQUEST_URI'];
...(中略)...
}
if ($new) {
    ...(中略)...
    $ORIG_SERVER_NAME = $_SERVER['SERVER_NAME'];
    $host = "www.gcd.org";
    $_SERVER['SERVER_NAME'] = $host;
    $_SERVER['REQUEST_URI'] = $new;
    $_SERVER['SCRIPT_NAME'] = $new;
    $_SERVER['PHP_SELF'] = $new;
    $abspath = "/usr/local/www/wordpress/";
    $themepath = "${abspath}wp-content/themes/sengoku_cto/";
    define('WP_USE_THEMES', true);
    define('TEMPLATEPATH', $themepath);
    define('STYLESHEETPATH', $themepath);
    require("${abspath}wp-blog-header.php");
...(中略)...
}
?&gt;
</pre>
<p>
つまり
http://sengoku.blog.klab.org/ へのアクセスは、
パス名に 「/category/cto/」 を追加することによって、
CTO日記カテゴリへのアクセスに変換する。
</p>
<p>
ページの体裁については、
「wp-content/themes/sengoku_cto/」 ディレクトリが、
CTO日記のテーマフォルダで、
二つの PHP 定数 TEMPLATEPATH と STYLESHEETPATH 
をこのディレクトリへ設定することによって、
テーマの切り替えを行なっている。
</p>
<p>
テーマフォルダの中にあるテーマ関数ファイル 「functions.php」 は、
WordPress の初期化中に読み込まれるので、
ここに PHP スクリプトを書いておくことによって
WordPress の挙動を変更することができる。
例えばブログのタイトルを 「仙石浩明CTO の日記」 に変更するには、
以下のスクリプトを functions.php に追加しておけばよい:
</p>
<pre class="code">
function option_blogname_cto() {
    return '仙石浩明CTO の日記';
}
add_filter('pre_option_blogname', 'option_blogname_cto');
</pre>
<p>
つまり、
pre_option_blogname フックに、
option_blogname_cto フィルタを登録する。
</p>
<p>
WordPress では、
ブログのタイトルなど各種オプションの設定値 (DB に格納している) を、
get_option($setting) 関数を呼び出すことで参照している。
例えばタイトルは get_option('blogname') を呼び出すことで得られ、
URL は get_option('home') で得られる。
</p>
<p>
get_option($setting) 関数は wp-includes/functions.php で定義されていて、
以下のようにフィルタフック pre_option_* が定義されている:
</p>
<pre class="code">
function get_option( $setting, $default = false ) {
    global $wpdb;

    // Allow plugins to short-circuit options.
    $pre = apply_filters('pre_option_' . $setting, false);
    if ( false !== $pre )
	return $pre;
    ...(中略)...
}
</pre>
<p>
つまり、
「pre_option_設定名」 というフックに登録されたフィルタが値を返すなら、
get_option はオプションの設定値ではなくフィルタが返した値を返すようになる。
前述の例なら、
「pre_option_blogname」 フックに登録された
「option_blogname_cto」 フィルタが 「仙石浩明CTO の日記」 という値を返すので、
get_option('blogname') も 「仙石浩明CTO の日記」 という値を返すようになり、
結果としてブログのタイトルを変更できる、というわけ。
</p>
<p>
ただし、
前述したように CTO日記は 「仙石浩明の日記」 の一カテゴリという位置付けなので、
ブログのタイトルを変更しただけだと、
ブログ 「仙石浩明CTO の日記」 の 「仙石浩明CTO の日記」 カテゴリということで、
ページのタイトル等が
「仙石浩明CTO の日記 &raquo; 仙石浩明CTO の日記」
という冗長なものになってしまう。
そこで、
以下のようなスクリプトを 「functions.php」 に追加して、
タイトルとカテゴリ名が同じときはカテゴリ名が表示されないようにする:
</p>
<pre class="code">
function single_cat_title_cto($category_name) {
    $name = get_option('blogname');
    if ($category_name == $name) return "";
    return $category_name;
}
add_filter('single_cat_title', 'single_cat_title_cto');
</pre>
<p>
single_cat_title フックは、
wp-includes/general-template.php で定義されていて、
ページのタイトルなどに表示されるカテゴリ名を変更することができる。
</p>
<p>
以上で、
「仙石浩明の日記」 の一カテゴリを 
「仙石浩明CTO の日記」 の体裁で見せることができるようになる。
しかし、
元が 「仙石浩明の日記」 であるだけに、
リンク先が全て 「仙石浩明の日記」 のページになってしまう。
例えば、
<a href="http://sengoku.blog.klab.org/">「仙石浩明CTO の日記」 のトップページ</a>の一番下に、
「<a href="http://sengoku.blog.klab.org/page/2/">古い投稿 &raquo;</a>」 
というリンクがあるが、
このリンク先が http://www.gcd.org/blog/page/2/ になってしまい、
たどると <a href="http://www.gcd.org/blog/page/2/">「仙石浩明の日記」 のトップページの 2ページ目</a>へ遷移してしまう。
</p>
<p>
また、
本文中 (あるいはサイドバー) に現れるリンクも、
DB のデータは
「仙石浩明の日記」 のパーマリンクを用いているので、
たとえそれが 「仙石浩明CTO の日記」 カテゴリに含まれていても、
そのリンクをたどると
「仙石浩明の日記」 の記事として表示されてしまう。
</p>
<p>
そこで、
遷移先も 「仙石浩明CTO の日記」 として表示したいリンクを、
フィルタで書き換えることにした。
つまり DB のデータは 「仙石浩明の日記」 へのリンクのままで、
ブラウザに送信する前に都度書き換える。
</p>
<p>
対象となるリンクは、
記事本文中だけでなく、
前述したページナビ 「古い投稿」 「新しい投稿」 や、
サイドバー (「人気記事」 や 「最近の投稿」) にも現れる。
ページ丸ごと (つまり HTTP レスポンス丸ごと) 
HTML を書き換えられるフックがあるとよかったのだが、
残念ながらそういうフックは定義されていないようだ。
以下のフックそれぞれについてリンクを書き換えればよさげ:
</p>
<table class="ruler">
<tr><th>フィルタフック</th>
<th>フィルタが変更できる対象, 第2引数, ...</th></tr>
<tr><td>the_content</td>
<td>記事本文 HTML</td></tr>
<tr><td>the_category</td>
<td>記事の末尾に表示されるカテゴリーリストの HTML,<br />
$separator, $parents</td></tr>
<tr><td>get_pagenum_link</td>
<td>ページ末尾に表示されるページナビ 「古い投稿」 「新しい投稿」 の URL</td></tr>
<tr><td>post_link</td>
<td>記事の URL (パーマリンク), $post, $leavename</td></tr>
<tr><td>widget_text</td>
<td>サイドバーに表示されるテキストウィジェットの HTML, $instance</td></tr>
<tr><td>wp_list_categories</td>
<td>サイドバーに表示されるカテゴリーのリストの HTML</td></tr>
<tr><td>category_feed_link</td>
<td>カテゴリーの RSSフィードの URL, $feed</td></tr>
</table>
<p>
書き換え対象のリンクを決めるために、
まず CTO日記カテゴリに属す記事の ID を取得する:
</p>
<pre class="code">
function setup_cto_id() {
    global $wpdb;
    global $is_cto_id;
    $result = $wpdb-&gt;get_results("SELECT object_id FROM wp_term_relationships WHERE term_taxonomy_id=17", ARRAY_N);
    foreach ($result as $row) {
	$is_cto_id[$row[0]] = 1;
    }
}
</pre>
<p>
あるカテゴリに属す記事ID のデータを取得する関数など、
WordPress に含まれているんじゃないかと探してみたのだが、
見つからなかったので DB に問合わせて取得するようにしてみた。
毎回 DB アクセスが発生してしまうが、
キャッシュとかはアクセス数が増えてから考える (^^;)。
「term_taxonomy_id=17」 が CTO日記のカテゴリ 
(決め打ち ^^;)。
CTO日記カテゴリに属す記事は、
配列 $is_cto_id[記事ID] に 1 を代入しておく。
</p>
<p>
次に URL を書き換えるスクリプト:
</p>
<pre class="code">
function replace_URL_cto($matches) {
    global $is_cto_id;
    global $ORIG_SERVER_NAME;
    if (!is_array($is_cto_id)) {
	setup_cto_id();
    }
    if (is_null($matches[2])) {
	if ($matches[1] == "category/cto/") return "http://$ORIG_SERVER_NAME/";
	return "http://$ORIG_SERVER_NAME/$matches[1]";
    } elseif ($is_cto_id[$matches[2]]) {
	return "http://$ORIG_SERVER_NAME/$matches[1]";
    }
    return $matches[0];
}

function convert_URLs_cto($text) {
    $textarr = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE);
    $stop = count($textarr);
    for ($i = 0; $i < $stop; $i++) {
	$content = $textarr[$i];
	if (strlen($content) > 0) {
	    $content = preg_replace_callback(
		    '@http://www.gcd.org/blog/(\d+/\d+/(\d+)|category/cto/)@',
		    'replace_URL_cto', $content);
	}
	$output .= $content;
    }
    return $output;
}
</pre>
<p>
顔文字を画像に変換して表示するフィルタ wptexturize 
(wp-includes/formatting.php で定義されている) を参考にさせてもらった。
preg_replace_callback() を使って書き換え対象リンクを探し、
replace_URL_cto で記事ID がCTO日記カテゴリに属す
($is_cto_id[$matches[2]] が TRUE)
場合のみ書き換える。
</p>
<p>
最後に、
この書き換えフィルタ replace_URL_cto を前述したフィルタフックに追加:
</p>
<pre class="code">
add_filter('the_content', 'convert_URLs_cto');
add_filter('the_category', 'convert_URLs_cto');
add_filter('get_pagenum_link', 'convert_URLs_cto');
add_filter('post_link', 'convert_URLs_cto');
add_filter('widget_text', 'convert_URLs_cto');
add_filter('wp_list_categories', 'convert_URLs_cto', 12000);
add_filter('category_feed_link', 'convert_URLs_cto');
</pre>
<p>
wp_list_categories にフィルタを追加すると
<a href="http://www.coppit.org/code/">Category Order プラグイン</a>と衝突するので、
優先順位を下げて Category Order プラグインの後で実行されるようにしている。
</p>
<p>
また、
the_category, post_link, widget_text, category_feed_link 各フックは、
2つ以上の引数を持つが、
第1引数 (書き換え対象の HTML あるいは URL) 
のみ使用するので引数の数 (add_filter の第3引数) を省略している。
</p>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/02/540/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>本ブログを livedoorブログから WordPress へ引越しました (URL は変更なし)</title>
		<link>http://www.gcd.org/blog/2010/01/494/</link>
		<comments>http://www.gcd.org/blog/2010/01/494/#comments</comments>
		<pubDate>Wed, 27 Jan 2010 23:26:08 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=494</guid>
		<description><![CDATA[このブログ 「仙石浩明の日記」 は、 今まで livedoorブログの有料プラン (月額 315円) を利用していた。 比較的自由にデザインをカスタマイズできること、 URL のドメインを自由に設定できることから、 20 [...]]]></description>
			<content:encoded><![CDATA[<p>
このブログ 「仙石浩明の日記」 は、
今まで livedoorブログの有料プラン (月額 315円) を利用していた。
比較的自由にデザインをカスタマイズできること、
URL のドメインを自由に設定できることから、
2006年3月9日にこのブログを開設して以来、
今まで 4年近く使い続けてきた。
しかし自前サーバでない不便さ、隔靴掻痒感はいかんともしがたく、
乗り換えを検討したことは何度もあって、
そのたびに不便さと乗り換えの面倒くささを天秤にかけて踏み切れずにいた。
</p>
<p>
例えば、
<a href="http://www.gcd.org/blog/2006/06/75/">HTML文法エラーを修正できない</a>問題点を見つけたときや、
ブログのサイドバーにある 
「livedoor Reader」
「RSS」
「livedoor Blog」などのバナーを非表示にしていたら、
ライブドアから警告メールが来て、
やむなくバナー表示を復活させたときなど。
なかでも、
昨年ブログ管理画面 (有料プラン) がリニューアルされたときは、
とても大きなフラストレーションを感じた。
ライブドアはよかれと思ってリニューアルしているのだろうが、
JavaScript を多用した新管理画面は、
私にとっては使いにくいことこのうえないし、
<a href="http://www.gcd.org/blog/2006/05/354/">生ログ取得スクリプト</a>も使えなくなった。
</p>
<p>
そして、
今年に入って
「ブログ共通ヘッダー」 
(最上端に表示される livedoor Blogのロゴ, 所属カテゴリ, テキスト広告) 
が勝手に表示されるようになったのが最後の背中のひと押しとなった。
これを非表示にしたければ
PRO プラン (月額 315円) ではなく
ADVANCE プラン (月額 840円) に変更しろという趣旨 (要は値上げ) 
なのだと思うが、
何の連絡もなく画面の一番目立つ最上端に、
醜悪なヘッダー
(ADVANCE プランへの移行を促すためにわざと醜悪にしている? *_*)
を勝手に挿入するデリカシーの無さに驚き呆れ、
<a href="http://www.gcd.org">自前サーバ</a>への引越を決めた。
</p>
<img src="http://www.gcd.org/sengoku/picts/livedoor-header.jpg" width="400" height="190" alt="common header" />
<p>
ブラウザ画面最上端 ↑ の 「ブログ共通ヘッダー」 
(所属カテゴリ 「IT > プログラミング 」 と 
「受験にまつわるエピソードを教えてください」 という広告? 表示) は、
ライブドアのサーバが HTML 中に自動挿入する以下の 
JavaScript (settings/header.js) 
によって body 要素へ追加 (appendChild) されている。
</p>
<pre class="code">
(function () {
    var hd = document.createElement('div');
    var str = '';
    str += '&lt;div id="header2" style="z-index:10001"&gt;&lt;table cellspacing="0" class="blog-common-header" id="header"&gt;';
	...(中略)...
    hd.innerHTML = str;
    document.body.appendChild(hd);
	...(後略)...
</pre>
<p>
ユーザがカスタマイズ可能な範囲の外であるため、
この JavaScript の実行そのものを止めることは無理っぽいが、
追加された子要素を削除する JavaScript を実行して消すことは可能。
例えば以下のようなコードを HTML ファイルのどこかに入れておけばよい。
</p>
<pre class="code">
&lt;script type="text/javascript" src="http://www.gcd.org/sengoku/docs/livedoor.js"&gt;&lt;/script&gt;
</pre>
<p>
livedoor.js は、
body 要素の子要素 「header2」 を取り除く (removeChild) だけの JavaScript:
</p>
<pre class="code">
(function () {
    var element = document.getElementById("header2");
    element.removeChild(element.childNodes.item(0));
})();
</pre>
<p>
正月早々 「ブログ共通ヘッダー」 
が勝手に表示されるようになったことに気付き、
直ちにこの対策を行なって非表示にしたのだが、
以前バナーを非表示にしたときライブドアから警告メールが来たことを考えれば、
今回も警告メールが来るのは時間の問題だろうと、
引越を急いだ次第。
</p>
<p>
私はブログを書くときも HTML で書いていて、
あまり CMS 的な機能は必要としないので、
トラックバックとコメントを受付けるだけの簡単な 
Web アプリを書こうと思っていたのだが、
試しにインストールしてみた 
<a href="http://ja.wordpress.org/">WordPress</a> 
が思いの外シンプルな作りで、
ちょっといじっているうちに簡単にカスタマイズできてしまったので、
これを使うことにした。
</p>
<blockquote>
livedoorブログでエクスポートしたデータを 
(Movable Type / TypePad のデータとして) WordPress でインポートしたら、
(pre 要素なのに) 行頭の空白文字が削除されてしまって往生したが、
大した量でもなかったので手作業で修正してしまった。
もう少し量が多かったら真面目に PHP コードを追ったのだが...
</blockquote>
<p>
こんなに簡単にできるなら、
もっと早く WordPress へ移行すればよかった。
既存のテーマを見よう見まねでいじっただけなのだが、
以前のデザインをほとんどそのまま踏襲することができた
(むしろ余計な div 要素を排除できたので、よりシンプルになった)。
</p>
<p>
livedoorブログと WordPress では URL (パーマリンク, permalink) 
の形式が異なる。
例えば、
<a href="http://www.gcd.org/blog/2008/04/154/">あるエントリ</a>の 
URL は、
livedoorブログ (旧URL) と
WordPress (新URL) で次のようになる:
</p>
<table class="ruler">
<tr><th width="70"></th>
<th>旧: livedoorブログ</th><th>新: WordPress</th></tr>
<tr><th>ベースURL</th>
<td>http://blog.gcd.org</td>
<td>http://www.gcd.org/blog</td></tr>
<tr><th>エントリ</th>
<td>/archives/51168556.html</td>
<td>/2008/04/154/</td></tr>
<tr><th>月別表示</th>
<td>/archives/2008-04.html</td>
<td>/2008/04/</td></tr>
<tr><th>カテゴリ</th>
<td>/archives/cat_50026701.html</td>
<td>/category/enlighten/</td></tr>
<tr><th>RSS</th>
<td>/index.rdf</td>
<td>/feed/rdf/</td></tr>
</table>
<p>
一見して、
WordPress のパーマリンクの方が分かりやすい。
正確に言えば、
<a href="http://wpdocs.sourceforge.jp/Using_Permalinks">WordPress のパーマリンクの形式</a>は任意に設定できて、
livedoorブログ (Movable Type ベース) の形式に合わせることも可能だが、
エントリID (上記の例だと 51168556 と 154) 
が異なるので形式を合わせること自体にはあまり意味はない。
</p>
<p>
livedoorブログを利用していた時の URL がそのまま使えるように、
旧URL を新URL へ変換する以下の PHP スクリプトを書いた
(switch 文で URL ごとにずらずら case を並べただけ)。
</p>
<pre class="code">
&lt;?php
$new = NULL;
if ($_SERVER['REQUEST_URI'] == "/") {
    $new = "/blog/";
} elseif ($_SERVER['REQUEST_URI'] == "/atom.xml") {
    $new = "/blog/feed/atom/";
} elseif ($_SERVER['REQUEST_URI'] == "/index.rdf") {
    $new = "/blog/feed/rdf/";
} elseif (preg_match('/^\/archives\/(20\d\d)-(\d\d)\.html$/',
		     $_SERVER['REQUEST_URI'], $matches)) {
    $year = $matches[1];
    $mon = $matches[2];
    $new = "/blog/$year/$mon/";
} elseif (preg_match('/^\/archives\/cat_(\d+)\.html$/',
		     $_SERVER['REQUEST_URI'], $matches)) {
    $id = $matches[1];
    $new = "/blog/category/";
    switch ($id) {
    case 50026701: $new .= "enlighten/";   break;
    case 50026704: $new .= "business/";    break;
    case 50026703: $new .= "engineer/";    break;
    case 50026699: $new .= "system/";      break;
    case 50035671: $new .= "hardware/";    break;
    case 50026700: $new .= "programming/"; break;
    case 50021362: $new .= "stone/";       break;
    case 50035209: $new .= "la-fonera/";   break;
    case 50041534: $new .= "hawaii/";      break;
    case 50045637: $new .= "hongkong/";    break;
    case 50026702: $new .= "others/";      break;
    default: $new = NULL;
    }
} elseif (preg_match('/^\/archives\/(\d+)\.html$/',
		     $_SERVER['REQUEST_URI'], $matches)) {
    $id = $matches[1];
    $new = "/blog/";
    switch ($id) {
    case 50071073: $new .= "2006/03/8/"; break;
	...(中略)...
    case 51168556: $new .= "2008/04/154/"; break;
	...(中略)...
    case 51552583: $new .= "2009/12/184/"; break;
    case 51555097: $new .= "2010/01/185/"; break;
    default: $new = NULL;
    }
}
if ($new) {
    $host = "www.gcd.org";
    $_SERVER['SERVER_NAME'] = $host;
    $_SERVER['REQUEST_URI'] = $new;
    $_SERVER['SCRIPT_NAME'] = $new;
    $_SERVER['PHP_SELF'] = $new;
    define('WP_USE_THEMES', true);
    require('/usr/local/www/wordpress/wp-blog-header.php');
} else {
    header("HTTP/1.1 301 Moved Permanently");
    header("Location: http://www.gcd.org/blog/");
    exit();
}
?&gt;
</pre>
<p>
http://blog.gcd.org/* にアクセスすると、
↑ この PHP スクリプトが実行される。
</p>
<p>
これに加え、
逆方向の変換、
つまり新URL から旧URL への変換も必要。
例えば 「はてなブックマーク」 
へ頂いたコメント
(例えば前述したエントリに対する<a href="http://b.hatena.ne.jp/entry/blog.gcd.org/archives/51168556.html">コメント一覧</a>) 
は旧URL に紐づいているので、
各エントリに旧URL を登録しておく必要がある。
WordPress には
<a href="http://wpdocs.sourceforge.jp/Using_Custom_Fields">カスタムフィールド</a>
という機能があって、ひとつのエントリに複数の 「キー」 と、
各キーにその値を登録することができる。
そこで沢山の 「ブックマーク」 を頂いたエントリには、
「hatena_b」 というキーで旧URL を登録しておくことにした。
すると、以下のような PHP スクリプトでブックマーク数
<a href="http://b.hatena.ne.jp/entry/http://blog.gcd.org/archives/51168556.html"><img style="border:0;" src="http://b.hatena.ne.jp/entry/image/http://blog.gcd.org/archives/51168556.html" alt="" /></a>
を表示できる。
</p>
<pre class="code">
&lt;?php
  $hatena_b = get_post_meta($post-&gt;ID, "hatena_b", true);
  if ($hatena_b)
    echo ' &lt;a href="http://b.hatena.ne.jp/entry/' . $hatena_b
       . '"&gt;&lt;img style="border:0;" src="http://b.hatena.ne.jp/entry/image/'
       . $hatena_b . '" alt="" /&gt;&lt;/a&gt;';
?&gt;
</pre>
<p>
最後に、
ネームサーバの設定変更を行なって、
blog.gcd.org レコード (TTL = 3時間) の値を 
「CNAME blog-01.livedoor.jp」 から 「CNAME www.gcd.org」 へ変更した。
1/26 19:35 に変更を行なったところ、
19:46 には最初のアクセスが www.gcd.org へ届き、
20時台には大半のアクセスが www.gcd.org へ届くようになった。
20時台にライブドア側へ届いた blog.gcd.org へのアクセスはわずかに 7件、
その後は 1 時間〜数時間ごとに、ぱらぱらアクセスがあるという状況が続いている。
旧レコードを保持しているキャッシュネームサーバが残っていて、
そのネームサーバを使ってるブラウザからのアクセスのみが、
ライブドア側へ届いているといった感じ。
</p>

<span id="more-494"></span>
<p>
同日 20:20 追記:
</p>
<p>
そろそろ帰ろうかと思っていたら、
<a href="http://d.hatena.ne.jp/hirose31/">ひろせ31さん</a>からチャットを頂いた:
</p>
<pre class="terminal">
(19:05:46) ひろせ: curl -I http://www.gcd.org/blog/ | grep X-Pingback
到達不可なヘッダついてますよー
(19:06:46) 仙石: ども、ご無沙汰してます。
(19:06:53) ひろせ: どもどもー
(19:08:21) 仙石: そっか、ヘッダに残ってましたか～
(19:08:34) 仙石: チェックするのを忘れていました。ありがとうございます
(19:08:40) ひろせ: いえいえー
</pre>
<p>
確かにヘッダに 「X-Pingback:」 フィールドがついている。↓
</p>
<pre class="terminal">
% curl -I http://www.gcd.org/blog/
HTTP/1.1 200 OK
Date: Thu, 28 Jan 2010 10:08:54 GMT
Server: Apache
X-Pingback: http://gt.gcd.org/wordpress/xmlrpc.php
Content-Type: text/html; charset=UTF-8

</pre>
<p>
<a href="http://wpdocs.sourceforge.jp/%E7%94%A8%E8%AA%9E%E9%9B%86#.E3.83.94.E3.83.B3.E3.83.90.E3.83.83.E3.82.AF">Pingback</a> 
って何だろう (^^;) と思いつつ、
「gt.gcd.org」 は <a href="http://www.gcd.org">GCD</a> (私の個人サイト) 
内部でのみ有効なホスト名 (IPアドレスは 192.168.1.1) なので、
このフィールドの目的が何であれ取り除いた方がいい。
実は、
HTTP レスポンス中にも
「gt.gcd.org」 
は出力されていて、
 ↓
</p>
<pre class="terminal">
&lt;link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://gt.gcd.org/wordpress/xmlrpc.php?rsd" /&gt;
&lt;link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://gt.gcd.org/wordpress/wp-includes/wlwmanifest.xml" /&gt; 
</pre>
<p>
これは公開前に気付いて取り除いたのだが、
HTTP ヘッダの方はチェックするのを忘れていた。orz
</p>
<p>
上記二つの link 要素は、
それぞれ rsd_link と wlwmanifest_link というフィルタが出力しているので、
このフィルタを wp_head フックから取り除いてやればよい。
具体的には、
現在のテーマの functions.php ファイルに、
以下のようなコードを追加する:
</p>
<pre class="code">
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
</pre>
<p>
ひろせ31さんから指摘を受けて、
条件反射的に WordPress の PHP コードを検索してみた:
</p>
<pre class="terminal">
% find /usr/local/www/wordpress -type f | xargs grep X-Pingback
/usr/local/www/wordpress/wp-includes/classes.php:	 * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing
/usr/local/www/wordpress/wp-includes/classes.php:		$headers = array('X-Pingback' => get_bloginfo('pingback_url'));
</pre>
<p>
classes.php の該当箇所を見てみると、
</p>
<pre class="code">

	function send_headers() {
		$headers = array('X-Pingback' => get_bloginfo('pingback_url'));
		...
</pre>
<p>
ありゃ、ハードコードされてる...
</p>
<p>
前述した link 要素のように、
フィルタで出力するようになっていれば、
そのフィルタを無効化するだけで済むのに...
(*_*)
</p>
<p>
さて、どうしたものか。
この send_headers() 関数は、
$headers 配列にフィールドを加えていって、
最後に $headers を HTTP ヘッダとして送信する、
という処理の流れのようだ。
</p>
<p>
さらに読み進めていくと、
</p>
<pre class="code">
		...
		$headers = apply_filters('wp_headers', $headers, $this);
		...
</pre>
<p>
ありがたいことにフィルタ・フックが定義されていた。
この wp_headers フックに、
X-Pingback: フィールドを取り除くフィルタを登録すれば、
うまくいきそうだ。
</p>
<pre class="terminal">
(19:12:57) 仙石: X-Pingback はハードコードされてますねぇ... > WordPress
(19:13:12) ひろせ: うひー
(19:13:22) 仙石: フィルタで削除するしかないのかな... あるいは WordPress のコードを直接書き換えるか...
(19:13:22) ひろせ: gt.gcd.orgって、内部ホストなんですかね？
(19:13:26) 仙石: です
(19:13:43) 仙石: 管理用なので、外部からはアクセスできないようにしたいです
(19:16:46) ひろせ: Apacheで、 Header unset X-Pingback で消えるかもすね。
(19:17:58) 仙石: なるほど
</pre>
<p>
現在のテーマの functions.php ファイルに、
以下の PHP コードを追加してみた:
</p>
<pre class="code">
function remove_X_Pingback($headers) {
	unset($headers['X-Pingback']);
	return $headers;
}
add_filter('wp_headers', 'remove_X_Pingback');
</pre>
<p>
初めて書く WordPress のフィルタだったが
(実をいうと PHP も書き慣れているわけではないので、
配列の要素を削除する方法もマニュアルを調べながら書いた ^^;)、
うまく動いているようだ:
</p>
<pre class="terminal">
% curl -I http://www.gcd.org/blog/
HTTP/1.1 200 OK
Date: Thu, 28 Jan 2010 10:29:33 GMT
Server: Apache
Content-Type: text/html; charset=UTF-8

</pre>
<p>
めでたく、X-Pingback: フィールドが出力されなくなった。
</p>
<pre class="terminal">
(19:30:23) 仙石: 取り除きました。＞ X-Pingback:
(19:30:33) ひろせ: oo
(19:30:38) 仙石: functions.php に、
function remove_X_Pingback($headers) {
        unset($headers['X-Pingback']);
        return $headers;
}
add_filter('wp_headers', 'remove_X_Pingback');
(19:30:51) 仙石: を加えました。
</pre>
<p>
ありがとうございました。＞ ひろせ31さん
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/01/494/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>自宅回線を 100Mbps から 200Mbps へ、帯域を倍にしてみた ～ NGN が自宅にやってきた</title>
		<link>http://www.gcd.org/blog/2009/11/180/</link>
		<comments>http://www.gcd.org/blog/2009/11/180/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 01:00:42 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[IPv6]]></category>
		<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/2009/11/180/</guid>
		<description><![CDATA[私の自宅がある神奈川でも、 先月 13日から 「フレッツ 光ネクスト ファミリー・ハイスピードタイプ」 の申込受付が始まったので、 早速申し込んでみた。 今まで使っていたのは 「Bフレッツ ニューファミリータイプ」 だか [...]]]></description>
			<content:encoded><![CDATA[<p>
私の自宅がある神奈川でも、
先月 13日から
「<a href="http://flets.com/next/">フレッツ 光ネクスト</a>
ファミリー・ハイスピードタイプ」
の<a href="http://www.ntt-east.co.jp/release/0909/090930a.html">申込受付が始まった</a>ので、
早速申し込んでみた。
今まで使っていたのは
「<a href="http://flets.com/opt/s_outline_type.html">Bフレッツ</a>
ニューファミリータイプ」
だから、
下りの帯域が 100Mbps から倍の 200Mbps に増える。
月額利用料は変わらない。
工事費が 5827円かかるが、
キャンペーン期間中につき月額料金が 3ヶ月間無料になるので悪くない。
</p>
<p>
10/16日に NTT東日本 0120-116116 に電話して申し込んだところ、
回線切替工事まで最短で 10営業日かかり、
工事の予約状況によると最速で 11/1(日) の工事になるらしい。
続いて NTT コミュニケーションズ 0120-506-506 に電話して、
200Mbps に変更したときプロバイダ (OCN) 側の変更が必要か確認。
個人向けサービス
(<a href="http://www.ocn.ne.jp/hikari/wflets/">OCN 光 with フレッツ</a>)
は元々 200Mbps に対応しているから契約変更は必要無しとのことだったが、
あいにく私が契約しているのは法人向けサービスであるところの
<a href="http://www.ocn.ne.jp/business/ftth/ip8-16/index.html">OCN 光アクセス IP8/IP16「Bフレッツ」プラン</a>
である。
この場合、
<a href="http://www.ocn.ne.jp/business/ftth/next_ip8-16/index.html">OCN 光アクセス IP8/IP16「フレッツ 光ネクスト」プラン</a>
への契約変更が必要となる。
</p>
<p>
NTT コミュニケーションズの担当者の話によると、
認証サーバの設定を変更するらしい。
PPPoE のユーザ名とパスワードはそのまま同じものを使っていい
(新しいユーザ名も付与されるので新旧どちらのユーザ名も使える) が、
認証サーバ側の設定を変更すると旧回線 (つまり Bフレッツ)
からの認証は拒否されるらしい。
すなわち認証サーバの設定変更後は、
新回線 (つまり フレッツ 光ネクスト) からでないと認証が通らない。
したがって、
回線が切り替わる前に PPPoE セッションが切れると、
旧回線のままでは再接続できなくなってしまう。
さらに、
設定変更は平日の未明 0:00 ～ 5:00 の間にしか実施しないらしい。
</p>
<p>
ということはつまり、
11/1(日) に回線工事を行なうには、
10/30(金) の未明に認証サーバの設定変更を行なうことになる。
10/30 未明から 11/1 の回線工事まで丸二日間、
もし PPPoE セッションが何らかの原因によって切れてしまうと、
工事完了後まで二度と再接続できなくなる。
最長丸二日間切れたままというのはあまりに不便なので、
11/2(月) の未明に認証サーバの設定変更を行ない、
同日午前 9:00-12:00 に回線切替工事を行なうことにした。
これなら PPPoE セッションが切れても数時間の切断で済む。
</p>

<span id="more-180"></span>
<p>
工事当日 11/2 朝、
すでに認証サーバの設定変更が行なわれたはずだから、
いま PPPoE セッションが切れると終わりだな～
とドキドキしながら回線切替工事を待つ。
10:06 に、
日本コムシスの工事担当者から電話があった。
これから工事に伺うとのこと。
ONU の置き換えだけだと私は思っていたのだが、
宅外の光ファイバ引き込み部分で光ファイバのつなぎ替えも行なうらしい。
そういえば何日か前、
光ファイバの敷設 (電柱の上なので架設?) 作業を近所で行なっていた。
もしかすると私の自宅にファイバを引くためだけに、
敷設作業を行なっていたのかもしれない。
</p>
<p>
<a href="http://twitter.com/gcd_org/status/5351446126">twitter で実況した</a>ように、
10:18 に ONU の電源を落として工事開始。
B フレッツ用の ONU (品名 B-PONHA B-ONU-E1 日立製作所製) から光ファイバを外し、
<a href="http://www.fujikura.co.jp/products/tele/o_f_splicers/td70025.html">超小型可視光レーザー光源</a> (フジクラ製) をつなぐ。
</p>
<img src="http://www.gcd.org/sengoku/picts/B-PONHA.jpg" width=356 height=267 alt="B-PONHA B-ONU-E1" />
<p>
何のためにレーザを光ファイバに入れるのかと工事担当者に質問してみた。
光ファイバの断線を検知するためと、
宅外で光ファイバをつなぎ替える際、
つなぐファイバを間違えないようにするためらしい。
宅外のつなぎ替えが終わると、
フレッツ 光ネクスト用の ONU (品名 GE-PON &lt;M&gt;A GE-PON-ONU 三菱電機製)
に光ファイバをつないで<a href="http://twitter.com/gcd_org/status/5352381787">工事完了</a>。
</p>
<p>
正確に言うと、光ファイバのコネクタ
(<a href="http://www.adamant-kogyo.com/japanese/products/fiber-optic-components/connectors-adapters/sc-connector-adapter.html">SC コネクタ</a>)
の付け替えも行なった。
旧ONU は 2004年12月18日に Bフレッツを契約したとき設置したものだが、
当時は宅内配線用の外装 (PVC被覆) 付き光ファイバケーブル
(ドロップ光ファイバ, 写真↑ 上から伸びている白っぽいケーブル)
にコネクタを直結できなかったらしく、
コネクタ付き光ファイバケーブル
(写真↑ レーザ光源につないでいる黄色のケーブル) と、
宅内配線用ケーブル双方の光ファイバを露出させて
(細く黒っぽい色なので写真↑ では見えにくい) 接続していた
(<a href="http://www.optronics.co.jp/fiber/kaiketsu/lesson/chap_9/cha_9main.html">メカニカルスプライス法</a>)。
今はドロップ光ファイバに直接コネクタを取りつけることができるらしい。
コネクタを取りつけた後、
光レベルを測定していた (-15.96dBm のようだが詳細不明)。
</p>
<p>
フレッツ 光ネクストには、
Bフレッツの 「<a href="http://www.flets/">FLET'S SQUARE</a>」
に相当するものはなく、
代わりに IPv6 ベースの
「<a href="http://www.flets-east.jp/">FLET'S SQUARE NEXT</a>」
がある。
<a href="http://www.flets-east.jp/speed/index.html">フレッツ速度測定 (IPv6) サイト</a>
で測定してみる。
</p>
<blockquote>
フレッツ速度測定(IPv6)サイトでは、お客様PC端末からNGNを経由して、<br />
フレッツ・スクウェア ネクスト(NGN内に設置したIPv6速度測定サーバ) までの、<br />
IPv6プロトコルを使った通信速度をご確認いただけます。
<div align="right"><a href="http://www.syutoken-speed.flets-east.jp/speedtest_ngn_200.html">フレッツ速度測定 (IPv6) サイト</a> から引用</div>
</blockquote>
<p>
IPv6 なので (ルータを介さずに) WAN 側に直接 PC をつなぐか、
あるいは<a href="http://www.gcd.org/blog/2006/04/29/">ブルータ</a>を利用する必要がある。
私の自宅内LAN では、
WAN 側セグメントと LAN 側セグメントをルータ (というか Linux マシン)
で分離しているので、
LAN 側から NGN へ IPv6 で通信することはできないが、
(ルータ代わりの二台の Linux マシンの他に) 一台だけ両セグメントにつながっている
<a href="http://www.gcd.org/sengoku/machine.ja.html#KAJIGAYA">Windows XP マシン</a>
がある。
この XP マシンで速度測定をやってみる。
</p>
<p>
ところが何度やっても 100Mbps 未満の値しかでず、
何のためにわざわざ回線工事を行なったのやら、
と嘆いていたら、
この XP マシンの二つの NIC のうち一つは 100baseT
(Mother Board 上の Intel Pro 10/100 LAN) だったことを思い出した
(もう片方は
<a href="http://kuroutoshikou.com/products/network/gbe-pci2.html">玄人志向 GbE-PCI2</a>)。
二本の ether ケーブルを逆につないで速度測定をやり直したところ、
無事 200Mbps 以上の値がでるようになった。
そして、
なぜか 200Mbps 以上の値が出てしまった。
</p>
<img src="http://www.gcd.org/sengoku/picts/200Mbps.jpg" width=280 height=202 alt="231.85Mbps" />
<p>
Bフレッツの時はフレッツ速度測定サイトで 80Mbps ほどの値だったので、
回線切替してよかったと言えるのかもしれないが、
NGN までの帯域が広くても、
その先の OCN (プロバイダ) の方があまり変っていない
(巷の速度測定サイトで 40Mbps ～ 60Mbps) ように感じられる
(まあ料金が変らないので文句も言えないが)。
それより、
従来は勤務先の LAN まで<a href="http://www.gcd.org/blog/2008/03/152/">フレッツ網経由</a>で 40Mbps くらい出ていたのが、
NGN とフレッツ網は相互に通信できないらしく、
勤務先の LAN へ IPv6 で直接通信できなくなってしまったのが痛い。
いったんプロバイダ経由でインターネットに出て通信せざるを得なくなり、
帯域が 1/4 以下になってしまった (勤務先が OCN でないのが影響している?)。
なお、
遅延 (latency) は切替前後でほとんど差がない。
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2009/11/180/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>__sync_bool_compare_and_swap_4 とは何か？ ～ glibc をビルドする場合は、 gcc の &#8211;with-arch=i686 configure オプションを使ってはいけない</title>
		<link>http://www.gcd.org/blog/2009/10/179/</link>
		<comments>http://www.gcd.org/blog/2009/10/179/#comments</comments>
		<pubDate>Thu, 08 Oct 2009 00:39:55 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>
		<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/2009/10/179/</guid>
		<description><![CDATA[glibc-2.10.1 をビルドしようとしたら、 「__sync_bool_compare_and_swap_4 が定義されていない」 というエラーが出た: senri:/usr/local/src/glibc-2.1 [...]]]></description>
			<content:encoded><![CDATA[<p>
glibc-2.10.1 をビルドしようとしたら、
「__sync_bool_compare_and_swap_4 が定義されていない」
というエラーが出た:
</p>
<pre class="terminal">
senri:/usr/local/src/glibc-2.10.1.i386 % ../glibc-2.10.1/configure
	...
senri:/usr/local/src/glibc-2.10.1.i386 % make
	...
/usr/local/src/glibc-2.10.1.i386/libc_pic.os: In function `__libc_fork':
/usr/local/src/glibc-2.10.1/posix/../nptl/sysdeps/unix/sysv/linux/i386/../fork.c:79: undefined reference to `__sync_bool_compare_and_swap_4'
/usr/local/src/glibc-2.10.1.i386/libc_pic.os: In function `__nscd_drop_map_ref':
/usr/local/src/glibc-2.10.1/nscd/nscd-client.h:320: undefined reference to `__sync_fetch_and_add_4'
	...
/usr/local/src/glibc-2.10.1.i386/libc_pic.os: In function `*__GI___libc_freeres':
/usr/local/src/glibc-2.10.1/malloc/set-freeres.c:39: undefined reference to `__sync_bool_compare_and_swap_4'
collect2: ld returned 1 exit status
make[1]: *** [/usr/local/src/glibc-2.10.1.i386/libc.so] Error 1
make[1]: Leaving directory `/usr/local/src/glibc-2.10.1'
make: *** [all] Error 2
</pre>
<p>
__sync_bool_compare_and_swap_4 は gcc の組み込み関数なので、
関数が未定義であることを示す
「undefined reference to ...」 というエラーメッセージは、
誤解を招く不親切なメッセージだと思う。
</p>
<p>
__sync_bool_compare_and_swap_4(mem, oldval, newval) は、
mem が指し示すメモリの値 (4バイト分) が oldval であれば newval に変更する、
という操作をアトミックに行なう組み込み関数。
<a href="http://ja.wikipedia.org/wiki/%E4%B8%8D%E5%8F%AF%E5%88%86%E6%93%8D%E4%BD%9C">アトミック (不可分) 操作</a>とは、
操作の途中が存在してはいけない操作のことで、
この例なら比較 (メモリの値が oldval か?)
と代入 (newval に変更) が必ず 「いっぺん」 に行なわれ、
「比較だけ行なったけどまだ代入は行なわれていない」
という状態が存在しないことを意味する。
</p>
<p>
アトミックに行なうためには、
当然ながら CPU でその操作をサポートしている必要がある
(複数個の命令の列で実現しようとすると、
命令列の半ばを実行中の状態が必ず存在してしまう)
わけだが、
残念ながら Intel 386 プロセッサでは、
この compare_and_swap
(<a href="http://faydoc.tripod.com/cpu/cmpxchg.htm">CMPXCHG 命令</a>)
をサポートしておらず、
サポートするのは Intel 486 以降の CPU である。
テストプログラムを書いて試してみる:
</p>
<pre class="code">
#include &lt;stdio.h&gt;

int main() {
    int mem[1], oldval, newval;
    oldval=0;
    newval=1;
    mem[0] = 0;
    __sync_bool_compare_and_swap(mem, oldval, newval);
    printf("mem[0]=%d\n", mem[0]);
    return 0;
}
</pre>
<p>
見ての通り、
mem[0] の値を oldval の値 (0) と比較し、
一致していたら newval の値 (1) を代入し、
mem[0] の値を表示するだけのプログラムである。
</p>
<p>
関数名が 「__sync_bool_compare_and_swap」 であって、
後ろに 「_4」 がついていないことに注意。
gcc が引数の型 (この例では int) を見て、
その型のビット長を後ろにつけてくれる
(この例では int 型は 4 バイトなので 「_4」 をつけてくれる)。
</p>
<p>
gcc では 「-march=タイプ」 オプションを指定することによって
CPU タイプを指定できる。
-march オプションを指定しなかったり
(この場合は全 CPU でサポートされる組み込み関数のみ利用できる)、
あるいは -march=i386 を指定したりすると、
コンパイル時にエラーになる:
</p>
<pre class="terminal">
% gcc -Wall test.c
/tmp/cc4eNX6L.o: In function `main':
test.c:(.text+0x3b): undefined reference to `__sync_bool_compare_and_swap_4'
collect2: ld returned 1 exit status
% gcc -Wall -march=i386 test.c
/tmp/cc6chtFj.o: In function `main':
test.c:(.text+0x36): undefined reference to `__sync_bool_compare_and_swap_4'
collect2: ld returned 1 exit status
% gcc -Wall -march=i486 test.c
% ./a.out
mem[0]=1
</pre>
<p>
いまさら i486 というのもアレなので、
今なら i686 を指定するのがよさげ。
私の手元にはいまだ PentiumIII マシンがあるものの、
PentiumIII より古いマシンはない
(昨年 <a href="http://www.gcd.org/blog/2008/03/151/">ML115 と SC440 を買った</a>とき
<a href="http://www.gcd.org/sengoku/machine.ja.html#ASAO1">PentiumII マシン</a>を引退させた)
ので、
pentium3 を指定すれば
SSE (Streaming SIMD Extensions) が利用できるようになるが、
glibc をビルドするときに必要かというと、
たぶん必要ない。
</p>
<p>
というわけでエラーの原因は分かったが、
では glibc をビルドするときは、
どうすればいいだろうか？
</p>
<p>
とりあえず google で検索してみたら、
gcc の configure オプションに
「--with-arch=i686」
を指定して gcc をビルドする必要がある、
と書いてあるページが見つかった。
</p>
<p>
--with-arch オプションは、
-march のデフォルトを設定するための configure オプションである。
つまり 「--with-arch=i686」 を指定して gcc を再インストールすると、
gcc に -march オプションをつけなくてもデフォルトが i686 になる。
なるほど確かにそうすれば、
glibc 側で何も変更せずに
 __sync_bool_compare_and_swap_4 関数が使えるようになりそうである。
</p>
<p>
いまどき i686 以前の
CPU 用のコードが必要になりそうなケースは滅多にないだろうから、
-march オプションのデフォルトを i686 にするのも悪い選択ではないように思えた。
gcc をビルドし直すのは面倒だなーと思いつつも、
ついでに gcc のバージョンを上げておこうと
gcc-4.3.4 をダウンロードしてきて  「--with-arch=i686」 付でビルドしてみた。
</p>
<p>
ところが！
</p>

<span id="more-179"></span>
<p>
「--with-arch=i686」付でビルドした gcc を使って
glibc をビルドしようとすると、
先ほどコンパイラエラーが出た場所より前の段階で、
アセンブラがエラーを出力して make が止まってしまった:
</p>

<pre class="terminal">
senri:/usr/local/src/glibc-2.10.1.i386 % make
	...
../sysdeps/i386/fpu/s_frexp.S: Assembler messages:
../sysdeps/i386/fpu/s_frexp.S:66: Error: invalid identifier for ".ifdef"
../sysdeps/i386/fpu/s_frexp.S:66: Error: junk at end of line, first unrecognized character is `1'
	...
../sysdeps/i386/fpu/s_frexp.S:66: Error: ".endif" without ".if"
../sysdeps/i386/fpu/s_frexp.S:66: Error: junk `.get_pc_thunk.dx' after expression
make[2]: *** [/usr/local/src/glibc-2.10.1.i386/math/s_frexp.os] Error 1
make[2]: Leaving directory `/usr/local/src/glibc-2.10.1/math'
make[1]: *** [math/subdir_lib] Error 2
make[1]: Leaving directory `/usr/local/src/glibc-2.10.1'
make: *** [all] Error 2
</pre>
<p>
こちら (コンパイラ側) を立てれば、あちら (アセンブラ側) が立たず、
な二律背反状態。
</p>
<p>
「invalid identifier for ".ifdef"」
というエラーメッセージが (ぱっと見には) 意味不明である。
続いて
「junk at end of line, first unrecognized character is `1'」
と言っているから、
「.ifdef 1」 みたいなコードなのだろうか？
エラーが起きた
sysdeps/i386/fpu/s_frexp.S の 66行目あたりは以下のようになっている。
LOAD_PIC_REG の行が問題の 66行目:
</p>
<pre class="code">
	cmpl	$0x00100000, %eax
	jae	2f

	fldl	VAL0(%esp)
#ifdef	PIC
	LOAD_PIC_REG (dx)
#endif
	fmull	MO(two54)
	movl	$-54, %ecx
	fstpl	VAL0(%esp)
	fwait
	movl	VAL1(%esp), %eax
	movl	%eax, %edx
	andl	$0x7fffffff, %eax
</pre>
<p>
おそらく LOAD_PIC_REG マクロを展開した結果がおかしいのだろうと、
LOAD_PIC_REG マクロの定義 (sysdeps/i386/sysdep.h) を調べてみる:
</p>
<pre class="code">
# define SETUP_PIC_REG(reg) \
  .ifndef __i686.get_pc_thunk.reg;					      \
  .section .gnu.linkonce.t.__i686.get_pc_thunk.reg,"ax",@progbits;	      \
  .globl __i686.get_pc_thunk.reg;					      \
  .hidden __i686.get_pc_thunk.reg;					      \
  .type __i686.get_pc_thunk.reg,@function;				      \
__i686.get_pc_thunk.reg:						      \
  movl (%esp), %e##reg;							      \
  ret;									      \
  .size __i686.get_pc_thunk.reg, . - __i686.get_pc_thunk.reg;		      \
  .previous;								      \
  .endif;								      \
  call __i686.get_pc_thunk.reg

# define LOAD_PIC_REG(reg) \
  SETUP_PIC_REG(reg); addl $_GLOBAL_OFFSET_TABLE_, %e##reg
</pre>
<p>
プリプロセッサの出力を確認してみると、
66行目は次のようにマクロ展開されている:
</p>
<pre class="code">
 .ifndef 1 .get_pc_thunk.dx; .section .gnu.linkonce.t. 1 .get_pc_thunk.dx,"ax",@progbits; .globl 1 .get_pc_thunk.dx; .hidden 1 .get_pc_thunk.dx; .type 1 .get_pc_thunk.dx,@function; 1 .get_pc_thunk.dx: movl (%esp), %edx; ret; .size 1 .get_pc_thunk.dx, . - 1 .get_pc_thunk.dx; .previous; .endif; call 1 .get_pc_thunk.dx; addl $_GLOBAL_OFFSET_TABLE_, %edx
</pre>
<p>
確かに、いきなり
「.ifndef 1」 となっているので、
エラーになるのは明らか。
上記 SETUP_PIC_REG マクロ定義内の
「__i686」 がことごとく 「1」 に展開されていて、
意味不明なコードになってしまっている。
</p>
<p>
というわけで、
原因に思い当たった。
プリプロセッサで自動的に定義されるマクロを表示させてみる:
</p>
<pre class="terminal">
% gcc -E -dM -x c /dev/null
#define __DBL_MIN_EXP__ (-1021)
#define __pentiumpro__ 1
#define __FLT_MIN__ 1.17549435e-38F
#define __DEC64_DEN__ 0.000000000000001E-383DD
#define __CHAR_BIT__ 8
	...
#define __i686 1
	...
#define __i686__ 1
	...
</pre>
<p>
「#define __i686 1」 だから当然そういうマクロ展開が行なわれる、
というわけ。
gcc と glibc では違うソフトウェアであるとはいえ、
gcc のプリプロセッサで自動的に定義される
「#define __i686 1」 とバッティングするようなラベル
「__i686.get_pc_thunk.reg」
をマクロ中で使うというのは、
いかがなものか。＞ glibc
</p>
<p>
「--with-arch=i686」付でビルドした gcc,
あるいは 「-march=i686」 オプション付で実行した gcc は、
-march を指定しない gcc と比べて、
プリプロセッサにおいて以下の定義が追加されるようだ:
</p>
<pre class="terminal">
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1
#define __i686 1
#define __i686__ 1
#define __pentiumpro 1
#define __pentiumpro__ 1
</pre>
<p>
ちなみに、
SETUP_PIC_REG マクロ定義内で定義されている __i686.get_pc_thunk.reg は、
eip の値をレジスタ reg へ代入するための関数 (というかサブルーチン)。
サブルーチン名の末尾の 「reg」 はマクロ引数で置換される。
この例では dx。
つまり SETUP_PIC_REG マクロを展開することにより
__i686.get_pc_thunk.dx というサブルーチンが定義される。
</p>
<p>
そして、
このサブルーチンを呼び出す (call __i686.get_pc_thunk.dx) と、
スタックトップをレジスタへ代入 (movl (%esp), %edx) して復帰 (ret) する。
つまりプログラムカウンタ (スタックトップに積まれた戻りアドレス)
の値がレジスタ edx へ代入される、というわけ。
</p>
<p>
続いて、
edx に $_GLOBAL_OFFSET_TABLE_ を加算することにより
edx にグローバル変数領域へのポインタが代入される
(addl $_GLOBAL_OFFSET_TABLE_, %edx)。
</p>
<blockquote>
__i686.get_pc_thunk.bx は、
gccが用意してくれる関数で、
PC (プログラムカウンタ、EIP レジスタの値) を ebx にコピーします。<br />
　(中略)<br />
なんでここで PC が必要かというと、
グローバル変数にアクセスしたいからです。
PIC/PIE の場合、
実行時に自分自身 (ELF バイナリ)
がどの仮想アドレスに mmap されるかはわかりませんので、
アドレス決め打ちで変数にアクセスはできません。
でも、
いま実行している命令 (61d:) から変数までのオフセットは
*.o をリンクして so にした時点で判明します
(so ファイルのナカミがほぼそのままメモリに貼られるわけなんで)。<br />
　(中略)<br />
mov 0xほげ(%eip) ふが; とか出来れば一番いいんですが(x86_64はできる)、
x86はそういうPC相対のアドレス指定はできません。
というか、eipを直接的に得ることすら出来ません。
なんで、仕方なく一度call命令を発行して、
リターンアドレス（callを発行した命令の次の命令のアドレス）
をスタック上に自動push させて、
__i686.get_pc_thunk.bx内でebxレジスタにpopしてます。
x86だと、PIC/PIEなコードでグローバル変数を触るだけで、
関数呼び出しと同じようなコストがかかるんざますよ！奥様。
<div align="right"><a href="http://d.hatena.ne.jp/yupo5656/20060907">memologue 2006-09-07</a> から引用</div>
</blockquote>
<p>
glibc でどんなグローバル変数にアクセスしているのかと思って、
ちょっと調べてみた:
</p>
<p>
この sysdeps/i386/fpu/s_frexp.S は、
そのファイル名の通り
<a href="http://www.linux.or.jp/JM/html/LDP_man-pages/man3/frexp.3.html">frexp(3)</a>
のアセンブリ言語による実装。
C による実装は sysdeps/ieee754/dbl-64/s_frexp.c にある。
ディレクトリ名から分かるように、
<a href="http://ja.wikipedia.org/wiki/IEEE_754">IEEE 754</a>
(IEEE 浮動小数点数演算標準) に基づいている。
</p>
<p>
double frexp(double x, int *exp) は、
浮動小数点実数 x を正規化小数と指数に分解し、
指数を *exp に格納した上で正規化小数を返す。
浮動小数点実数 x が<a href="http://ja.wikipedia.org/wiki/%E9%9D%9E%E6%AD%A3%E8%A6%8F%E5%8C%96%E6%95%B0">非正規化数</a>であるとき
frexp は x に 2^54 を乗じた上で *exp に -54 を返すのだが、
この 2^54 = 18014398509481984 という定数を double 型で保持するために、
グローバル変数 two54 を使用している。
</p>

<p>
話が脱線したので元に戻すと、
要は
「--with-arch=i686」 付でビルドした gcc だと、
C のソースだろうとアセンブリ言語で書かれたソースだろうと見境なく
「#define __i686 1」 を定義してしまうので、
相手が C のソースの時のみ 「-march=i686」 を付けて gcc を実行すればよい。
つまり、
gcc は 「--with-arch=i686」 付でビルドしてはいけない。
</p>
<p>
ではどうすればいいか？
いろいろ方法はあると思うが、
glibc をビルドするときに CFLAGS を
「CFLAGS="-g -O2 -march=i686"」 などと指定して
configure を実行するのも一つの解決策。
CFLAGS は C のソースをコンパイルするときだけ使われて、
アセンブルするときには使われないので、
前述したようなコンパイラ/アセンブラ二律背反問題を回避できる。
というわけで、
無事 glibc 2.10.1 をビルドすることができた:
</p>
<pre class="terminal">
senri:/usr/local/src/glibc-2.10.1.i386 % ../glibc-2.10.1/configure CFLAGS="-g -O2 -march=i686"
	...
senri:/usr/local/src/glibc-2.10.1.i386 % make
	...
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2009/10/179/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>文字化けしていなくても MySQL 内の文字コードが正しくない場合がある</title>
		<link>http://www.gcd.org/blog/2009/09/177/</link>
		<comments>http://www.gcd.org/blog/2009/09/177/#comments</comments>
		<pubDate>Sun, 13 Sep 2009 23:19:29 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/2009/09/177/</guid>
		<description><![CDATA[MySQL 5 からテーブルごとに文字列のエンコーディングを指定できるようになった (「そんなことは知ってるYO!」という人も多いと思うので、 そういう人は「これからが本題」 の部分まで読み飛ばして欲しい)。 例えばテー [...]]]></description>
			<content:encoded><![CDATA[<p>
MySQL 5 からテーブルごとに文字列のエンコーディングを指定できるようになった
(「そんなことは知ってるYO!」という人も多いと思うので、
そういう人は「<a href="http://www.gcd.org/blog/2009/09/177/#UTF8LATIN1">これからが本題</a>」
の部分まで読み飛ばして欲しい)。
例えばテーブルを作るときに、
</p>
<pre class="terminal">
mysql&gt; CREATE DATABASE test;
Query OK, 1 row affected (0.05 sec)

mysql&gt; USE test
Database changed
mysql&gt; CREATE TABLE user ( name VARCHAR(255) ) CHARSET=utf8;
Query OK, 0 rows affected (0.05 sec)

</pre>
<p>
などと 「CHARSET=utf8」 を指定すれば、
文字列を UTF-8 エンコーディングで格納する。
「CHARSET」 すなわち 「文字集合」 と、
エンコーディング (文字符号化) は本来別の概念であるが、
MySQL の場合は両者をまとめて CHARSET ないし character_set と呼んでいるので、
ここではそれを踏襲してキャラクタセットと呼ぶことにする。
MySQL のシステム変数のうちキャラクタセットに関連するものは、
以下のように沢山ある。
</p>
<pre class="terminal">
mysql&gt; SHOW VARIABLES LIKE 'character\_set\_%';
+--------------------------+--------+
| Variable_name            | Value  |
+--------------------------+--------+
| character_set_client     | latin1 |
| character_set_connection | latin1 |
| character_set_database   | latin1 |
| character_set_filesystem | binary |
| character_set_results    | latin1 |
| character_set_server     | latin1 |
| character_set_system     | utf8   |
+--------------------------+--------+
7 rows in set (0.00 sec)

</pre>
<p>
たくさんあってややこしいが、
重要なのは 「character_set_client」 と
「character_set_connection」 「character_set_results」 で、
この3変数はクライアントがクエリを送信し、
クエリ結果を受信するときのキャラクタセットを設定する。
charset コマンド (あるいは SET NAMES) を使うと、
クライアント側のキャラクタセットに関係するこの3変数を一度に変更できるので、
特に必要がなければこの3変数は常に同じキャラクタセット、
すなわちクライアント側で送受信するキャラクタセットに一致させておくとよい
(PHP スクリプトから MySQL をアクセスするときは、
mysql_set_charset() を使ってクライアント側のキャラクタセットを設定する)。
</p>
<pre class="terminal">
mysql&gt; CHARSET utf8
Charset changed
mysql&gt; SHOW VARIABLES LIKE 'character\_set\_%';
+--------------------------+--------+
| Variable_name            | Value  |
+--------------------------+--------+
| character_set_client     | utf8   |
| character_set_connection | utf8   |
| character_set_database   | latin1 |
| character_set_filesystem | binary |
| character_set_results    | utf8   |
| character_set_server     | latin1 |
| character_set_system     | utf8   |
+--------------------------+--------+
7 rows in set (0.00 sec)

mysql&gt; INSERT INTO user VALUES ('仙石　浩明');
Query OK, 1 row affected (0.00 sec)

mysql&gt; SELECT name, HEX(name) FROM user;
+-----------------+--------------------------------+
| name            | HEX(name)                      |
+-----------------+--------------------------------+
| 仙石　浩明      | E4BB99E79FB3E38080E6B5A9E6988E |
+-----------------+--------------------------------+
1 row in set (0.00 sec)

</pre>
<p>
このように 「select HEX()」 で確認すると、
文字列が正しく UTF-8 エンコーディングで格納されていることが確認できる。
</p>
<blockquote>
蛇足だが、mysql クライアントが GNU readline を使っている場合は、
~/inputrc などで 「set convert-meta Off」 を設定しておく。
デフォルトの readline では convert-meta が On なので、
キャラクタの最上位ビット (MSB) を 0 にしてしまう。
つまり UTF-8 (および EUC-JP や Shift_JIS) などの 8bit キャラクタセットだと、
MSB が 1 である文字が正しく送信されない。
例えば 「あ」 (UTF-8 で E3 81 82) を入力しようとしても、
MSB が落ちた 「c^A^B」 (63 01 02) が送られてしまう。
</blockquote>
<p>
「character_set_server」 が latin1 になっていて気持ち悪いかも知れないが、
このシステム変数は新しく database を作るときのデフォルトを設定するものなので、
(latin1 な database は金輪際作らないというのでも無い限り)
変更する必要はない。
</p>
<p>
latin1 になっているもう片方の変数 「character_set_database」 は、
デフォルト database に合わせて (つまり USE コマンドを発行するごとに)
サーバがこの変数を変更するので、
これもユーザが変更する必要はない。
</p>
<p>
前置きが長くなったが、<a name="UTF8LATIN1">これからが本題</a>。
</p>
<p>
UTF-8 なテーブルを読み書きする際は、
「charset utf8」 コマンドを送信してクライアント側のキャラクタセットを
UTF-8 に設定すればよいのであるが、
デフォルトが latin1 であるクライアントも多い。
PHP などから MySQL サーバにアクセスする場合なども、
(PHP のビルド方法にも依存するが) デフォルトは latin1 になっている
(PHP の場合 mysql_client_encoding() で確認できる)。
このようなクライアントをデフォルトの latin1 のままで使うとどうなるだろうか？
</p>

<span id="more-177"></span>
<p>
クライアント側のキャラクタセットが latin1 のままで、
UTF-8 なデータをテーブルに挿入してみる:
</p>
<pre class="terminal-scroll">
mysql> DELETE FROM user;
Query OK, 1 row affected (0.03 sec)

mysql> INSERT INTO user VALUES ('仙石　浩明');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT name, HEX(name) FROM user;
+-----------------+--------------------------------------------------------------------+
| name            | HEX(name)                                                          |
+-----------------+--------------------------------------------------------------------+
| 仙石　浩明 | C3A4C2BBE284A2C3A7C5B8C2B3C3A3E282ACE282ACC3A6C2B5C2A9C3A6CB9CC5BD |
+-----------------+--------------------------------------------------------------------+
1 row in set (0.00 sec)

</pre>
<p>
文字化けせずに読み書きできているし、
同様に (latin1 な) PHP スクリプトからこのレコードを読んでも、
正しい UTF-8 な文字列として読み込むことができる。
ところが、
HEX() 演算子で文字列のコードをチェックしてみると、
全く違うコード (しかも正しい UTF-8 コードが 15バイトであるのに対し、
2倍以上長い 33バイト)
になってしまっていることがわかる。
</p>
<p>
文字化けしていれば誰でも気付くし、
直そうとするので問題は少ないのであるが、
このように文字化けしない場合は、
問題が放置されたままになりかねないので注意を要する。
</p>
<p>
例えば google で検索してみると、
mysqldump で文字化けしないようにするための 「ノウハウ」 として、
「--default-character-set=latin1」 オプションを指定すればよい、
と書かれているページが沢山見つかる。
おそらく、
latin1 なクライアント (例えば PHP スクリプト) で、
非 latin1 文字列 (例えば漢字文字列) をデータベースに書込んでしまったために、
mysqldump でバックアップを取るときに
latin1 を指定しないと文字化けしてしまう状況なのだろう。
</p>
<p>
もちろんこれは間違った対処法である。
UTF-8 なテーブルをバックアップするときは 「--default-character-set=utf8」
オプションを指定すべきだし、
もしこのオプションで文字化けするなら、
テーブルに格納されたデータが間違っているわけで、
修正すべきはテーブル内のデータであって、
指定するオプションではない。
</p>
<p>
上述した実行例のように、
HEX() 演算子で文字列のコードをチェックすれば、
テーブルに正しいエンコーディングで格納されているか確認できるが、
クエリ結果の罫線がずれていないかでも間違いに気付くことはできる。
上記の例ではクエリ結果に 「仙石　浩明」
という漢字文字列が正しく表示されているものの、
その次の罫線キャラクタ「|」の位置がずれている。
これは mysql クライアントが、
長さ 15 文字の latin1 文字列を出力したつもりになっていることを意味している。
その latin1 な 15 文字を、
端末側が勝手に UTF-8 な (全角) 5文字として解釈したため
「|」 の位置がずれてしまった、
というわけ。
</p>
<p>
では、なぜテーブル内のデータがこんな長大なコードになってしまうのか？
</p>
<pre class="code">
正: E4BB99E79FB3E38080E6B5A9E6988E
誤: C3A4C2BBE284A2C3A7C5B8C2B3C3A3E282ACE282ACC3A6C2B5C2A9C3A6CB9CC5BD
</pre>
<p>
INSERT 文で 「仙石　浩明」 という値のレコードを挿入したとき、
送信したデータは正しい UTF-8 文字列なのであるが、
クライアント側のキャラクタセットの設定が latin1 なので、
MySQL サーバは受信したデータを latin1 文字列と見なす。
つまり、
「<tt>E4 BB 99 E7 9F B3 E3 80 80 E6 B5 A9 E6 98 8E</tt>」
というデータは
「&#x00E4; &#x00BB; &trade; &#x00E7; &Yuml; &#x00B3; &#x00E3; &euro; &euro;
&#x00E6; &#x00B5; &#x00A9; &#x00E6; &tilde; &#381;」
(見易くするため文字と文字の間に空白を挟んだ)
という latin1 文字列として扱われる。
</p>
<p>
「&#x00E4;」 (小文字のａウムラウト) の UTF-8 コードは、
「<tt>C3 A4</tt>」 である。以下同様に、<br />
「&#x00BB;」 (right-pointing double angle quotation mark) は 「<tt>C2 BB</tt>」<br />
「&trade;」 (trade mark sign) は 「<tt>E2 84 A2</tt>」<br />
「&#x00E7;」 (latin small letter c with cedilla) は 「<tt>C3 A7</tt>」<br />
「&Yuml;」 (latin capital letter Y with diaeresis) は 「<tt>C5 B8</tt>」<br />
「&#x00B3;」 (superscript three) は 「<tt>C2 B3</tt>」<br />
「&#x00E3;」 (latin small letter a with tilde) は 「<tt>C3 A3</tt>」<br />
「&euro;」 (euro sign) は 「<tt>E2 82 AC</tt>」<br />
「&#x00E6;」 (latin small letter ae) は 「<tt>C3 A6</tt>」<br />
「&#x00B5;」 (micro sign) は 「<tt>C2 B5</tt>」<br />
「&#x00A9;」 (copyright sign) は 「<tt>C2 A9</tt>」<br />
「&tilde;」 (small tilde) は 「<tt>CB 9C</tt>」<br />
「&#381;」 (latin capital letter Z with caron) は 「<tt>C5 BD</tt>」
</p>
<p>
したがって、テーブルには
「C3 A4 C2 BB E2 84 A2 C3 A7 C5 B8
C2 B3 C3 A3 E2 82 AC E2 82 AC C3 A6
C2 B5 C2 A9 C3 A6 CB 9C C5 BD」 という 33バイトの UTF-8 コードが格納される。
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2009/09/177/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>wpa_supplicant のバグ: TLS拡張 (TLS Extensions) 対応の OpenSSL と使うと、WPA EAP-TLS が常に失敗する</title>
		<link>http://www.gcd.org/blog/2009/01/169/</link>
		<comments>http://www.gcd.org/blog/2009/01/169/#comments</comments>
		<pubDate>Wed, 07 Jan 2009 23:58:40 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/2009/01/169/</guid>
		<description><![CDATA[あけましておめでとうございます。 今年もよろしくお願いします。 自宅の無線LAN を WPA2-EAP (WPA2 エンタープライズ) にして 1ヶ月になるが、 すこぶる快適。 アクセスポイントがエンタープライズモードに [...]]]></description>
			<content:encoded><![CDATA[<p>
あけましておめでとうございます。
今年もよろしくお願いします。
</p>
<p>
<a href="http://www.gcd.org/blog/2008/12/165/">自宅の無線LAN を WPA2-EAP</a>
(WPA2 エンタープライズ) にして 1ヶ月になるが、
すこぶる快適。
アクセスポイントがエンタープライズモードに対応していなければいけないのと、
<a href="http://www.gcd.org/blog/2008/12/165/">RADIUS サーバ</a>が必要である上、
EAP-TLS を用いる場合は SSL クライアント証明書を発行する
<a href="http://www.gcd.org/sengoku/docs/NikkeiLinux00-09/SSL.ja.html#CA">認証局 (CA) を作る</a>必要まであるので、
自宅LAN での導入にはややハードルが高いが、
いったん導入してしまえば WPA/WPA2 パーソナルより手間がかからない。
</p>
<p>
WPA/WPA2 パーソナルだと、
秘密のパスワードを全ての無線LAN 端末で共有するので、
端末の台数が増えてくるとどんどん漏洩のリスクが高まる。
</p>
<blockquote>
自宅LAN でそんなに端末があるのか？
という突っ込みが入りそうだが、
<a href="http://www.gcd.org/blog/2008/12/168/">格安 NetBook</a> や、
無線LAN 機能付スマートフォンなどを買っていると、
自宅LAN とはいえ、
「エンタープライズ」 的なニーズが出てくる。
</blockquote>
<p>
WPA2 エンタープライズで認証方式として EAP-TLS を使うと、
無線LAN 端末にはそれぞれ個別の証明書をインポートしておくだけでよく、
万一その端末を紛失しても、
その端末の証明書を無効にするだけで済む。
</p>
<p>
EAP-TLS には対応機器/OS が多いというメリットもある。
無線LAN 端末として、
Windows VISTA, Windows XP,
<a href="http://www.ubuntu.com/getubuntu/releasenotes/804overview">Ubuntu 8.04 LTS</a> などを使ってみたが、
いずれもあっけないくらい簡単に接続できてしまった。
証明書さえインポートしておけば (Windows ならダブルクリックだけ)、
あとはパスワードを入力しなくてもいいぶん WPA/WPA2 パーソナルより設定が簡単。
</p>
<p>
既存の OS (Linux の場合はディストリビューション)
で無線接続できるようになったので、
次は私が独自に構築した GNU/Linux
(いわば <a href="http://www.gcd.org/blog/2007/09/129/#mydistribution">my distribution</a>)
上で WPA2 EAP-TLS 接続を試みた。
</p>
<p>
まず <a href="http://hostap.epitest.fi/wpa_supplicant/">wpa_supplicant</a> 0.5.11 をダウンロードしてコンパイル。
続いて設定ファイルである wpa_supplicant.conf を書く。
</p>
<pre class="code">
network={
	ssid="XXXXXXXXXX"
	key_mgmt=WPA-EAP
	eap=TLS
	identity="olevia"
	ca_cert="/usr/local/ssl/certs/GCD_Root_CA.pem"
	client_cert="/usr/local/ssl/certs/olevia.pem"
	private_key="/usr/local/ssl/private/olevia.pem"
	private_key_passwd=""
}
</pre>
<p>
「ca_cert=」 「client_cert=」 「private_key=」 にそれぞれ
CA の公開鍵、端末の公開鍵、端末の秘密鍵のファイル名を指定している。
で、wpa_supplicant を実行。
</p>
<pre class="terminal">
# wpa_supplicant -i eth1 -c /etc/wpa_supplicant.conf -d -D wext
Initializing interface 'eth1' conf '/etc/wpa_supplicant.conf' driver 'wext' ctrl_interface 'N/A' bridge 'N/A'
Configuration file '/etc/wpa_supplicant.conf' -> '/etc/wpa_supplicant.conf'
Reading configuration file '/etc/wpa_supplicant.conf'
Priority group 0
   id=0 ssid='XXXXXXXXXX'
Initializing interface (2) 'eth1'
EAPOL: SUPP_PAE entering state DISCONNECTED
EAPOL: KEY_RX entering state NO_KEY_RECEIVE
EAPOL: SUPP_BE entering state INITIALIZE
EAP: EAP entering state DISABLED
	...(中略)...
</pre>
<p>
wpa_supplicant はデバッグモード (-d オプション)
にすると大量のログを出力するので動作を追いにくいが、
wpa_supplicant のプログラムは状態遷移機械 (オートマトン)
として動作するよう書かれているので、
「EAP: EAP entering state」 の部分を追っていくとよい。
「DISABLED」 と出力されているのが、
その時点におけるオートマトンの状態。
</p>
<blockquote>
状態遷移機械 (オートマトン) と言ってしまうと、
コンピュータ/プログラムは全てオートマトンなのであるが (^^;)、
プログラミングの方法として状態遷移機械 (state machine) と言うときは、
各状態に名前を付けて、
各状態ごとの動作を (switch 文や if ... else if ... 文などで)
分けて書く方法を指す。
</blockquote>
<p>
状態には INITIALIZE, DISABLED, IDLE, RECEIVED,
GET_METHOD, METHOD, SEND_RESPONSE, DISCARD,
IDENTITY, NOTIFICATION, RETRANSMIT, SUCCESS,
FAILURE の 13状態がある。
もちろん 「SUCCESS」 が受理状態。
SUCCESS 状態は、
アクセスポイントが接続を許可した状態を意味する。
</p>
<p>
ところが、ログを追っていくと、
</p>
<pre class="terminal">
EAP: EAP entering state RECEIVED
EAP: Received EAP-Success
EAP: EAP entering state FAILURE
CTRL-EVENT-EAP-FAILURE EAP authentication failed
</pre>
<p>
FAILURE 状態 (非受理状態) に遷移している (*_*)。当然、接続できず。<br />
その直前に 「Received EAP-Success」 と出ているにもかかわらず！
</p>
<p>
アクセスポイント側 (正確に言うと RADIUS サーバ) のログを調べてみると、
ちゃんと接続を許可している (EAP-Success を送っているのだから当然だが)。
一体これはどうしたことか？
</p>
<p>
私がコンパイルした wpa_supplicant に問題があるのかと思って、
この wpa_supplicant を、
既に接続できることが確認済みの Ubuntu 上にコピーして実行してみる。
「Received EAP-Success」 をログの中から探してみると、
</p>
<pre class="terminal">
EAP: EAP entering state RECEIVED
EAP: Received EAP-Success
EAP: EAP entering state SUCCESS
CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully
</pre>
<p>
ちゃんと SUCCESS 状態に遷移しているし、接続もできた。<br />
<b>同一バイナリなのに、</b>異なる環境 (ディストリビューション) だと、
<b>どうして結果が異なるのか？</b>
</p>
<p>
どのような可能性が考えられるだろうか？
腕に覚えがあるかたは、
この続きを見ずに
(といっても、既にタイトルでネタバレしてしまっているが ^^;)
原因を推測してみてはいかがだろうか？
</p>

<span id="more-169"></span>
<p>
同一バイナリと言っても、
動的リンクしているので、
リンクしているライブラリは異なる。
</p>
<pre class="terminal">
% ldd /usr/local/sbin/wpa_supplicant
	linux-gate.so.1 =>  (0xffffe000)
	libssl.so.0.9.8 => /usr/local/lib/libssl.so.0.9.8 (0xf7f68000)
	libcrypto.so.0.9.8 => /usr/local/lib/libcrypto.so.0.9.8 (0xf7e41000)
	libdl.so.2 => /lib/libdl.so.2 (0xf7e3d000)
	libc.so.6 => /lib/libc.so.6 (0xf7d0b000)
	/lib/ld-linux.so.2 (0xf7fbf000)
</pre>
<p>
OpenSSL のライブラリである libssl.so.0.9.8 と libcrypto.so.0.9.8 が怪しそう。
試しにこの 2 ファイルを Ubuntu 上へコピーしてみる。
wpa_supplicant を再度実行してみると、
果たせるかな FAILURE 状態に遷移した。
</p>
<p>
といってもこの 2 ライブラリに問題があると決めつけるのは早計である。
実はこのライブラリは、
アクセスポイントが使用している RADIUS サーバにあるものと同一のもの
(RADIUS サーバも、私独自の my distribution 上で動いている)。
もしこのライブラリに問題があるなら他でも問題が出るはず。
一応、
念のため <a href="http://www.openssl.org">OpenSSL</a>
のバージョンを最新版の 0.9.8i に上げてみたが、
症状は変わらず FAILURE 状態に遷移する。
</p>
<p>
もうちょっとログを詳しく調べてみることにした。
SUCCESS に遷移するログ (Ubuntu に含まれる OpenSSL ライブラリを使用) と、
FAILURE に遷移するログ (自前でコンパイルしたライブラリを使用) とを、
「Received EAP-Success」 の時点から遡って比較してみる。
</p>
<p>
Ubuntu に含まれる OpenSSL ライブラリを使った場合:
</p>
<pre class="terminal">
EAP: EAP entering state METHOD
SSL: Received packet(len=69) - Flags 0x80
SSL: TLS Message Length: 59
SSL: (where=0x1001 ret=0x1)
SSL: SSL_connect:SSLv3 read finished A
SSL: (where=0x20 ret=0x1)
SSL: (where=0x1002 ret=0x1)
SSL: 0 bytes pending from ssl_out
OpenSSL: tls_connection_handshake - Failed to read possible Application Data error:00000000:lib(0):func(0):reason(0)
SSL: No data to be sent out
EAP-TLS: Done
EAP-TLS: Derived key - hexdump(len=64): [REMOVED]
EAP-TLS: Derived EMSK - hexdump(len=64): [REMOVED]
SSL: Building ACK
EAP: method process -> ignore=FALSE methodState=DONE decision=UNCOND_SUCC
EAP: EAP entering state SEND_RESPONSE
</pre>
<p>
METHOD 状態というのは、
Server Hello メッセージ
(<a href="http://www.gcd.org/blog/2007/06/122/">SSL 通信開始時のハンドシェーク</a>を参照)
の中の compression_method を読み終えた状態。
compression_method は Server Hello メッセージの最後のデータなので、
ログに 「SSL: SSL_connect:SSLv3 read finished A」 と出力し、
wpa_supplicant は続いて送信状態 SEND_RESPONSE へ遷移する。
</p>
<p>
一方、自前でコンパイルした OpenSSL ライブラリを使った場合:
</p>
<pre class="terminal">
EAP: EAP entering state METHOD
SSL: Received packet(len=1024) - Flags 0xc0
SSL: TLS Message Length: 1290
SSL: Need 276 bytes more input data
SSL: Building ACK
EAP: method process -> ignore=FALSE methodState=MAY_CONT decision=FAIL
EAP: EAP entering state SEND_RESPONSE
</pre>
<p>
METHOD 状態に遷移して compression_method を読み終えたはずなのに、
まだデータがある (Need 276 bytes more input data)。
</p>
<p>
...というところまでログの意味が理解できた時点で、
原因に思い当たった。
後者は Server Hello メッセージではなく、
Extended Server Hello メッセージだった。
Extended Server Hello メッセージにおいては、
compression_method が最後ではなく
server_hello_extension_list が後に続く
(<a href="http://www.ietf.org/rfc/rfc3546.txt">RFC 3546</a> 参照)。
</p>
<p>
なぜ同じ RADIUS サーバが Server Hello メッセージを送信したり、
Extended Server Hello メッセージを送信したり、
という違いが生じるかと言えば、
Ubuntu に含まれるライブラリを使った場合は、
wpa_supplicant が Client Hello メッセージを送信するのに対し、
自前でコンパイルしたライブラリを使った場合は、
Extended Client Hello メッセージを送信するから。
</p>
<p>
つまり、私が OpenSSL をコンパイルするときは、
<a href="http://www.gcd.org/blog/2007/06/122/">TLS拡張</a>
(TLS Extensions, <a href="http://www.ietf.org/rfc/rfc3546.txt">RFC 3546</a>)
のサポートを有効にしていたから、
このような差が生じたのだった
(RADIUS サーバが使用する OpenSSL も
TLS拡張のサポートを有効にしていた、という点もミソ)。
だから、試しに TLS拡張のサポートを無効にして
OpenSSL をコンパイルし直してみると、
無事 SUCCESS 状態に遷移して接続することができた。
</p>
<blockquote>
動的ライブラリで TLS拡張のサポートを無効にしてしまうと、
TLS拡張を使用するプログラム
(例えば <a href="http://www.gcd.org/sengoku/stone/Welcome.ja.html">stone</a>)
を実行するとき困るので、
TLS拡張のサポートを無効にしたライブラリを、
wpa_supplicant と静的リンクしておくことにした。
</blockquote>
<p>
OpenSSL 側で TLS拡張のサポートを無効にすれば対処できるとはいえ、
wpa_supplicant が正しく Extended Server Hello メッセージを解釈できたほうが、
より望ましいに違いない。
</p>
<p>
そこで、
<a href="http://w1.fi/bugz/">開発元のバグ報告ページ</a> に、
<a href="http://w1.fi/bugz/show_bug.cgi?id=294">バグレポート</a>を出してみた:
</p>
<pre class="terminal">
Summary: wpa_supplicant linked with TLSEXT-enabled OpenSSL always fails in WPA-EAP

wpa_supplicant linked with TLSEXT-enabled OpenSSL 0.9.8i always enters
`state FAILURE' in WPA-EAP although radius server authorizes.

OpenSSL supported TLS Extensions (RFC 3546) since 0.9.8 and if you want
to make TLSEXT enabled OpenSSL library, do:

  $ ./config enable-tlsext
  $ make

With this library, wpa_supplicant sends Extended Client Hello (see RFC
3546 Section 2.1), then TLSEXT-enabled radius server*1 responds with
Extended Server Hello (Section 2.2).  But unfortunately
wpa_supplicant-0.5.11 won't expect `server_hello_extension_list' field
in the Extended Server Hello, and enters the state FAILURE in spite of
receiving EAP-Success.

*1 TLSEXT-disabled radius server simply ignores
   `client_hello_extension_list' field in Extended Client Hello

EAP: EAP entering state RECEIVED
EAP: Received EAP-Request id=9 method=13 vendor=0 vendorMethod=0
EAP: EAP entering state METHOD
SSL: Received packet(len=1024) - Flags 0xc0
SSL: TLS Message Length: 1290
SSL: Need 276 bytes more input data
SSL: Building ACK
EAP: method process -> ignore=FALSE methodState=MAY_CONT decision=FAIL
EAP: EAP entering state SEND_RESPONSE
EAP: EAP entering state IDLE
EAPOL: SUPP_BE entering state RESPONSE
EAPOL: txSuppRsp
EAPOL: SUPP_BE entering state RECEIVE
RX EAPOL from XX:XX:XX:XX:XX:XX
EAPOL: Received EAP-Packet frame
EAPOL: SUPP_BE entering state REQUEST
EAPOL: getSuppRsp
EAP: EAP entering state RECEIVED
EAP: Received EAP-Success
EAP: EAP entering state FAILURE
CTRL-EVENT-EAP-FAILURE EAP authentication failed

I examined wpa_supplicant linked with TLSEXT-disabled OpenSSL library,
and it works perfectly.
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2009/01/169/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>initramfs シェル環境 (initramfs shell environment) でジョブ制御する方法 (aka &#8220;can&#8217;t access tty; job control turned off&#8221; を消す方法)</title>
		<link>http://www.gcd.org/blog/2008/10/163/</link>
		<comments>http://www.gcd.org/blog/2008/10/163/#comments</comments>
		<pubDate>Sun, 26 Oct 2008 22:38:04 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/2008/10/163/</guid>
		<description><![CDATA[GNU/Linux OS のブート時に、init(8) を経由せずにシェル (/bin/sh) を実行すると、 このシェル上ではジョブ制御 (job control) が行なえない。 つまりこのシェル環境は制御端末 (c [...]]]></description>
			<content:encoded><![CDATA[<p>
GNU/Linux OS のブート時に、init(8) を経由せずにシェル (/bin/sh) を実行すると、
このシェル上ではジョブ制御 (job control) が行なえない。
つまりこのシェル環境は制御端末 (controlling tty) に成れない。
これがどんなに不便かというと、
自動的に止まらないプログラム (例えばオプション無しで ping を実行したときなど)
を止める方法が無いわけで、
いったんそういうプログラムを動かしてしまったら最後、
CTRL-ALT-DEL で reboot させる他なくなってしまう。
</p>
<p>
そもそも、なぜ init(8) を起動する前に /bin/sh を実行したいかというと、
ミニルート
(<a href="http://www.gcd.org/blog/2007/09/129/">initramfs</a>) 上で
作業を行ないたいから。
initramfs の init (これは init(8) ではなくシェル・スクリプト) の中で、
<a href="http://busybox.net/">BusyBox</a> の /bin/sh (/bin/ash) を
exec する (つまり PID=1) ことによって、
initramfs 上での作業を可能にする。
</p>
<p>
init(8) は、
GNU/Linux OS の全てのプロセスの親プロセスだが、
その万物の親すら生まれていない創世記以前に作業を行なえるメリットは数多い。
例えば、ルート・ファイル・システム (root file system)
すらマウントしていない段階なので、
マウント後 (つまり init(8) 起動後) には実行不可能な操作
(xfs_repair などのファイルシステム修復操作とか) を行なうことができる。
しかもこのシェルはプロセスID が 1番なので、
このシェル環境上で root file system を「/」にマウントし、
続いて init(8) を exec すれば、
そのまま GNU/Linux OS を起動することができる。
</p>
<blockquote>
root file system のメンテナンス等は、
別の起動ディスク (CD-ROM や USB メモリ等) からブートして行なうのが一般的だが、
CD-ROM ドライブや USB メモリを準備したり、
あるいは CD-ROM や USB メモリの抜き差しが必要になったりと、
なにかと面倒である。
メンテナンス用の起動パーティションを root file system とは別に用意する、
という方法も考えられるが、
メンテナンス専用のパーティションを維持管理するのが面倒くさい
(普段使わないものほど陳腐化して、いざというとき役に立たない)。<br />
initramfs だとハードディスクすら不要
(例えば PXE ブート) でメンテナンスが可能になるし、
普段 GNU/Linux OS 起動用として使ってる initramfs が、
そのまま非常用のメンテナンス環境になるため、
陳腐化する心配がない。<br />
実は「<a href="http://www.gcd.org/blog/2008/01/148/">突然死したハードディスクを復旧させる『お手軽パック』</a>」は、
initramfs そのものだったりする。
しかも「復旧」用として作った initramfs というわけではなく、
私が普段 GNU/Linux OS を
<a href="http://www.gcd.org/blog/2007/09/129/">ブートするときに使っている initramfs</a> と
全く同じものである (だからこそ
<a href="http://www.gcd.org/blog/2008/01/147/">ハードディスクの突然死問題</a>
が勃発した直後にリリースできた)。
</blockquote>
<p>
というわけで、
いいことづくめの initramfs シェル環境 (initramfs shell environment) なのだが、
<a href="http://www.gcd.org/blog/2008/01/148/">「復旧お手軽パック」実行例</a>
にもあるように、
init(8) 以前の段階で /bin/sh を実行すると
</p>
<pre class="terminal">
/bin/sh: can't access tty; job control turned off
#
</pre>
<p>
と表示してジョブ制御がオフになってしまう。
つまりこのシェル環境では、
プログラムを実行中に control-C (^C) を押しても止める
(正確にいうと SIGINT シグナルを送る) ことができない。
</p>
<p>
ジョブ制御 (^C などでシグナルを送ること) ができない状態に陥って
初めて沸き起こる制御端末 (controlling tty) に対する感謝の念なのであるが、
initramfs が役目を終えて init(8) が起動して (GNU/Linux OS がブートして)
しまうと、
喉元過ぎればなんとやらで
「can't access tty」をなんとかしようという意欲は雲散霧消し、
そのままになっていた。
</p>
<p>
制御端末になれない initramfs シェル環境に対して何十回目かの悪態をついた後、
ようやく対策を立てるべく原因を調べてみることにした。
</p>
<blockquote>
Linuxカーネル（ドライバ）のソースを読んでみたところ、
以下の端末デバイスは制御端末に成れないのです。
興味がある人は、ソース、
drivers/char/tty_io.cのtty_open()を見てみてください。<br />
<br />
* /dev/console -- カーネルの起動時の端末。<br />
* /dev/tty0 -- tty1～の「Linux Virtual Terminal」のうち、現在表示している物を示す。<br />
* /dev/tty -- 現在使っている端末を示す。<br />
* PTYのマスター側<br />
<div align="right"><a href="http://omake.accense.com/wiki/LinuxControllingTTY">Linux:制御端末</a> から引用</div>
</blockquote>
<p>
initramfs シェル環境で使っている端末は /dev/console だから制御端末になれない。
だから BusyBox には /dev/console という仮想的な端末ではなく、
本物のデバイスを探すための cttyhack というプログラムが付属している。
/bin/sh を実行する代わりに cttyhack /bin/sh を実行すれば
ジョブ制御ができると BusyBox のマニュアルには書いてある。
</p>
<p>
...という解説は上に引用したページをはじめ、
WWW 上のあちらこちらのページで見かけるし、
私としても当然そんなことは先刻承知で、
</p>
<pre class="terminal">
/bin/sh: can't access tty; job control turned off
# tty
/dev/console
# cttyhack sh
sh: can't access tty; job control turned off
# tty
/dev/tty1
#
</pre>
<p>
などと、確かに cttyhack の働きにより /dev/console ではなく
/dev/tty1 を使うようになったものの、
相変わらず「can't access tty」エラーが出ているので困っているわけである。
cttyhack を使っているのにジョブ制御できないわけで、
cttyhack-- と思っていた。
</p>
<p>
前置きが長くなったが、ここからが本題である。
</p>

<span id="more-163"></span>
<blockquote>
Last night, I finally succeeded in vanquishing the dread
"can't access tty; job control turned off" message.
As it turns out, there are several discrete steps
involved in the creation of a controlling terminal,
and they're all seemingly mandatory:
<div align="right"><a href="https://secure.bonkabonka.com/blog/2008/05/15/turning_on_job_control.html">Turning on job control</a> から引用</div>
</blockquote>
<p>
「昨晩ついに、
恐怖の "can't access tty; job control turned off" メッセージを抑えつけた!」
という書き出しに、
いやがおうにも期待が高まる。
この、TTY を制御端末にするための必須条件とやらは、
今まで日本語での記述を見たことがないので、
訳してみる:
</p>
<ol>
<li>
session owner かつ process group leader でなければならない。
つまり前もって setsid(2) を呼び出しておく必要がある
(もし既に別の制御端末を持っていたら、先に捨てる)。</li>
<li>/dev/console などカーネルが提供する仮想的な端末ではなく、
/dev/tty1 など「本物の」TTY デバイスでなければならない。
/dev/console が制御端末になれないのは、
<a href="http://lkml.org/lkml/2000/9/15/110">Linux カーネル ML での議論</a>
によれば、init を CTRL-ALT-DEL に応答させる方法に関する歴史的理由による。
もし /dev/console が制御端末だったら、
CTRL-C が効いてブートスクリプトを止めることが可能になってしまう。
<li>CTRL-C, CTRL-Z その他ものもろのキーが入力できる
TTY でなければならない。</li>
<li>TIOCSCTTY ioctl を行なうことによって、
標準入力の TTY を制御端末にすることができる。
</ol>
<p>
つまり cttyhack が解決できるのは上記条件のうち 2番目の条件だけで、
1番目の条件は満たしていない。
そこで、cttyhack を呼ぶ前に setsid してみる:
</p>
<pre class="terminal">
# setsid cttyhack sh
# ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.077 ms
^C
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.077/0.077/0.077 ms
#
</pre>
<p>
お～ ついにあの忌まわしい「can't access tty; job control turned off」
エラーが表示されなくなり、
^C でプログラムの実行を止められるようになった。
</p>
<p>
結局、鍵は setsid にあったわけで、
「setsid cttyhack」をキーに検索してみると、
同様の話が他にも見つかった:
</p>
<blockquote>
setsid cttyhack ash
<br />
This makes the ash error about job control go away, <br />
and "echo Hi &gt; /dev/tty" now works.
<div align="right"><a href="http://www.uclibc.org/lists/busybox/2007-December/029642.html">setsid cttyhack ash</a> から引用</div>
</blockquote>
<p>
じゃ、なんで cttyhack は自前で setsid しないかというと、
cttyhack は init(8) から呼び出されることを前提としているからのようだ:
</p>
<blockquote>
On Fri, Dec 07, 2007 at 01:17:57PM -0800, Lombard, David N wrote:<br />
&gt; Hmmm.  cttyhack doesn't do:<br />
&gt;<br />
&gt; setsid()<br />
&gt; ioctl(0,TIOCSCTTY,1)<br />
<br />
Did you set CONFIG_FEATURE_INIT_SCTTY?  There's conditionally compiled
code in init to do the above, see init/init.c
<br />
<div align="right"><a href="http://www.busybox.net/lists/busybox/2007-December/029489.html">CTRL-C - Make the hurting stop</a> から引用</div>
</blockquote>
<p>
init(8) 起動前だからこそ、
initramfs シェル環境は有用であると思うのだが、
そのような initramfs の使い方は、
まだ一般的にはなっていないのだろう。
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2008/10/163/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>フレッツ・ドットネットを解約したら、フレッツ網 router へ ping6 できなくなった！</title>
		<link>http://www.gcd.org/blog/2008/03/152/</link>
		<comments>http://www.gcd.org/blog/2008/03/152/#comments</comments>
		<pubDate>Wed, 19 Mar 2008 06:32:29 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[IPv6]]></category>
		<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/2008/03/152/</guid>
		<description><![CDATA[昨年12月26日に、 NTT東日本から 「BフレッツにおけるＩＰｖ６映像視聴等機能の標準装備について」 というお知らせが来た。 3月3日より、 「Bフレッツ」に IPv6 映像視聴等機能を標準装備するので、 フレッツ・ド [...]]]></description>
			<content:encoded><![CDATA[<p>
昨年12月26日に、
NTT東日本から
「BフレッツにおけるＩＰｖ６映像視聴等機能の標準装備について」
というお知らせが来た。
3月3日より、
「Bフレッツ」に IPv6 映像視聴等機能を標準装備するので、
フレッツ・ドット・ネットの契約が不要になるとのこと。
</p>
<blockquote>
現在、ＩＰｖ６映像視聴等機能は「フレッツ・ドットネット」にて
提供しておりますが、
平成２０年３月３日（月）以降は、
ブロードバンド映像サービスのみ をご利用の場合は、
「フレッツ・ドットネット」のご契約が不要になります。
これにより解約を希望されるお客さまにつきましては、
平成２０年３月３日（月） より※３受付を開始いたします。
　なお、ブロードバンド映像サービス以外で、
「FdNネーム」「FdNディスク」「FdNディスクビューセレクト」「FdNナンバー」等
「フレッツ・ドットネット」サービス※４をご利用の際には、
引き続き「フレッツ・ドットネット」のご契約が必要となりますのでご注意ください。
</blockquote>
<p>
私は IPv6 機能のためだけに「フレッツ・ドットネット」を契約していて、
「FdNネーム」「FdNディスク」「FdNディスクビューセレクト」「FdNナンバー」等の
サービスを利用したことはない
(「ブロードバンド映像サービス」も利用していない)
ので、
<a href="http://www.flets/">フレッツ・スクウェア</a>トップから、
サービス申込受付を選んで、「フレッツ・ドットネット」を解約した。
</p>
<p>
<b>ところが！</b>
</p>
<p>
フレッツ網側の v6 ルータが ping に反応しなくなった。
</p>
<pre class="terminal">
senri % ping6 -n router.flets.gcd.org
PING router.flets.gcd.org(2001:c90:XXXX:XXXX:2d0:2bff:fe30:b91a) 56 data bytes
From 2001:c90:XXXX:XXXX:2d0:2bff:fe30:b91a icmp_seq=1 Destination unreachable: Administratively prohibited
From 2001:c90:XXXX:XXXX:2d0:2bff:fe30:b91a icmp_seq=2 Destination unreachable: Administratively prohibited

--- router.flets.gcd.org ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1000ms
</pre>
<p>
ちなみに、
この v6 ルータからの router advertisement は正常に流れてきている:
</p>
<pre class="terminal">
senri # tcpdump -i eth1 -vvv ip6
tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
	...
13:02:02.904899 fe80::2d0:2bff:fe30:b91a > ff02::1: icmp6: router advertisement(chlim=64, pref=medium, router_ltime=1800, reachable_time=0, retrans_time=0)(src lladdr: 00:d0:2b:30:b9:1a)(mtu: mtu=1500)[ndp opt] [class 0xe0] (len 64, hlim 255)
</pre>
<p>
v6 ルータ越えの通信が全て禁止されてしまったらしく、
フレッツ網に接続している他サイトとの IPv6 通信も同様に禁止されて
(Administratively prohibited) しまった。
例外は、<a href="http://flets-v6.jp">フレッツ・スクウェアv6</a> へのアクセス:
</p>
<pre class="terminal">
senri % ping6 -n flets-v6.jp
PING flets-v6.jp(2001:c90:ff:1::1) 56 data bytes
64 bytes from 2001:c90:ff:1::1: icmp_seq=1 ttl=52 time=5.05 ms
64 bytes from 2001:c90:ff:1::1: icmp_seq=2 ttl=52 time=4.55 ms

--- flets-v6.jp ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 4.554/4.803/5.053/0.258 ms
</pre>
<p>
おそらく「ブロードバンド映像サービス」を提供するサーバへの IPv6 通信も
許可されているのだろう。
つまり、
「ＢフレッツにＩＰｖ６映像視聴等機能を標準装備」
という NTT東日本の発表のココロは、
IPv6 映像サービスへの IPv6 通信＊のみ＊許可するということであって、
それ以外の IPv6 通信は対象外ということのようだ。
</p>
<p>
<a href="http://www.ntt-east.co.jp/release/0712/071226a_2.html">「フレッツ・ドットネット」サービスの概要</a>には、
「フレッツ・ドットネットサービス機能一覧」として、
</p>
<blockquote>
・FdNネームが1つ利用できます。<br />
・FdNディスク(100MB)で、ファイル共有が利用できます。<br />
・FdNディスク(100MB)には、最大10のグループメンバーを登録できます。<br />
※NTT東日本が無料で提供する専用ソフトウェア「FLET'S.Netメッセンジャーにより、
ファイル転送やビデオチャットが利用できます。
</blockquote>
<p>
が列挙されているのみであって、
IPv6 通信の許可/不許可について言及していないのは、
とてもミスリーディングな記述だと思う。
</p>
<p>
慌てて再度フレッツ・ドットネット契約を
(<a href="http://www.flets/">フレッツ・スクウェア</a>で) 申込むと、
10分ほどで再び IPv6 通信ができるようになった:
</p>
<pre class="terminal">
senri % ping6 -n router.flets.gcd.org
PING router.flets.gcd.org(2001:c90:XXXX:XXXX:2d0:2bff:fe30:b91a) 56 data bytes
64 bytes from 2001:c90:XXXX:XXXX:2d0:2bff:fe30:b91a: icmp_seq=1 ttl=64 time=3.11 ms
64 bytes from 2001:c90:XXXX:XXXX:2d0:2bff:fe30:b91a: icmp_seq=2 ttl=64 time=0.920 ms

--- router.flets.gcd.org ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.920/2.019/3.118/1.099 ms
</pre>
<p>
フレッツ網を経由した IPv6 通信を利用しているかたは、
たとえフレッツ・ドットネットサービスを利用していなくても、
フレッツ・ドットネットを解約すべきではないので、
ご注意のほどを！
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2008/03/152/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

