2008年01月
p0f は、 通信相手の OS を受動的に特定するツールで、 迷惑メール送信などの スパム行為を行なう「敵」を知る手段として有用である。 例えば、もし (あくまで仮定の話だが) 受信するメールのほとんどすべてが Linux や FreeBSD などの UNIX 系サーバから送信されるメールであって、 Windows マシンから送られてきたメールのほとんどすべてが迷惑メールであったなら、 Windows マシンからのメールを排除するという対策は合理的なものとなるだろう。
もちろん、Windows を使ってマトモなメールを送ってくるケースもあるだろうから、 Windows から送られたメールを全て排除するのは現実的ではないが、 p0f での判定結果と、 その他の手段 (例えば送信元 IP アドレス) での判定結果を組合わせて 迷惑メールであるか否かの判断を行なえば、 より精度の高い迷惑メール排除が可能になる。
ところが、 p0f は通信相手から送られてくる IP パケットを元に、 通信相手の OS を特定するツールであるから、 間にファイアウォールや NAT (IPアドレス・ポート変換) を行なう機器があると、 通信相手ではなくファイアウォールや NAT について調べてしまう。 だから、メールを受信するサーバがファイアウォールの内側にある場合は、 意味ある結果が得られないし、 外側にある場合だとメールを受信するサーバとは別の場所 (つまりファイアウォールの内側) で スパム判定を行ないたくなるものだろう。 例えばメールサーバは DMZ 上にあるが、 迷惑メール判定は LAN 内のマシンで行ないたい場合など。
私の個人サイト GCD は、 b フレッツに PPPoE 接続している。 p0f は調べる通信のインタフェース名を -i オプションで指定する必要があるが、 (1) PPPoE だからインタフェース名 (ppp0〜) が変わることがある。 また、 PPPoE を行なうゲートウェイマシンは二台ある (冗長構成) ので、 (2) アクティブ側で p0f を実行しないと意味がない。 さらに、 メールサーバは (メールボックスを一ヶ所にまとめたかったので) 一台だけであり、 (3) 異なるサーバ上 (アクティブ側のゲートウェイ) で動いている p0f の結果を メールサーバから参照しなければならない。
以上 (1) 〜 (3) の 3点を満たすための構成を考えてみた。
まず (1) と (2) は、pppd の ip-up スクリプトから p0f を実行すればよい。 例えば、ip-up で
command=$0
interface=$1
...
case $command in
*ip-up)
p0f -i $interface -Q /var/run/p0f-sock \
'port 25 and (not src net 192.168.0.0/16)' \
-u stone -d -t -o /var/log/p0f.log
;;
*ip-down)
killall p0f
;;
esac
などと p0f を起動し、ip-down で p0f を終了させる。 これでアクティブ側のゲートウェイ上でのみ p0f が動く。
p0f による判定結果は、 p0f の -Q オプションで指定した UNIX ドメイン・ソケット (上記の例では、 /var/run/p0f-sock) を介して 問合わせることができるが、 UNIX ドメイン・ソケットなので当然のことながら 別のマシンからは問合わせることができない。 そこで stone に転送させる:
stone /var/run/p0f-sock 12345 &
アクティブ側のゲートウェイは、 仮想ルータの IP アドレス 192.168.1.1 を持っているので、 「192.168.1.1:12345」へアクセスすれば、 それを stone が /var/run/p0f-sock へ中継してくれるので、 (3) p0f の結果を参照できる。
p0f の結果を参照するサンプルプログラムとして、 p0f には perl で書かれた p0fq.pl と、 C で書かれた p0fq.c が付属しているが、 あいにくどちらも UNIX ドメイン・ソケットにしか対応していない (当たり前)。 ちょっといじってリモート上の p0f へ (stone 経由で) アクセスできるようにしてみる。
p0fq.pl へのパッチ:
--- test/p0fq.pl.org 2006-08-21 23:11:10.000000000 +0900
+++ test/p0fq.pl 2008-01-31 08:00:14.652880068 +0900
@@ -30,8 +30,14 @@
$src->intip(), $dst->intip(), $ARGV[2], $ARGV[4]);
# Open the connection to p0f
-my $sock = new IO::Socket::UNIX (Peer => $ARGV[0],
+my $sock;
+if ($ARGV[0] =~ /^[\-\w]+:\d+$/) {
+ $sock = new IO::Socket::INET (PeerAddr => $ARGV[0],
Type => SOCK_STREAM);
+} else {
+ $sock = new IO::Socket::UNIX (Peer => $ARGV[0],
+ Type => SOCK_STREAM);
+}
die "Could not create socket: $!\n" unless $sock;
# Ask p0f
「IO::Socket::UNIX」を「IO::Socket::INET」に変更するだけで済む。
p0fq.c へのパッチ:
--- test/p0fq.c.org 2006-08-21 21:29:49.000000000 +0900
+++ test/p0fq.c 2008-01-31 08:05:55.499326450 +0900
@@ -16,6 +16,7 @@
#include <sys/types.h>
#include <sys/socket.h>
+#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -40,6 +41,7 @@
struct p0f_response r;
_u32 s,d,sp,dp;
_s32 sock;
+ char *str;
if (argc != 6) {
debug("Usage: %s p0f_socket src_ip src_port dst_ip dst_port\n",
@@ -55,12 +57,37 @@
if (!sp || !dp || s == INADDR_NONE || d == INADDR_NONE)
fatal("Bad IP/port values.\n");
+ if ((str=strchr(argv[1], ':'))) {
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint;
+ int err;
+ *str++ = '\0';
+ hint.ai_flags = 0;
+ hint.ai_family = AF_INET;
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_TCP;
+ hint.ai_addrlen = 0;
+ hint.ai_addr = NULL;
+ hint.ai_canonname = NULL;
+ hint.ai_next = NULL;
+ err = getaddrinfo(argv[1], str, &hint, &ai);
+ if (err) {
+ if (err == EAI_SYSTEM) pfatal("getaddrinfo");
+ else fatal("getaddrinfo(%s,%s): %s\n",
+ argv[1], str, gai_strerror(err));
+ }
+ memcpy(&x, ai->ai_addr, ai->ai_addrlen);
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ freeaddrinfo(ai);
+ if (sock < 0) pfatal("socket");
+ } else {
sock = socket(PF_UNIX,SOCK_STREAM,0);
if (sock < 0) pfatal("socket");
memset(&x,0,sizeof(x));
x.sun_family=AF_UNIX;
strncpy(x.sun_path,argv[1],63);
+ }
if (connect(sock,(struct sockaddr*)&x,sizeof(x))) pfatal(argv[1]);
getaddrinfo を呼び出すための準備に行数を費やしているので 複雑に見えるかも知れないが、 本質は プロトコル・ファミリ (protocol family) を AF_UNIX から AF_INET に変更しただけである。
(p0f を実行しているマシンとは異なるマシン上で) p0fq を実行してみる:
% p0fq 192.168.1.1:12345 81.36.137.136 2943 60.32.85.220 25 Genre : Windows Details : 2000 SP4, XP SP1+ Distance : 21 hops Link : pppoe (DSL)
上記は、 メール送信元 (81.36.137.136 のポート 2943番) が GCD の MX (60.32.85.220 のポート 25番) へメールを送ってきた通信の p0f による判定結果。 メールサーバで、 メールヘッダにメール送信元のポート番号も出力するようにしておけば、 メールを受信するユーザが自前のメール振り分けプログラム (procmail など) を使って p0f の判定結果を参照できる点がミソ。
81.36.137.136 はスペインのプロバイダの IP アドレスらしいが、 逆引きしてみると 136.Red-81-36-137.dynamicIP.rima-tde.net となるので 動的に割当てられているアドレスなのだろう。 これは p0f の結果に pppoe (DSL) と出ていることと符合する。 そして Windows 2000 SP4 か Windows XP SP1 以降を使って 送信していることが分かる。
「1/9 以降、Windows VISTA 搭載 レッツノートのハードディスクが突然死する可能性がある」問題を解決するための BIOS が公開された。
レッツノート (1/21):
本BIOSの導入によって現象を回避することが可能ですが、 詳細が判明次第、弊社Webサイトにてご報告いたしますので、 引き続き弊社Webサイトからお知らせを覧いただきますようお願いします。
ただし、本現象がすでに発生している場合は、 対策のBIOSアップデートプログラムを導入できません。
大変お手数ですが、下記の修理相談窓口に修理のご相談をお願いします。
Lenovo ThinkPad (1/15):
注意:Windows Vistaを使用しているシステムで、 電源投入時に"2100: Initialization error on HDD0 (Main hard disk drive)"と 表示され、 HDDから起動できなくなることがある問題を修正しました。
レッツノートの BIOS アップデートは Windows 上から実行する必要があるので、 「本現象がすでに発生している場合は」、 まず「Windows VISTA 用の更新プログラム KB943899 が原因で突然死したハードディスクを復旧させる方法」などを用いてハードディスクをスピン・アップさせ、 Windows を正常起動させておく必要がある。 「修理相談窓口に修理のご相談を」するのは「大変お手数」なので、 ハードディスク以外の方法 (USB メモリからの起動など) で 起動する手段を提供すべきだと思うのだが...
さっそくレッツノートで BIOS アップデートを行なってみた。
試しに、 ハードディスクを故意に Power-Up In Standby 状態にしてみる。 すなわち、 「Windows VISTA 用の更新プログラム KB943899 が原因で突然死したハードディスクを復旧させる方法」 の 「お手軽パック」などを使って Linux を起動し、 hdparm コマンドを使って Power-Up In Standby 状態にしてみる:
# mount -t vfat /dev/sdb1 /mnt # /mnt/hdparm -s1 /dev/sda /dev/sda: Use of -s1 is VERY DANGEROUS. This requires BIOS and kernel support to recognize/boot the drive. Please supply the --yes-i-know-what-i-am-doing flag if you really want this Program aborted #
おお、fool proof 機能付とは、なかなか親切なコマンドだ。 とても危険なので、 「何をしようとしているか分かっている」場合のみ、 「--yes-i-know-what-i-am-doing」オプションを付けて再実行する:
# /mnt/hdparm -s1 --yes-i-know-what-i-am-doing /dev/sda
/dev/sda:
setting power-up in standby to 1 (on)
# /mnt/hdparm -I /dev/sda | grep Power-Up
* Power-Up In Standby feature set
#
これで、ハードディスクが Power-Up In Standby 状態になった。 つまり電源投入時にスタンバイ・モードに入り、 明示的に spin up 命令を送らない限りは回転を始めない。 だから、 レッツノートの以前の BIOS ではハードディスクを認識できず起動に失敗していた。
Ctrl-Alt-Del を押して再起動すると、 ハードディスクから正常にブートした。 再び「お手軽パック」を使って Linux を起動し、 ハードディスクの状態を確認してみる:
# /mnt/hdparm -I /dev/sda | grep Power-Up
Power-Up In Standby feature set
#
Power-Up In Standby 機能が無効になっていた。 新しい BIOS は、 ハードディスクに spin up 命令を送るだけでなく、 Power-Up In Standby 機能を無効にする命令も送っているようだ。
昨日書いた
「1/9 以降、Windows VISTA 搭載 レッツノートのハードディスクが突然死する可能性がある
」を大変多くの方に読んで頂けた。
頂いたコメントで目立ったのが、
復旧方法が万人向けではない、という点。
確かに Linux 2.6.22 以上の LiveCD を自前で調達しなければならないというのは、
よほど普段から Linux を使いこなしていない限りは難しいだろう。
というわけで、 突然死したハードディスクを復旧させる「お手軽パック」を作ってみた。 もちろん無保証である。 いかなる損害が発生しても私は何の責任も負えない、 ということに同意して頂けるかたのみに対して使用を許諾する。
まず、この zip アーカイブ をダウンロードして、 USB メモリへ展開する。 以下の例では USB メモリが「ドライブ D」になっているが、 実際の USB メモリのドライブ名で読み替えて欲しい。
D:\>dir
ドライブ D のボリューム ラベルがありません。
ボリューム シリアル番号は 0000-0000 です
D:\ のディレクトリ
2007/07/16 08:49 23,040 syslinux.exe
2008/01/18 07:33 537,844 hdparm
2007/12/25 08:07 2,411,333 initz
2007/12/24 09:50 1,423,768 linuz
2008/01/18 08:18 58 syslinux.cfg
5 個のファイル 4,396,043 バイト
0 個のディレクトリ 510,672,896 バイトの空き領域
D:\>
Windows のコマンド プロンプト にて、 D:\syslinux.exe を以下のように実行する。 もちろん、引数の「D:」は USB メモリのドライブ名で読み替える。
D:\>syslinux.exe -ma D: D:\>
これで Linux 2.6.23.12 がブート可能な USB メモリができた。 このメモリをレッツノートに差して起動する。 BIOS 設定で USB から起動可能にしておくのを忘れずに。
ハードディスクが回転しないのであるから、 以下のメッセージが表示されて止まってしまうが、
Phoenix TrustedCore(tm) NB Copyright 1985-2004 Phoenix Technologies Ltd. All Rights Reserved Copyright (C) Matsushita Electric Industrial Co.,Ltd. 2007 BIOS Version 1.00-L13 CPU = 1 Processors Detected, Cores per Processor = 2 Intel(R) Core(TM) Duo CPU U2400 @ 1.06GHz 2048MシステムRAMテスト完了。 システムBIOSがシャドウされました。 ビデオBIOSがシャドウされました。 ハードディスク0: マウスが初期化されました。 エラー 0200: ハードディスクエラーです。 0 <F2>キーを押すとセットアップを起動します。
ここで構わず <F1>キーを押すと、 USB メモリからの起動が始まる。
linux が起動すると /bin/sh が実行されてプロンプト「#」が表示されるが、 その後 USB メモリが認識されてカーネル・ログが出力される。
... PWD='/' ROOTPARM=' -o ro' TERM='linux' initrd='initz' /bin/sh: can't access tty; job control turned off # [ 31.556958] scsi 6:0:0:0: Direct-Access Multi Flash Reader 1.00 PQ: 0 ANSI: 0 [ 32.077952] sd 6:0:0:0: [sdb] 1006592 512-byte hardware sectors (515 MB) [ 32.079952] sd 6:0:0:0: [sdb] Write Protect is off [ 32.080089] sd 6:0:0:0: [sdb] Mode Sense: 03 00 00 00 [ 32.080094] sd 6:0:0:0: [sdb] Assuming drive cache: write through [ 32.085449] sd 6:0:0:0: [sdb] 1006592 512-byte hardware sectors (515 MB) [ 32.087574] sd 6:0:0:0: [sdb] Write Protect is off [ 32.087699] sd 6:0:0:0: [sdb] Mode Sense: 03 00 00 00 [ 32.087704] sd 6:0:0:0: [sdb] Assuming drive cache: write through [ 32.087830] sdb: sdb1 [ 32.090803] sd 6:0:0:0: [sdb] Attached SCSI removable disk [ 32.091527] usb-storage: device scan complete
プロンプトの後にカーネル・ログが出力されてしまっているため紛らわしいが、 Enter キーを押せばプロンプトが表示される。
このカーネル・ログでは [sdb] と表示されているが、 レッツノートに接続した USB デバイスが他にもあれば、 sdc や sdd になっているかもしれない。 その場合は、以下の sdb を sdc なり sdd なりで読み替えて欲しい。
以下のように mount コマンドを実行して、 USB メモリを /mnt へマウントする。 そして USB メモリ上の hdparm コマンドを実行してみる:
# mount -t vfat /dev/sdb1 /mnt
# /mnt/hdparm -i /dev/sda
/dev/sda:
Model=Hitachi HTS541612J9SA00 , FwRev=SBDOC70P, SerialNo= SBXXXXXXXXXXXX
Config={ HardSect NotMFM HdSw>15uSec Fixed DTR>10Mbs }
RawCHS=16383/16/63, TrkSize=0, SectSize=0, ECCbytes=4
BuffType=DualPortCache, BuffSize=7516kB, MaxMultSect=16, MultSect=?16?
CurCHS=16383/16/63, CurSects=16514064, LBA=yes, LBAsects=234441648
IORDY=on/off, tPIO={min:120,w/IORDY:120}, tDMA={min:120,rec:120}
PIO modes: pio0 pio1 pio2 pio3 pio4
DMA modes: mdma0 mdma1 mdma2
UDMA modes: udma0 udma1 udma2 udma3 udma4 *udma5
AdvancedPM=yes: mode=0x80 (128) WriteCache=enabled
Drive conforms to: ATA/ATAPI-7 T13 1532D revision 1: ATA/ATAPI-2,3,4,5,6,7
* signifies the current active mode
#
以上のように、 レッツノートのハードディスク (/dev/sda) の諸元が表示されれば成功である。 以下のように hdparm を実行して「Power-Up In Standby feature」を無効にすれば、 ハードディスクの復旧が完了する。
# /mnt/hdparm -s0 /dev/sda /dev/sda: spin-up: setting power-up in standby to 0 (off) #
後は、Ctrl-Alt-Del を押すなどして再起動すれば、 ハードディスクから正常にブートする。 USB メモリを抜くのを忘れずに。
先日 1/10 (運悪く出張初日だった *_*) に、 レッツノート CF-R6MWVAJP のハードディスク・ドライブ (以下、HDD と略記) が 回転しなくなり (スピン・アップしない) 往生した。 電源を入れても、 BIOS が
Phoenix TrustedCore(tm) NB Copyright 1985-2004 Phoenix Technologies Ltd. All Rights Reserved Copyright (C) Matsushita Electric Industrial Co.,Ltd. 2007 BIOS Version 1.00-L13 CPU = 1 Processors Detected, Cores per Processor = 2 Intel(R) Core(TM) Duo CPU U2400 @ 1.06GHz 2048MシステムRAMテスト完了。 システムBIOSがシャドウされました。 ビデオBIOSがシャドウされました。 ハードディスク0: マウスが初期化されました。 エラー 0200: ハードディスクエラーです。 0 <F2>キーを押すとセットアップを起動します。
というメッセージをビープ音とともに出力して止まってしまう。 HDD が壊れたのかと思い、 BIOS で HDD を起動ドライブから外して USB メモリからブートを試みたが、 起動ドライブから外しても内蔵 HDD に問題があると先に進めないようだ。
HDD を完全に取り除いてしまえば USB メモリからブートするらしいが、 このような中途半端な死にかただとスピン・アップ待ちになってしまって、 そこから先に進めなくなる (後で知ったのだが、 実は上記画面で止まっているとき <F1> を押すと、 ブートを継続できて USB メモリ等からブートできる)。
昔、HDD のスピンドル (spindle) が固着して、 スピン・アップ (spin up) しなくなる症状に見舞われたことがあったが、 今回はつい数分前まで全く正常に動いていた HDD が、 突然スピン・アップしなくなったのであって、 あからさまに症状が異なるように思われた。 しかもごく短い距離を移動するためにレッツ・ノートを閉じただけなので、 途中衝撃なども一切与えていない。 機械的な障害ではないように思われた。
とはいえ、 いちおー無駄な抵抗は試みた (^^;)。 すなわち、 HDD が回転しなくなったときに、 HDD を振り回すことによってトルクを与える手法や、 あるいは結露等で回路に異常がおきたときに、 結露を霧散させる手法 (平たく言うとしばらく放置しただけ ;) を試したのだが、 HDD が回転音を発することは無かった。 出張初日で、 代替マシンの調達もままならず、 Advanced/W-ZERO3[es] に USB キーボードをつないで急場をしのいだものの、 えらく難儀した。
結論から言うと、 想像通りハードウェア的にはなんら故障していなかった。 単に HDD が 「電源投入時にスタンバイ・モードに入る (power-on in standby)」 状態になっていただけだった。
1/9 に行なわれた Windows Update KB943899 が原因らしいです。 おそらくこの Update のバグで、 HDD に power-on in standby を有効にする命令が 送られてしまうことがあるのでしょう。 私の CF-R6 は 1/10 に発症しましたが、 他にも同様の発症例が多数あるとか。
例えば、 Linux の hdparm コマンドのマニュアルには、 次のような記述がある。
NAME
hdparm - get/set hard disk parameters
SYNOPSIS
hdparm [ flags ] [device] ..
OPTIONS
-s Enable/disable the power-on in standby feature, if supported by
the drive. VERY DANGEROUS. Do not use unless you are abso-
lutely certain that both the system BIOS (or firmware) and the
operating system kernel (Linux >= 2.6.22) support probing for
drives that use this feature. When enabled, the drive is pow-
ered-up in the standby mode to allow the controller to sequence
the spin-up of devices, reducing the instantaneous current draw
burden when many drives share a power supply.
「VERY DANGEROUS」と書いてある通り、 HDD がひとたびこの「power-on in standby」モードになってしまうと、 電源を投入してもスピン・アップしなくなる (スタンバイ状態になる)。 HDD が沢山あるサーバなどで、 全ディスクが一斉に回転を始めると、 突入電流が電源の許容範囲を超えてしまったりするわけで、 それを回避するためのモードらしい。 つまり、 電源を投入したあと、 おもむろに HDD を一台づつスピン・アップしていくことにより、 回転開始にともなう突入電流を分散させることができるというわけ。
実験してみる (非常に危険なので何をやっているか完全に理解するまでは 真似しないでください):
# hdparm -s1 /dev/sda
/dev/sda:
setting power-up in standby to 1 (on)
# hdparm -I /dev/sda
/dev/sda:
ATA device, with non-removable media
Model Number: Hitachi HTS541612J9SA00
Serial Number: SBXXXXXXXXXXXX
Firmware Revision: SBDOC70P
Standards:
Used: ATA/ATAPI-7 T13 1532D revision 1
Supported: 7 6 5 4
Configuration:
Logical max current
cylinders 16383 16383
heads 16 16
sectors/track 63 63
--
CHS current addressable sectors: 16514064
LBA user addressable sectors: 234441648
LBA48 user addressable sectors: 234441648
device size with M = 1024*1024: 114473 MBytes
device size with M = 1000*1000: 120034 MBytes (120 GB)
Capabilities:
LBA, IORDY(can be disabled)
Queue depth: 32
Standby timer values: spec'd by Vendor, no device specific minimum
R/W multiple sector transfer: Max = 16 Current = 16
Advanced power management level: 128 (0x80)
Recommended acoustic management value: 128, current value: 254
DMA: mdma0 mdma1 mdma2 udma0 udma1 udma2 udma3 udma4 *udma5
Cycle time: min=120ns recommended=120ns
PIO: pio0 pio1 pio2 pio3 pio4
Cycle time: no flow control=120ns IORDY flow control=120ns
Commands/features:
Enabled Supported:
* SMART feature set
* Security Mode feature set
* Power Management feature set
* Write cache
* Look-ahead
* Host Protected Area feature set
* WRITE_BUFFER command
* READ_BUFFER command
* NOP cmd
* DOWNLOAD_MICROCODE
* Advanced Power Management feature set
* Power-Up In Standby feature set
* SET_FEATURES required to spinup after power up
SET_MAX security extension
Automatic Acoustic Management feature set
* 48-bit Address feature set
* Device Configuration Overlay feature set
* Mandatory FLUSH_CACHE
* FLUSH_CACHE_EXT
* SMART error logging
* SMART self-test
* General Purpose Logging feature set
* WRITE_{DMA|MULTIPLE}_FUA_EXT
* 64-bit World wide name
* IDLE_IMMEDIATE with UNLOAD
* SATA-I signaling speed (1.5Gb/s)
* Native Command Queueing (NCQ)
* Host-initiated interface power management
* Phy event counters
Non-Zero buffer offsets in DMA Setup FIS
DMA Setup Auto-Activate optimization
Device-initiated interface power management
In-order data delivery
* Software settings preservation
Security:
Master password revision code = 2007
supported
enabled
not locked
frozen
not expired: security count
not supported: enhanced erase
Security level maximum
72min for SECURITY ERASE UNIT.
Checksum: correct
#
「Power-Up In Standby feature set」が有効になっていることが分かる。 この状態で再起動する (HDD の電源をいったん切る) と、 冒頭に引用した「0200: ハードディスクエラーです。 0」を表示して止まってしまう。 <F1> を押して HDD 以外から起動させると、
[ 0.000000] Linux version 2.6.23.12 (sengoku@senri.gcd.org) (gcc version 4.1.2) #1 SMP Mon Dec 24 09:50:41 JST 2007 ... [ 72.479025] ata3.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x0 [ 72.479031] ata3.00: irq_stat 0x40000001 [ 72.479044] ata3.00: cmd c8/00:08:00:00:00/00:00:00:00:00/e0 tag 0 cdb 0x0 data 4096 in [ 72.479047] res 51/04:08:00:00:00/00:00:00:00:00/e0 Emask 0x1 (device error) [ 72.481033] ata3.00: configured for UDMA/33 [ 72.481043] sd 2:0:0:0: [sda] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE,SUGGEST_OK [ 72.481051] sd 2:0:0:0: [sda] Sense Key : Aborted Command [current] [descriptor] [ 72.481060] Descriptor sense data with sense descriptors (in hex): [ 72.481066] 72 0b 00 00 00 00 00 0c 00 0a 80 00 00 00 00 00 [ 72.481082] 00 00 00 00 [ 72.481089] sd 2:0:0:0: [sda] Add. Sense: No additional sense information [ 72.481099] end_request: I/O error, dev sda, sector 0 [ 72.481114] ata3: EH complete [ 72.481738] sd 2:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA [ 72.481794] sd 2:0:0:0: [sda] 234441648 512-byte hardware sectors (120034 MB) [ 72.481818] sd 2:0:0:0: [sda] Write Protect is off [ 72.481824] sd 2:0:0:0: [sda] Mode Sense: 00 3a 00 00 [ 72.481861] sd 2:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
何度も HDD へリクエストを再送した末、「I/O error」になる。 Linux 2.6.22 以降の場合、 I/O error にはなるものの HDD デバイス自体は認識しているので、 hdparm コマンドからコントロールすることができる。
逆に言うと、古いカーネルだとデバイス認識も行なわれないので、 hdparm コマンドすら使えなくなってしまい 以下の方法では復旧できない。 現時点では Knoppix などの LiveCD の多くは、 2.6.21 以前のカーネルのまま (例えば Knoppix v5.1.1 は 2.6.19) なので注意。
hdparm を使って「Power-Up In Standby feature」を無効にする:
# hdparm -I /dev/sda | grep Power-Up * Power-Up In Standby feature set # hdparm -s0 /dev/sda /dev/sda: setting power-up in standby to 0 (off) # hdparm -I /dev/sda | grep Power-Up Power-Up In Standby feature set #
これで、電源投入時に HDD は自動的に回転を始めるようになる。
再起動すれば HDD から正常にブートする。
妻が、ガシャポン (ガチャガチャ) で サウンドロップを買ってきた。
なんでも、最初に出てきたのが「ヒート・ロッド電流音」だったそうで、 「効果音」ではない「セリフ」バージョン (アムロの声とか) が欲しくて、 ついつい二個三個と買ってしまったらしい。 馬鹿だねぇ...
で、サウンドロップを改造するページを見つけてきて、 同じように改造してくれという。
ネジを外してバラすと、 基板上隅に S1 S2 S3 S4 とシルク印刷したパターンがある。 このパターンのいずれかをショートすると対応する音声を選択できるらしい。 右下隅に「P36C」とシルク印刷してあるが、 これは基板のID と思われる。 「P36」というのが「〜青い巨星編〜」シリーズを意味していて、 末尾の「C」がシリーズに含まれる各バリエーションを表す記号なのだろう。
上の写真の赤枠で囲った部分を拡大:
S2 がショートしてある。
S2 の半田を取り除いて S1 をショートしてみると、
ヒート・ロッド電流音がセイラさんの「あなたなら出来るわ」に変わった。
ショートするパターンと音声をまとめると次のようになる。
| P36A | S? | 1 | ランバ・ラルA | ザクとは違うのだよ! ザクとは! |
| S? | 2 | ランバ・ラルB | いい目をしているな | |
| P36B | S1 | 5 | アムロ・レイB | 親父にもぶたれたことないのに! |
| S2 | 4 | アムロ・レイA | おだてないでください | |
| P36C | S1 | 3 | セイラ・マス | あなたなら出来るわ |
| S2 | 6 | ヒート・ロッド電流音 | シュビンッ! ビビビビ… | |
| P36D | S1 | 7 | ビーム・サーベルを抜いた音 | ブォンッ! |
| S2 | 8 | ビーム・サーベルで斬り付けた音〜爆発音 | ズギャン! ビゴゴゴ… ドォォン! |
あけましておめでとうございます。 今年もよろしくお願いします。
La Fonera (La Fonera+) のブート・ローダ RedBoot は、 TFTP サーバに置いたカーネルを load して起動することができる。 したがって、 TFTP サーバから load したカーネルで root ファイル・システムを NFS マウントすれば、 フラッシュ・メモリを使わずにブートすること (ディスクレス・ブート、というか flashless boot) ができる。
La Fonera のようなルータをディスクレスにして 何が嬉しいのかと思う人もいるかもしれないが、 ルータのように常時ネットワークにつなぐものだからこそ、 ネットワークにつないでいないと使えないという ディスクレス最大の欠点が欠点にならないわけで、 むしろルータこそ積極的にディスクレスにすべきではなかろうか。
ディスクレス化することによるメリットは数多い。 なんといっても最大のメリットは、 フラッシュ・メモリを書き換えること無く、 手軽にファームウェアを入れ替えられる (OpenWrt や DD-WRT など) こと。 フラッシュ・メモリを書き換える時間が節約できるし、 書き換え回数を気にする必要もなくなるわけで、 心置き無くファームウェアのデバッグを行なえる。 また、 NFS を使えばフラッシュ・メモリの容量をはるかに超えるディスク領域を使えるので、 セルフ・コンパイル環境の構築なども容易に行なうことができる。
試しに、 La Fonera のフラッシュ・メモリを書き換えること無く DD-WRT をブートさせてみる。
まず、 DD-WRT の root ファイル・システムを NFS サーバ上に展開する。 ここでは例として /nfs/dd-wrt に展開した。 /usr/sbin/udhcpc の実行を抑制するため (理由は後述) に、 /nfs/dd-wrt/usr/sbin/udhcpc を削除しておく。
senri:/tmp # wget "http://www.dd-wrt.com/dd-wrtv2/downloads/release candidates/DD-WRT v24 RC6.2/Atheros WiSoc/Fonera/root.fs"
--09:22:37-- http://www.dd-wrt.com/dd-wrtv2/downloads/release%20candidates/DD-WRT%20v24%20RC6.2/Atheros%20WiSoc/Fonera/root.fs
=> `root.fs'
...
09:22:46 (335.56 KB/s) - `root.fs' saved [2834432/2834432]
senri:/tmp # unsquashfs.dd-wrt -dest /nfs/dd-wrt root.fs
Reading a different endian SQUASHFS filesystem on root.fs
created 347 files
created 61 directories
created 159 symlinks
created 0 devices
created 0 fifos
senri:/tmp # rm /nfs/dd-wrt/usr/sbin/udhcpc
次に DD-WRT のカーネルを改変して、 root ファイル・システムを NFS マウントできるようにする。 DD-WRT のソースを取得:
senri:/usr/local/src % svn co svn://svn.dd-wrt.com/DD-WRT A DD-WRT/tools A DD-WRT/tools/addpattern_gs A DD-WRT/tools/uemf.h ...
La Fonera 用のカーネルは、
DD-WRT/src/linux/ar531x/linux-2.6.23 にある。
SVN revision 8745 の場合は Linux 2.6.23.12 ベース。
ビルド用ディレクトリ /usr/local/src/linux-2.6.23.12-dd-wrt を作って、
DD-WRT のデフォルトのカーネル設定ファイル .config_generic をコピーし、
make menuconfig を実行:
senri:/usr/local/src % mkdir /usr/local/src/linux-2.6.23.12-dd-wrt senri:/usr/local/src % cd DD-WRT/src/linux/ar531x/linux-2.6.23 senri:/usr/local/src/DD-WRT/src/linux/ar531x/linux-2.6.23 % make O=/usr/local/src/linux-2.6.23.12-dd-wrt menuconfig ... senri:/usr/local/src/DD-WRT/src/linux/ar531x/linux-2.6.23 % cp .config_generic /usr/local/src/linux-2.6.23.12-dd-wrt/ senri:/usr/local/src/DD-WRT/src/linux/ar531x/linux-2.6.23 % cd /usr/local/src/linux-2.6.23.12-dd-wrt senri:/usr/local/src/linux-2.6.23.12-dd-wrt % make menuconfig
root ファイル・システムを NFS マウントするために必要な変更は以下の通り:
まず、 カーネルによる IP アドレス自動取得機能を有効にする:
-> Networking
-> Networking support (NET [=y])
-> Networking options
-> TCP/IP networking (INET [=y])
-> IP: kernel level autoconfiguration (IP_PNP [=y])
-> IP: DHCP support (IP_PNP_DHCP [=y])
DD-WRT のデフォルト設定では、 ネットワーク・ドライバ AR2313 がモジュールになっているので、 カーネル組み込み (built-in) に変更する:
-> Device Drivers
-> Network device support (NETDEVICES [=y])
-> Ethernet (10 or 100Mbit) (NET_ETHERNET [=y])
-> AR2313 Ethernet support (AR2313 [=y])
そして NFS サポートを有効にする:
-> File systems
-> Network File Systems
-> NFS file system support (NFS_FS [=y])
-> Provide NFSv3 client support (NFS_V3 [=y])
-> Root file system on NFS (ROOT_NFS [=y])
root ファイル・システムはカーネルがマウントするので mount コマンドは使用しないが、 La Fonera 用の DD-WRT の mount コマンド (busybox に含まれる) は、 NFS をサポートしていないので注意が必要である。 NFS サーバ上の root ディレクトリ /nfs/dd-wrt には、 NFS マウントをサポートした mount コマンドをインストールしておいたほうが便利。
RedBoot の exec コマンドでカーネル・コマンドラインを指定しても、 なぜか無視される (バグ?) ので、 カーネルのデフォルト・コマンドラインを変更しておく:
-> Kernel hacking -> Default kernel command string (CMDLINE)
カーネル・コマンドラインとして以下を設定:
console=ttyS0,115200 ip=on root=/dev/nfs nfsroot=/nfs/dd-wrt,nolock
以上まとめると、 カーネル設定ファイル .config の変更箇所は以下の通り:
CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_AR2313=y CONFIG_NFS_FS=y CONFIG_NFS_V3=y CONFIG_ROOT_NFS=y CONFIG_LOCKD=y CONFIG_LOCKD_V4=y CONFIG_NFS_COMMON=y CONFIG_SUNRPC=y CONFIG_CMDLINE="console=ttyS0,115200 ip=on root=/dev/nfs nfsroot=/nfs/dd-wrt,nolock"
make して TFTP サーバに置く:
senri:/usr/local/src/linux-2.6.23.12-dd-wrt % make make -C /usr/local/src/DD-WRT/src/linux/ar531x/linux-2.6.23 O=/usr/local/src/linux-2.6.23.12-dd-wrt GEN /usr/local/src/linux-2.6.23.12-dd-wrt/Makefile ... LD vmlinux SYSMAP System.map ... senri:/usr/local/src/linux-2.6.23.12-dd-wrt % mips-linux-uclibc-objcopy -O binary vmlinux /var/tftp/vmlinux.bin
La Fonera で読み込んでブートさせる... といいたいところだが、 DD-WRT のカーネルは、 flash RAM に「nvram」という名前の区画がないと Kernel panic を起こす:
RedBoot> load -r -b 0x80041000 vmlinux.bin Using default protocol (TFTP) Raw file loaded 0x80041000-0x8031cbb3, assumed entry at 0x80041000 RedBoot> exec Now booting linux kernel: Base address 0x80030000 Entry 0x80041000 Cmdline : Linux version 2.6.23.12 (sengoku@senri.gcd.org) (gcc version 4.1.2) #14 Wed Jan 2 13:20:19 JST 2008 ... Unhandled kernel unaligned access[#1]: Cpu 0 $ 0 : 00000000 0000006e ffffffed 00000000 ... Kernel panic - not syncing: Attempted to kill init!
flash-less boot の主旨には反するが (^^;)、 nvram 区画を作って再度ブートを試みる。
もちろん、 flash を一切参照しないファームウェアを作ることも可能ではあるが、 わずか 64k byte の flash 領域を確保するだけで、 既存のファームウェアをほとんどそのまま流用できるのであれば、 費用対効果の高い flash の使い方と言えるのではないだろうか。
nvram 区画の内容は「空っぽ」でも構わないのであるが、 ついでなので他の DD-WRT マシンで設定した nvram (nvram.bin) をコピーすれば、 設定の手間が省ける。
DD-WRT のデフォルト設定では、 ath0 (無線LAN) と eth0 (WAN) がブリッジとして動作する設定になっていて、 ブート途中で一時的に eth0 を停止する (ifconfig eth0 down あるいは ifconfig eth0 0.0.0.0)。 当然、eth0 を止めた瞬間に NFS サーバと通信できなくなり、 「nfs: server not responding, still trying」という ログを出し続けてハングしてしまう。 したがって、 eth0 を止めない設定にしておく必要がある。 例えば、 NFS サーバ上の root ディレクトリから /usr/sbin/udhcpc を削除した上で、 「WAN Connection Type」を DHCP に設定する。
ついで^2 に、 La Fonera+ には、 「image2」という名前の普段は使われていない区画がある (「image」区画が壊れた場合の非常用) ので削除しておく (fis delete image2)。
RedBoot> fis delete image2 Delete image 'image2' - continue (y/n)? y ... Erase from 0xa8660000-0xa87a0000: .................... ... Erase from 0xa87e0000-0xa87f0000: . ... Program from 0x80ff0000-0x81000000 at 0xa87e0000: . RedBoot> load -r -b 0x80041000 nvram.bin Using default protocol (TFTP) Raw file loaded 0x80041000-0x80050fff, assumed entry at 0x80041000 RedBoot> fis create -b 0x80041000 -f 0xA87D0000 -l 0x00010000 nvram ... Erase from 0xa87d0000-0xa87e0000: . ... Program from 0x80041000-0x80051000 at 0xa87d0000: . ... Erase from 0xa87e0000-0xa87f0000: . ... Program from 0x80ff0000-0x81000000 at 0xa87e0000: . RedBoot> fis list Name FLASH addr Mem addr Length Entry point RedBoot 0xA8000000 0xA8000000 0x00030000 0x00000000 loader 0xA8030000 0x80100000 0x00010000 0x80100000 image 0xA8040000 0x80040400 0x00240230 0x80040400 nvram 0xA87D0000 0xA87D0000 0x00010000 0x80041000 FIS directory 0xA87E0000 0xA87E0000 0x0000F000 0x00000000 RedBoot config 0xA87EF000 0xA87EF000 0x00001000 0x00000000
flash がこの状態でも、 La Fonera+ のオリジナル・ファームウェアは正常に起動する。 また、 ここでは La Fonera+ を使用しているが、 La Fonera でも同様に nvram 区画を作ればよい。
より正確に言うと、 区画の個数が同じ (/dev/mtd9 が board_config) でないと、 オリジナル・ファームウェアでは fonsmcd が異常終了してしまって、 FON ルータが接続されていないと FON にみなされてしまう。 image2 を削除して nvram を追加すれば区画の個数は変わらないので、 fonsmcd を正常に動かすことができる。
ついでに言うと、 区画の個数が変化してしまっても、 /usr/sbin/fonsmcd ファイル中の「/dev/mtd9ro」という部分を変更すれば、 この問題は回避できる。 MTD パーティションの番号をハードコーディングしてしまっているのは、 この部分だけだと思われる。
再度ブート:
RedBoot> load -r -b 0x80041000 vmlinux.bin
Using default protocol (TFTP)
Raw file loaded 0x80041000-0x8031cbb3, assumed entry at 0x80041000
RedBoot> exec
Now booting linux kernel:
Base address 0x80030000 Entry 0x80041000
Cmdline :
Linux version 2.6.23.12 (sengoku@senri.gcd.org) (gcc version 4.1.2) #14 Wed Jan 2 13:20:19 JST 2008
...
IP-Config: Got DHCP answer from 172.16.255.254, my address is 172.16.191.230
IP-Config: Complete:
device=eth0, addr=172.16.191.230, mask=255.255.0.0, gw=172.16.255.254,
host=172.16.191.230, domain=, nis-domain=(none),
bootserver=172.16.255.254, rootserver=172.16.255.254, rootpath=
Looking up port of RPC 100003/2 on 172.16.255.254
Looking up port of RPC 100005/1 on 172.16.255.254
VFS: Mounted root (nfs filesystem) readonly.
Mounted devfs on /dev
Freeing unused kernel memory: 140k freed
...
DD-WRT v24 std (c) 2008 NewMedia-NET GmbH
Release: 01/02/08 (SVN revision: 8743)
DD-WRT login: root
Password: XXXXXXXX
==========================================================
____ ___ __ ______ _____ ____ _ _
| _ \| _ \ \ \ / / _ \_ _| __ _|___ \| || |
|| | || ||____\ \ /\ / /| |_) || | \ \ / / __) | || |
||_| ||_||_____\ V V / | _ < | | \ V / / __/|__ _|
|___/|___/ \_/\_/ |_| \_\|_| \_/ |_____| |_|
DD-WRT v24
http://www.dd-wrt.com
==========================================================
BusyBox v1.4.2 (2008-01-02 03:34:20 CET) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
root@DD-WRT:~# cat /proc/mounts
rootfs / rootfs rw 0 0
/dev/root / nfs ro,vers=2,rsize=4096,wsize=4096,hard,nolock,proto=udp,timeo=10,retrans=2,sec=sys,addr=172.16.255.254 0 0
none /dev devfs rw 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
ramfs /tmp ramfs rw 0 0
devpts /dev/pts devpts rw 0 0
root@DD-WRT:~#
NFS サーバに置いた DD-WRT の root ディレクトリ /nfs/dd-wrt が、 無事 / にマウントできている。