FreeBSDでRealtekなethernetインターフェースはWOLに対応しているので、mrubyからたたき起こしてみました。
ターゲットはASRock DeskMini A300で、BIOSの設定はネットで調べてみました。
mrubyはmrbgemsのmruby-socketを入れてビルドしておきます。
s = UDPSocket.new
s.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, 1)
mac = "12:34:56:78:9a:bc"
mes = ['FF'].pack('H2') * 6
sp = mac.split(':')
mgc = [sp[0]].pack('H2')
mgc << [sp[1]].pack('H2')
mgc << [sp[2]].pack('H2')
mgc << [sp[3]].pack('H2')
mgc << [sp[4]].pack('H2')
mgc << [sp[5]].pack('H2')
mes << mgc * 16
s.send(mes, 0, '192.168.1.255', 9)
macはターゲットのMACアドレスにします。192.168.1.255は自分のネットワークのブロードキャストアドレスにします。
FreeBSDは12.1でとくに設定は必要なかったです。インターフェースはこんなになってました。
re0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
options=8209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGC,LINKSTATE>
ether 12:34:56:78:9a:bc
inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255
media: Ethernet autoselect (1000baseT <full-duplex>)
status: active
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
tcpdump -e -xX ether broadcastで確認したところ、なぜか
mes << mac.split(':').pack('H2H2H2H2H2H2') * 16
がおかしな動きをしていたのでpackをばらしました。追記:修正済みです。
このターゲットは負荷がなければ13Wくらいなのですが、でんこちゃんに怒られるので、未使用時は電源落としています。
昔元祖amd64のHummerなマシンが消費電力が100W以上あってskにパッチを当ててWOLできるようにしてみたことがありました。確かレジスタにフラグを立てるだけだったような記憶があります。
mrubyはFreeBSD/mipsのルーターに入っているのですが、ルーターは3Wくらいなので常時通電しています。
shutdownするときは必ず-pで電源を落とす必要があります。単にHALTしてシングルユーザになるとコントロールできなくなります。FreeBSDやLinuxにはpoweroffというコマンドもあるので、これを使っても良いかもしれません。
WOLのパケットはEtherパケットのヘッダーがブロードキャストアドレス(0xff,0xff,0xff,0xff,0xff,0xff)と自分のMACアドレスでパケットの種別がIP(0x0800)の14バイトになります。その後IPv4パケットが入りIPヘッダーと上のmrubyで作っているデータが入ります。WOL待ちのインターフェースはブロードキャストのEtherパケットを受け取れるようになっていて、その中身が自分宛のWOLパケットのときにパワーオンします。
WOLパケット待ちのreなインターフェースは100BaseTXでLink upした状態になっています。インターフェースがLink Down(LEDが消えてる)してるとWOLはできません。Linkは接続しているスイッチのLEDでも確認できます。
いくら自分しか知らないはずといっても通信がどこかでもれて不正アクセスで起動されると困ります。安全にインターネットから直接パケットを飛ばすことはできないので、VPNを張ってルーターに入ってスクリプトを実行しています。
追記:FreeBSDには/usr/sbin/wakeというWOLのパケットを送れるコマンドがありました。portsにもいくつかありますが、依存もなくwakeでもよさそうです。とはいえ、ルーターはZRouterでイメージを作っていて、バイナリを追加するのはビルドと焼き直しが必要で、mrubyで処理するのも一つの方法です。
ネットで検索するとncコマンドでWOLのパケットを送信しているページがありますが、LinuxとFreeBSDではncの実装が違うためそのままは使えません。
Windowsのmruby-socket入りのmrubyでも動作しました。
Windows 7以降ではBATファイルでもWOLパケットが送れるようです。
WOLはBIOSで有効にした上で、shutdownする前にインターフェースのWOLをenableしておく必要があります。FreeBSDのreやfxpはデフォルトでWOLがenable(optionのWOL_MAGIC)になっています。
BIOSの設定はWOLとなっている場合もあり、PCI PMEとなってる場合もあり様々です。
PCI 2.1ではWOLはPCIボードとマザーボードをケーブルで接続していましたが、2.2ではPMEとしてPCIバスの中に取り込まれたので、ケーブル接続は必要なくなったようです。
Debianなマシンも起こせました。FreeBSDではデフォルトでWOLが有効なインターフェースが多いですが、Debianでは設定しないと有効にならないようです。
猛牛さんのLGY-PCI-TXDは古いものは蟹さんのRTL8139Cを使っていたようですがハードオフでRTL8139Dな物を110円で購入しDebianなマシンに入れてみたのですがWOLできませんでした。RTL8139DはRTL8139Cと互換がないのかもしれません。
停電があるとWOLできなくなります。機種によってはBIOSに電源復旧後に起動するなどの設定があるので、そうやって立ち上がったものを落とせば大丈夫かもしれません。