今日も微速転進

ここではないどこかへ

Raspbian Stretch Lite で突然無線LANに接続不能になったとき


スポンサーリンク

引き続きラズパイ関連ネタで。無線LANのトラブルに関して(Voice Kitは無関係です)。

問題なく無線LANに接続できていたのに突然接続できなくなってしまった場合の話。

Qiitaに書こうと思ったが、唐突に買収されたらしいのでちょっと様子見。

非常に冗長な記事なので結論を先に書いておくと、rfkillコマンドがポイント。インストールして/etc/rc.localrfkill unblock wifと1行入れれば解決。

その場しのぎの解決策ではあるけど。

環境

  • ブロードバンドルーター: NEC WX01 (WiMAX2+)
  • Raspbian Stretch Lite (2017/11/29 ver. )
  • Raspberry Pi model 3B

環境要因ではなさそう。

現象

初回セットアップ直後は問題はなく、普通に無線LANに接続できていた(はず)。

接続が不安定とかそういうのではなく、特定のタイミングからさっぱりつながらなくなってしまった。

パッケージを追加後の後処理が実行されている最中(systemd 関係のフック処理?)で通信が途切れて再起動しても接続できず。

$ sudo ip link set wlan0 up
RTNETLINK answers: Operation not possible due to RF-kill

"RF-kill"に関してはArch LinuxのWikiが参考になった。無線LANデバイスをソフトウェア側で無効化するスイッチがあり、これを"RF-kill"と呼ぶらしい。

ワイヤレス設定 - ArchWiki

理由はわからないが、無線LANデバイスを無効化するための内部ロックがセットしたまま解除されてない状態。

ソフトウェア的に無線LANが無効化されているので接続処理が実行されない。

対処

無線LANデバイスの内部ロックを外すためにrfkillコマンドが必要だがRaspbian Liteではインストールされていない。 有線ネットワークにつないでrfkillパッケージをaptでインストールする。

$ sudo apt install rfkill

以下のコマンドでロックを外すことができる。

$ rfkill unblock wifi

この状態で、再度ipコマンドでデバイスを有効化する。

$ sudo ip link set wlan0 up

成功していれば、無事にIPアドレスを取得できるはず。

その他

rfkillコマンドの他のオプションとして、listで現在のデバイスのブロック状態を確認できる。

$ rfkill list
0: phy0: Wireless LAN
    Soft blocked: no
    Hard blocked: no
1: hci0: Bluetooth
    Soft blocked: yes
    Hard blocked: no

ラズパイの無線LANモジュール用のカーネルモジュール名はbrcmfmacなので、以下のようにしてカーネルのログを絞り込んだりできる。

$ dmesg | grep brcm

あとはjournalctl -lでログを見る。

再起動のたびに接続不能になる場合

上記で問題が解決するかと思いきや、手元の環境では解決せず。とりあえずudevsystemdが容疑者。

systemd-udevd[169]: Process '/usr/lib/raspberrypi-sys-mods/rfkill-persist' failed with exit code 1

頑張って調べていくと、udevは/lib/udev/rules.d/15-rfkill.rulesというルールにより、/usr/lib/raspberrypi-sys-mods/rfkill-persistというスクリプトを実行している。

このスクリプトはどういうスクリプトかというと、/dev/rfkill の状態が変化した時にudev経由で実行されるもの。 こいつは何をしているかというと、/lib/systemd/systemd-rfkillというコマンドを引数付きで実行するのだが……。

ところがこのsystemd-rfkillコマンドは引数を要求しない。よって必然的にエラーになる。

$ /lib/systemd/systemd-rfkill save wifi
This program requires no arguments.

このコマンドは何をするかというと、ソースを見る限り、udevイベントの状態をチェックして、/var/lib/systemd/rfkill/ディレクトリ配下のファイルに状態を記録あるいは読み出すコマンド。

マニュアルによるとカーネルのコマンドラインオプションをチェックするので無効化自体はできるみたいだけど、そもそも状態の保存に失敗しているので、オプションに関係なくうまく動いていないのでは……。

試行錯誤のメモ

とりあえず問題のあるスクリプトを修正してみた。

$ sudo vi  /usr/lib/raspberrypi-sys-mods/rfkill-persist

要するに引数なしで実行すればいいので、3行目のコマンドの引数2つを削除。

#!/bin/sh
#RFKID=`basename $DEVPATH`
#/lib/systemd/systemd-rfkill save $RFKID
/lib/systemd/systemd-rfkill

2行目をコメントアウト。3行目をコピーして元の行をコメントアウト。コピーした行は引数を除去。

もう一度、sudo rfkill unblock wifiを実行して再起動。やはり解決せず。

結論

仕方がないので、/etc/rc.localでお茶を濁す。まあその場しのぎなんだけど。

以下の1行を/etc/rc.localの、# By default this script does nothing.という行の下に追加。

/usr/sbin/rfkill unblock wifi

systemd、バグってんじゃないのかと思いつつ、ググってもそれらしいバグは上がってない。それとRaspbianのGUIなしを使うユーザはあまり無線LANは使わないのかな……。

適切かどうか断言できないけど、udevルールのうち、/lib/udev/rules.d/15-rfkill.rulesというのは無駄ではないかと思う。理由としては"systemd-rfkill"というサービスが存在しており起動時と終了時に実行されるはずなので*1

参考情報

古いバージョンのRaspbian向けの情報だと、/etc/network/interfacesを編集しろと書いてあるけどこれは不要。 同時に、/etc/network/interfaces.dも空で問題なく無線LANは接続できる(前述のRF Kill問題さえなければ)。

pi@raspberrypi:~ $ cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d

上記の設定ファイルでインクルードしているディレクトリは空。

pi@raspberrypi:~ $ ls /etc/network/interfaces.d
pi@raspberrypi:~ $

なお、天下無双の嫌われ者?、systemd様がよろしくやってくれるので、wpa_supplicant.serviceも無効のままで問題ない。

$  systemctl is-enabled  wpa_supplicant.service
disabled

IPアドレスを固定したいとかそう言うのはdhcpcdでやるか、systemd-networkdにやらせろということらしい。

なお、ifupおよびifdownコマンドの応答を見ると、デバイスがみつからないように見えるが、この状態でも正常につながるので気にしなくていい。

$ ifup wlan0
ifup: unknown interface wlan0

まとめ

ちゃんとRaspbianのネットワークまわりの初期化処理の流れを把握していないので勉強しないとまずそう。

良くも悪くもハードウェアの知識が知らずと身につくのがLinux系のいいところ。Windows にrfkill相当のコマンドがあるのか知りませんが。 トラブルの度に今まで知らなかった知識が増えるんでこういうのも(たまには)悪くない*2

関連リンク

*1:サービスの"status"が"static"になっている

*2:もちろんお金とか締め切りが絡んでいない時に限る

広告