Raspberry PiでUSBモデム挿入時にSORACOMへ自動接続する


概要

簡単にRaspberry PiでSORACOM Airを使う(FS01BU USBモデムを用いて自動接続)で紹介されているような方法を使うとFS01BU USBモデム+SORACOM AirのSIMの組み合わせでブート時に自動接続できるようになりますが、デバイス挿入時に自動接続させたかったので、その方法をまとめます。OS は Raspbian です。
udev でデバイス挿抜時に ifup/ifdown を実行することで実現します。

準備

必要なパッケージをインストールします。

pi@raspberrypi:~ $ sudo apt-get install wvdial usb-modeswitch

手動接続用のスクリプトをダウンロードします。

pi@raspberrypi:~ $ curl -O http://soracom-files.s3.amazonaws.com/connect_air.sh
pi@raspberrypi:~ $ chmod 755 ./connect_air.sh

モデムを挿した状態で実行して手動接続できることを確認します。

pi@raspberrypi:~ $ sudo ./connect_air.sh

この際、 /etc/wvdial.conf が生成されるので、以下ではそれを編集して使います。私の環境では以下が生成されます。

/etc/wvdial.conf
[Dialer Defaults] 
Init1 = ATZ 
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0 
Init3 = AT+CGDCONT=1,"IP","soracom.io" 
Dial Attempts = 3 
Stupid Mode = 1 
Modem Type = Analog Modem 
Dial Command = ATD 
Stupid Mode = yes 
Baud = 460800 
New PPPD = yes 
Modem = /dev/ttyUSB2 
ISDN = 0 
APN = soracom.io 
Phone = *99***1# 
Username = sora 
Password = sora 
Carrier Check = no 
Auto DNS = 1 
Check Def Route = 1 

ifup で接続できるようにする

/etc/network/interfaces に以下を追記します。

/etc/network/interfaces
allow-hotplug soracom
iface soracom net wvdial

ifup soracom で接続されることを確認します。

pi@raspberrypi:~ $ sudo ifup soracom
pi@raspberrypi:~ $ ifconfig ppp0
ppp0      Link encap:Point-to-Pointプロトコル  
          inetアドレス:10.xxx.xxx.xxx P-t-P:10.64.64.64  マスク:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  メトリック:1
          RXパケット:7 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:8 エラー:0 損失:0 オーバラン:0 キャリア:0
      衝突(Collisions):0 TXキュー長:3 
          RXバイト:130 (130.0 B)  TXバイト:181 (181.0 B)

ifdown soracom で切断できます。

pi@raspberrypi:~ $ sudo ifdown soracom
pi@raspberrypi:~ $ ifconfig ppp0
ppp0: インタフェース情報を取得中にエラーが発生しました: デバイスが見つかりません

接続が切れても自動で再接続できるようにする

/etc/ppp/peers/wvdialpersist オプションを有効にします。

--- a/ppp/peers/wvdial
+++ b/ppp/peers/wvdial
@@ -2,3 +2,5 @@ noauth
 name wvdial
 usepeerdns
 replacedefaultroute
+persist
+holdoff 10

デバイス名を固定する

USBシリアルデバイスが1つしか接続されていない場合、ブート時に FS01BU が認識されると、

/dev/ttyUSB0
/dev/ttyUSB1
/dev/ttyUSB2

の3つのデバイスファイルを作成され、ダイヤルアップに使われるのは /dev/ttyUSB2 です。しかし、他の USB シリアルデバイスが接続されていると認識順序によってこれがずれます。また、 FS01BU だけ接続されていても活線挿抜によりデバイス番号がずれることがあります。
このため、デバイス名を固定するを参考にデバイス名を固定します。ただし、誤判定を避けるため bNumEndpointsbInterfaceNumber だけではなく idVendoridProductATTRS{../によって参照します。

/etc/udev/rules.d/10-soracom.rules
KERNEL=="ttyUSB*", ATTRS{../idVendor}=="1c9e", ATTRS{../idProduct}=="6801", ATTRS{bNumEndpoints}=="02", ATTRS{bInterfaceNumber}=="02", SYMLINK+="modem"

リブートしてシンボリックリンクが張られていることを確認します。

pi@raspberrypi:~ $ ls -l /dev/ttyUSB* /dev/modem
lrwxrwxrwx 1 root root         7  2月 26 07:48 /dev/modem -> ttyUSB2
crw-rw---- 1 root dialout 188, 0  2月 26 07:48 /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 1  2月 26 07:48 /dev/ttyUSB1
crw-rw---- 1 root dialout 188, 2  2月 26 07:48 /dev/ttyUSB2

wvdial はデフォルトで /dev/modem を使うので、 /etc/vwconf.conf/dev/ttyUSB2 を指定している箇所を削除します。

--- a/wvdial.conf
+++ b/wvdial.conf
@@ -9,7 +9,6 @@ Dial Command = ATD
 Stupid Mode = yes
 Baud = 460800
 New PPPD = yes
-Modem = /dev/ttyUSB2
 ISDN = 0
 APN = soracom.io
 Phone = *99***1#

再度 ifup soracom で接続されることを確認します。

デバイス挿入時に接続する

Raspberry Pi 2 + systemd + udevで、USBデバイス挿入時にサービスを起動するに倣って先程の udev ルールを以下のように書き換えます。後述するように action を記述しないのがポイントです。

/etc/udev/rules.d/10-soracom.rules
KERNEL=="ttyUSB*", ATTRS{../idVendor}=="1c9e", ATTRS{../idProduct}=="6801", ATTRS{bNumEndpoints}=="02", ATTRS{bInterfaceNumber}=="02", SYMLINK+="modem", ENV{SYSTEMD_WANTS}="ifup@soracom"

リブートすると、デバイス認識時に ifup soracom が自動実行されて接続されます。

pi@raspberrypi:~ $ ls -l /dev/ttyUSB* /dev/modem
lrwxrwxrwx 1 root root         7  2月 26 20:55 /dev/modem -> ttyUSB2
crw-rw---- 1 root dialout 188, 0  2月 26 20:55 /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 1  2月 26 20:55 /dev/ttyUSB1
crw-rw---- 1 root dialout 188, 2  2月 26 22:10 /dev/ttyUSB2
pi@raspberrypi:~ $ ifconfig ppp0
ppp0      Link encap:Point-to-Pointプロトコル  
          inetアドレス:10.xxx.xxx.xxx P-t-P:10.64.64.64  マスク:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  メトリック:1
          RXパケット:7 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:8 エラー:0 損失:0 オーバラン:0 キャリア:0
      衝突(Collisions):0 TXキュー長:3 
          RXバイト:130 (130.0 B)  TXバイト:181 (181.0 B)

この状態でデバイスを抜いて挿すと、デバイス番号がずれますが、シンボリックリンクが追従し、再接続されます。

pi@raspberrypi:~ $ ls -l /dev/ttyUSB* /dev/modem
lrwxrwxrwx 1 root root         7  2月 26 23:10 /dev/modem -> ttyUSB3
crw-rw---- 1 root dialout 188, 0  2月 26 23:10 /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 1  2月 26 23:10 /dev/ttyUSB1
crw-rw---- 1 root dialout 188, 3  2月 26 23:12 /dev/ttyUSB3
pi@raspberrypi:~ $ ifconfig ppp0
ppp0      Link encap:Point-to-Pointプロトコル  
          inetアドレス:10.xxx.xxx.xxx P-t-P:10.64.64.64  マスク:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  メトリック:1
          RXパケット:7 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:8 エラー:0 損失:0 オーバラン:0 キャリア:0
      衝突(Collisions):0 TXキュー長:3 
          RXバイト:130 (130.0 B)  TXバイト:181 (181.0 B)

補足

wvdial で他の接続先も使う場合

/etc/wvdial.confDefaults 以外の Dialer セクションを作りそちらに SORACOM 用の設定を移します。
その上で、 /etc/network/interfaces で provider としてそのセクションを指定します。

--- a/wvdial.conf
+++ b/wvdial.conf
@@ -1,4 +1,4 @@
-[Dialer Defaults]
+[Dialer soracom]
 Init1 = ATZ
 Init2 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
 Init3 = AT+CGDCONT=1,"IP","soracom.io"
--- a/network/interfaces
+++ b/network/interfaces
@@ -25,3 +25,4 @@ iface wlan1 inet manual

 allow-hotplug soracom
 iface soracom inet wvdial
+    provider soracom

ifdown について

systemd の Unit ファイルには [email protected] はあっても、 [email protected] はないのですが、

pi@raspberrypi:~ $ systemctl list-unit-files | grep 'if.*@'
[email protected]                          static

[email protected] の中身が以下のようになっており、 udev ルールで action を書かなければ add 時に ifupremove 時に ifdown が実行されます。

pi@raspberrypi:~ $ cat /lib/systemd/system/[email protected] 
[Unit]
Description=ifup for %I
After=local-fs.target network-pre.target networking.service
Before=network.target
BindsTo=sys-subsystem-net-devices-%i.device
ConditionPathIsDirectory=/run/network
DefaultDependencies=no

[Service]
ExecStart=/sbin/ifup --allow=hotplug %I
ExecStop=/sbin/ifdown %I
RemainAfterExit=true

参考

下記の手法を組み合わせることで実現できました。ありがとうございます。

簡単にRaspberry PiでSORACOM Airを使う(FS01BU USBモデムを用いて自動接続)
デバイス名を固定する
Raspberry Pi 2 + systemd + udevで、USBデバイス挿入時にサービスを起動する