Raspberry Piを3GネットワークPPP接続のみで運用（SORACOM Air）

これができます

![Raspbery Pi ZeroとAKA-020の悪魔合体](https://qiita-image-store.s3.amazonaws.com/0/50435/3b3c16e4-8f8a-196c-07ed-ba0d406fc216.gif)

2016年8月現在、Raspberry Piを3Gでネットに接続する方法で入手しやすい製品として、「SORACOMスタータキット（3G USBドングル AK-020同梱）」を使う方法があります。

今回は、Raspberry Pi A+ や Raspberry Pi Zero に USB接続の3GモデムAK-020を繋いで、セットに同梱のSORACOM AirでRaspberry Piをインターネットに接続できるマシンを作ります。

**注意**

* `wvdial` ではなくて `pon` `poff` によるダイヤルアップ制御[^1]をします。
* SORACOM Airは通信量従量制で、安価なサービスというわけではない[^2]ので、定額SIM用に[別の作例](http://qiita.com/CLCL/items/99acf6dd3bd9c251f1aa)を用意しています。

[^1]: Raspberry PiをSORACOM Airを利用する場合、`wvdial` というダイアラーを使う作例が多いですが、それは、本命の Linuxで一般的な標準パッケージ `ppp` に含まれている、古式ゆかしい `pon` `poff` によるPPP接続Linuxで一般的な標準パッケージ `ppp` に含まれている、古式ゆかしい `pon` `poff` によるPPP接続を作るときに利用する `pppconfig` に不具合があり、PPP接続ができなかったため、代替方法として `wvdial` を選択してきたのだと思われます。今回 `pppconfig` を使った時の不具合とそれの解消方法が判明したので、`pon` `poff` によるPPP接続をします。

[^2]: 今回の作例で使う `SORACOM Air` は通信コスト的に安くはない（AWSを使うと国内クラウド基盤を使うより割高程度のニュアンスで高い）、定額SIMを使いたい場合は、定額SIMで作った場合の作例を用意していますからそちらを参照してください。 `SORACOM Air` を使った時の最大の利点は、IoT的な利用シーンでデバイスが盗難にあった時、SORACOM のコントロールパネルからSIMの停止ができる点ですので、IoTの実運用のプロダクトのときは使えばいいかと。

# ざっくり概要

設定とか入る前にざっくりした説明しておきます

## 動作確認は？

今回のテストにあたり、

* Raspberry Pi 1 Model B 512MB
* Raspberry Pi 1 Model A+ 256MB
* Raspberry Pi 1 Model A+ 512MB
* Raspberry Pi 3 Model B+
* Raspberry Pi Zero カメラ端子なし版
* Raspberry Pi Zero カメラ端子あり版

にNOOBSでインストールした Raspbian Jessie(2016-05-27)で動作確認をしております。

* Raspberry Pi 1 Model B 512MB
* Raspberry Pi 1 Model A+ 512MB
* Raspberry Pi 3 Model B+

にNOOBSでインストールした Raspbian Jessie With PIXEL(2016-11-25)で動作確認をしております。

## 必要3G-USBモデム・3G SIM

下記セット商品に同梱される3G USBモデム・3G SIMを利用します。

[SORACOMスターターキット 3G USBドングル AK-020同梱 [switch-science.com]](https://www.switch-science.com/catalog/2767/) ![セット内容写真](https://docid81hrs3j1.cloudfront.net/contents/large/2767_001.jpg)

* SORACOMスターターキット 3G USBドングル AK-020同梱 **&yen;5,980**
    * 3G-USBモデム **ABIT AK-020（&yen;4,980）**
    * 3G SIM **SORACOM Air nano-SIM（&yen;954）**
    * nano-SIM→mini-SIM変換 **SORACOM SIM アダプター（&yen;100）**
    * 1,000円分クーポン同梱

Raspberry Piのシステムファイルには、機材固有の設定をします。今回はSORACOM Air／AK-020専用設定となります。

## ⚠ 制作に入る前に

* 本稿では、Raspberry Piがインターネットに接続可能になっている状態からセットアップしていきますので、**Raspberry PiにRaspbian Jessieをセットアップする作業**、**Raspberry Pi を有線もしくはWi-Fiでインターネットにつなぐ作業は予め完了しておいてください**。
* **SORACOM Airの nanoSIM のアクティベーション**を済ませ、1000円分のクーポンは適用済みにしておいてください。
* 説明の際、 `pi@raspberrypi:~ $ ` と書いている場所は、コンソールが表示するプロンプトですので、入力しないでください。
* `RaspberryPiのコンソールへコピペ` と書いているところは、閲覧しているブラウザ上でクリップボードにコピーしておいて、コンソール上にペーストするとファイルが生成されたりコマンドが実行されるようになっています。最後の行は改行がコピーできていないので、自分でEnterキーを入れてあげてください。

# 設定

実際に操作を順番に追って設定していきます。

## ① Raspbianにpiユーザでログイン

以下の方法のいずれかで、Raspberry Piへコンソールアクセスします。

* Raspberry PiのGUIで、`LXTerminal` を起動
* Raspberry PiをDHCP制御下の有線LANにつないで、他のホストからSSH接続して `pi` ユーザーでログイン
* Raspberry PiでWi-Fiを使えるようにして、無線LANのネットワークに参加させ、ネットワーク内の他のホストからSSH接続して `pi` ユーザーでログイン
* Raspberry PiのGPIOに出ているUARTにシリアル接続

## ② 3G-USBモデム AK-020を挿入した時のLinuxの挙動を調整

先人達の努力により、

* 3G-USBモデムを接続すると最初はCDとして認識され、 `Eject` すると本来の通信デバイスとして認識される
* Linux側でUSBの認識制御をする `usb_modeswitch` があって、特定のUSBデバイスが認識されたら `eject` するまでできちゃう
* `udev` により、デバイスの認識とそれに対するふるまいを設定できる
* `Systemd` と統合されているためデバイスの認識に合わせて、　`Systemd` へメッセージを送ることができる

ということがわかっています。

今回使用する3G-USBモデム、AK-020用の特別設定を準備します。

```shell-session:RaspberryPiのコンソールへコピペ
cat << 'EOS' | sudo tee /etc/udev/rules.d/40-ak-020.rules
ACTION=="add",\
  ATTRS{idVendor}=="15eb",\
  ATTRS{idProduct}=="a403",\
  RUN+="/usr/sbin/usb_modeswitch --std-eject --default-vendor 0x15eb --default-product 0xa403 --target-vendor 0x15eb --target-product 0x7d0e"

ACTION=="add",\
  ATTRS{idVendor}=="15eb",\
  ATTRS{idProduct}=="7d0e",\
  RUN+="/sbin/modprobe usbserial vendor=0x15eb product=0x7d0e"

ATTRS{../idVendor}=="15eb",\
  ATTRS{../idProduct}=="7d0e",\
  ATTRS{bNumEndpoints}=="03",\
  ATTRS{bInterfaceNumber}=="02",\
  SYMLINK+="ttyAK020",\
  ENV{SYSTEMD_WANTS}+="ifup@wwan0.service"
EOS
```
```shell-session:RaspberryPiのコンソールへコピペ
sudo reboot
```

これで、Raspberry PiのUSBポートにAK-020を差し込むと、

* 最初に認識されるCD-ROMデバイスが自動でマウント解除され、
* 次に認識されるUSBシリアルデバイスにドライバをあてがい、
* USBシリアルデバイスを `/dev/ttyAK020` にリンクします。
* あと、（あれば）ネットワークインタフェース `wwan0` をupします。

実際にAK-020をRaspberry Piにつなぐ前と、つないだ後で比較してみます。

```shell-session:udev調整後、AK-020未接続の/dev/tty*のリスト
$ ls /dev/tty*
/dev/tty    /dev/tty21  /dev/tty35  /dev/tty49  /dev/tty62
/dev/tty0   /dev/tty22  /dev/tty36  /dev/tty5   /dev/tty63
/dev/tty1   /dev/tty23  /dev/tty37  /dev/tty50  /dev/tty7
/dev/tty10  /dev/tty24  /dev/tty38  /dev/tty51  /dev/tty8
/dev/tty11  /dev/tty25  /dev/tty39  /dev/tty52  /dev/tty9
/dev/tty12  /dev/tty26  /dev/tty4   /dev/tty53  /dev/ttyAMA0
/dev/tty13  /dev/tty27  /dev/tty40  /dev/tty54  /dev/ttyS0
/dev/tty14  /dev/tty28  /dev/tty41  /dev/tty55  /dev/ttyprintk
/dev/tty15  /dev/tty29  /dev/tty42  /dev/tty56
/dev/tty16  /dev/tty3   /dev/tty43  /dev/tty57
/dev/tty17  /dev/tty30  /dev/tty44  /dev/tty58
/dev/tty18  /dev/tty31  /dev/tty45  /dev/tty59
/dev/tty19  /dev/tty32  /dev/tty46  /dev/tty6
/dev/tty2   /dev/tty33  /dev/tty47  /dev/tty60
/dev/tty20  /dev/tty34  /dev/tty48  /dev/tty61
```

```shell-session:udev調整後、AK-020接続後の/dev/tty*のリスト
 $ ls /dev/tty*
/dev/tty    /dev/tty21  /dev/tty35  /dev/tty49  /dev/tty62
/dev/tty0   /dev/tty22  /dev/tty36  /dev/tty5   /dev/tty63
/dev/tty1   /dev/tty23  /dev/tty37  /dev/tty50  /dev/tty7
/dev/tty10  /dev/tty24  /dev/tty38  /dev/tty51  /dev/tty8
/dev/tty11  /dev/tty25  /dev/tty39  /dev/tty52  /dev/tty9
/dev/tty12  /dev/tty26  /dev/tty4   /dev/tty53  /dev/ttyAK020
/dev/tty13  /dev/tty27  /dev/tty40  /dev/tty54  /dev/ttyAMA0
/dev/tty14  /dev/tty28  /dev/tty41  /dev/tty55  /dev/ttyS0
/dev/tty15  /dev/tty29  /dev/tty42  /dev/tty56  /dev/ttyUSB0
/dev/tty16  /dev/tty3   /dev/tty43  /dev/tty57  /dev/ttyUSB1
/dev/tty17  /dev/tty30  /dev/tty44  /dev/tty58  /dev/ttyUSB2
/dev/tty18  /dev/tty31  /dev/tty45  /dev/tty59  /dev/ttyUSB3
/dev/tty19  /dev/tty32  /dev/tty46  /dev/tty6   /dev/ttyprintk
/dev/tty2   /dev/tty33  /dev/tty47  /dev/tty60
/dev/tty20  /dev/tty34  /dev/tty48  /dev/tty61
```

後の工程で使うため、AK-020は接続したままにしておきます。

## ③ USB-3GモデムにAPN設定を入れる

今回利用したUSB-3Gモデム・AK-020は実質APN設定をUSB-3Gモデムに記憶させておく必要がありません。なのでこの工程は飛ばす。

## ④ RaspbianにPPP設定を作る

ppp 設定用対話型プログラムの `pppconfig` をインストール。

```shell-session:RaspberryPiのコンソールへコピペ
sudo apt-get -y install pppconfig
```

`pppconfig` を起動する。これは、SORACOM AirとAK-020用の接続設定となります。

```shell-session:RaspberryPiのコンソールへコピペ
sudo pppconfig soracom_ak-020
```

以下の設定を順番に設定する。モーダルインタフェースとなっているから、1項目ごとに入力していくので注意。

```
Configure Nameservers(DNS)：Dynamic
Authentication Method：PAP
User Name：sora
Password：sora
Speed：460800
Pulse or Tone：Tone
Phone Number：*99#
Choose Modem Config Method：No
Manually Select Modem Port：/dev/ttyAK020
```

Finishedで設定ファイルの書き出しを行い、そのあと、Quitして終了。

Authentication Method（認証メソッド）ですが、~~`pppconfig 2.3.18` のバージョンには、~~ Raspbian Jessie(2016-05-27)では、認証メソッドに `CHAP` を選んでも、パスワードが `/etc/ppp/chap-secrets` に記録されない不具合がある**ので[^3]、認証メソッドを `PAP` にします。

[^3]: なお、PPP接続のための別のパッケージ `wvdial` で `CHAP` 認証で設定すると、正しく `/etc/ppp/chap-secrets` にパスワードが設定されるため、以降 `CHAP` で設定していた `pppconfig` で作った接続設定でもつながるようになります。Raspbianでの接続例は `wvdial` での例ばかりなのはおそらくこれのせい。

### PPP接続時、デフォルトルートをPPP側に置き換える設定

* `persist`：接続維持
* `replacedefaultroute`：PPP接続時にデフォルトルートを自動で置き換える設定。
実質的にこれによって3Gモデムでインターネット接続ができることになる。

```shell-session:RaspberryPiのコンソールへコピペ
cat << EOS | sudo tee -a /etc/ppp/peers/soracom_ak-020

persist
replacedefaultroute
EOS
```

書き換え後は以下のようになります。

```shell-session:/etc/ppp/peers/soracom_ak-020
# This optionfile was generated by pppconfig 2.3.18.
#
#
hide-password
noauth
connect "/usr/sbin/chat -v -f /etc/chatscripts/soracom_ak-020"
debug
/dev/ttyAK020
460800
defaultroute
noipdefault
user "sora"
remotename soracom_ak-020
ipparam soracom_ak-020

usepeerdns
persist
replacedefaultroute
```

### APN設定・PPP接続用チャットスクリプトの修正

PPP接続時に、モデムとコマンドシーケンスを送受信しますが、それの制御をするチャットスクリプトを調整します。

3G SIMのAPNの設定もこちらになります。

```shell-session:RaspberryPiのコンソールへコピペ
sudo sed -i -e "s/'' ATZ/'' ATH\n\
OK AT+CFUN=1\n\
OK ATZ\n\
OK 'ATQ0 V1 E1 S0=0 \&C1 \&D2'\n\
OK AT+CGDCONT=1,\"IP\",\"soracom.io\"\
/" /etc/chatscripts/soracom_ak-020
```

書き換え後は以下のようになります。

```shell-session:/etc/chatscripts/soracom_ak-020
# This chatfile was generated by pppconfig 2.3.18.
# Please do not delete any of the comments.  Pppconfig needs them.
#
# ispauth CHAP
# abortstring
ABORT BUSY ABORT 'NO CARRIER' ABORT VOICE ABORT 'NO DIALTONE' ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT DELAYED
# modeminit
'' ATH
OK AT+CFUN=1
OK ATZ
OK 'ATQ0 V1 E1 S0=0 &C1 &D2'
OK AT+CGDCONT=1,"IP","soracom.io"
# ispnumber
OK-AT-OK "ATDT*99#"
# ispconnect
CONNECT \d\c
# prelogin

# ispname
# isppassword
# postlogin

# end of pppconfig stuff
```


## ⑤ PPP接続テスト

AK-020は接続したままにしてありますので、この状態でPPP接続のテストをします。

```shell-session:RaspberryPiのコンソールへコピペ
sudo pon soracom_ak-020
```

AK-020のランプが、接続後待機状態の緑点灯⇒赤点灯⇒緑点滅（2Hz）になると、PPP接続完了です。

```shell-session:RaspberryPiのコンソールへコピペ
curl globalip.me
```

で、SORACOM Air（AWS）のホストIPが割りあてられていることを確認してください。

```shell-session:実行例
pi@raspberrypi:~ $ curl globalip.me
54.250.252.65
pi@raspberrypi:~ $ host `curl globalip.me`
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    14  100    14    0     0      5      0  0:00:02  0:00:02 --:--:--     5
65.252.250.54.in-addr.arpa domain name pointer ec2-54-250-252-65.ap-northeast-1.compute.amazonaws.com.
pi@raspberrypi:~ $
```

確認が終わったら、PPP接続を切断します。

```shell-session:RaspberryPiのコンソールへコピペ
sudo poff soracom_ak-020
```

3G-USBモデムのランプの緑点滅が、緑点灯になり、PPP接続が終了しました。

## ⑥ PPP接続用ネットワークインタフェースを作る

電源投入後、または3G-USBモデムをUSBポートに挿したタイミングで自動でPPP接続したいので、設定を作ります。

Raspberry Piに仮想のネットワークインタフェース `wwan0` を用意し、PPP接続と紐づけます。

```shell-session:RaspberryPiのコンソールへコピペ
cat << 'EOS' | sudo tee -a /etc/network/interfaces

allow-hotplug wwan0
iface wwan0 inet ppp
    provider soracom_ak-020
EOS
```

書き換え後は以下の通り。

```shell-session:/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

auto lo
iface lo inet loopback

iface eth0 inet manual

allow-hotplug wlan0
iface wlan0 inet manual
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

allow-hotplug wlan1
iface wlan1 inet manual
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

allow-hotplug wwan0
iface wwan0 inet ppp
    provider soracom_ak-020
```

なお、Raspbian Jessieの `Systemd` では、`/etc/network/interface` に書かれたネットワークインタフェースは `Systemd` における `.service` タイプの Unit として認識されるようです。

## ⑦ ネットワークインタフェースのリンクアップテスト

ネットワークインタフェースがシステム上にブリングアップしてきたときにちゃんとPPP接続ができるかテストをしていきます。ケースは3通りです。

* 手動でネットワークインタフェースを `ifup` してみる
* 3G-USBモデムをUSBポートに挿入してみる
* 3G-USBモデムをUSBポートに挿入したままRaspberry Piを再起動してみる

### 手動でネットワークインタフェースをifup/ifdownして接続テスト

`wwan0` ネットワークインタフェースを `ifup` することで、PPP接続が開始します。

```shell-session:RaspberryPiのコンソールへコピペ
sudo ifup wwan0
```
3G-USBモデムの緑ランプが2Hz点滅するとPPP接続済み。

`wwan0` ネットワークインタフェースを `ifdown` することで、PPP接続が終了します。

```shell-session:RaspberryPiのコンソールへコピペ
sudo ifdown wwan0
```

3G-USBモデムの緑ランプが点灯（待機状態）に切り替わります。

### 3G-USBモデムをUSBポートに挿入してリンクアップするかテスト

まだ3G-USBモデムがUSBポートに刺さったままだと思いますので、いったん抜きます。

少し待ってから（10秒くらい）、もう一度USBポートに挿してみると、数十秒から2分ぐらいまでの間で自動で接続開始します（3G-USBモデムの緑ランプが2Hz点滅するとPPP接続済み）。

[^3]: 3G-USBモデムが通信中に、いきなりUSBポートから抜いても、PPP接続は正常にクローズ・終了処理されますが、通信路が急になくなるので、アプリケーションレベルで不具合が出ることがあります（TCP通信であれば、FINパケットなしで通信が途絶するので通信路クローズのタイミングは実装と設定による）。

### Raspberry Piを再起動して自動でリンクアップするかテスト

3G-USBモデムをUSBポートに挿した状態のままにして、Raspberry Piを再起動します。

```shell-session:RaspberryPiのコンソールへコピペ
sudo reboot
```

Raspberry Piブートアップの途中で、3G-USBモデムがリンクアップするはずです。

## ⑧ 一巻の終わり

PPP接続開始の時点で、デフォルトゲートウェイが3G側に切り替わり、Raspberry Piから外に出る接続は3G経由となりますが、3G SIMに割り当てられるIPアドレスがAWSのプライベートIPアドレスだったりします。

ということは、Raspberry Piの内部から外のインターネット側へは通信できますが、**インターネット側からRaspberry Piの内部には入って行けません**。そのような必要があるのなら、せっかくのLinuxなのですから、Raspbery PiからどこかのVPSなど自分所有ホストへSSHトンネルを掘るなどするとよいですね。

# おまけ

この仕組み、なんに使おうかなー

## Raspberry Piから外部ホストへSSHトンネルを掘る

3G-USBモデムだけが刺さったRaspberry Piに、別ホストからリモートログインできるようにするため、**Raspberry Piから、3G-USBモデムを通じインターネット上のほかのホストにSSH接続し、トンネルを掘ります**。

遠隔地に置きっぱなしのRaspberry Piがあったとして、そのRaspberry Piが遠隔地から手元のホストにトンネルを自力で掘ってくれるので、自分が遠隔地に行かなくともRaspberry Pi側から掘られ済みのトンネルを通じてリモートログインできるのはとっても便利なような気がします。

見方を変えると、インターネットからRaspberry Piに入れるようになるUSBドングル、とも言えますね。

```shell-session:RaspberryPiのコンソールへコピペ
sudo apt-get -y install autossh
```

```shell-session:RaspberryPiのコンソールへコピペ
cat << "EOS" | sudo tee /etc/ppp/ip-up.d/10ssh-tunnel
#!/bin/sh

# This script is called with the following arguments:
#    Arg  Name                          Example
#    $1   Interface name                ppp0
#    $2   The tty                       /dev/ttyS1
#    $3   The link speed                38400
#    $4   Local IP number               12.34.56.78
#    $5   Peer  IP number               12.34.56.99
#    $6   Optional ``ipparam'' value    foo

if [ "$2" = "/dev/ttyL05A" \
  -o "$2" = "/dev/ttyAK020" \
  -o "$2" = "/dev/ttyE1750" \
]; then

    # ppp0 の txqueuelen が 3 なので、大きくする
    /sbin/ifconfig $1 txqueuelen 128

    # root権限で動いているため、ほかのユーザで実行したい場合は
    # su <user> -c "echo hoge" 的なことをする
    #
    # autossh でトンネルを掘る場合は、トンネルの出口の sshd の
    # 設定で、ClientAliveInterval 10/ ClientAliveCountMax 3
    # などの 回線が死んだらsshも自然に死ぬようにしておく必要が
    # あります（送り側のautosshでsshのセッションを張りなおしても、
    # 受け側の古いsshdが死なずに居座るとListen出来ないので）。

    su pi -c \
        '/usr/bin/autossh \
        -N -f -M 0 \
        -o ServerAliveInterval=60 \
        -o ServerAliveCountMax=3 \
        -o ExitOnForwardFailure=yes \
        -o StrictHostKeyChecking=no \
        -R 10022:localhost:22 \
        -i /home/pi/.ssh/id_rsa \
        -l remotehostuser \
        example.com'
fi

exit
EOS
```

```shell-session:RaspberryPiのコンソールへコピペ
sudo chmod 755 /etc/ppp/ip-up.d/10ssh-tunnel
```

`/etc/ppp/ip-up.d/` の中に実行権限をつけたスクリプトを置いておくと、PPPで接続確立後に実行されますので、 `autossh` によるSSHトンネルを掘る例です。

これをすると、3G電波だけでインターネットにつながっている `Raspberry Pi` に外部ホストからリモートログインが実現できます。

```shell-session:SSHトンネルの出口となっている外部ホストのコンソールへコピペ
slogin pi@localhost -p 10022 -o StrictHostKeyChecking=no
```

# 応用

GPSを搭載して、`node.js` を使って `AWS IoT` に `JSON` をPubして、 `DynamoDB` に現在位置をダダ記録するのはどうでしょう。

![PasPiApuls-3GUSB-GPS.jpg](https://qiita-image-store.s3.amazonaws.com/0/50435/2a6dc029-fac8-f204-613d-d9e8ca465a23.jpeg)


# 参考文献

* [Raspberry Pi A+ (Raspbian wheezy) でAK-020を使ってSORACOMに自動接続する - Qiita](http://qiita.com/mikage014/items/c8919b6130f7ad963776)
* [Raspberry PiでUSBモデム挿入時にSORACOMへ自動接続する - Qiita](http://qiita.com/gimy/items/1d49f8e08210e9c9f20a)
* [Rasoberry Pi + SORACOM Air SIM ( L-02C , AK-020 ) の wvdial.conf 設定情報 - Qiita](http://qiita.com/leonhard_jp/items/184f112e85f4bcdc2d67)
* [AK-020 を Raspbian で使う(とりあえず版) - Qiita](http://qiita.com/j3tm0t0/items/08c199fb8863b69e1139)
* [Raspberry PI+docomo L-05A+SORACOM AirでUSBモデム自動起動 - Qiita](http://qiita.com/hiroyasu55/items/e0ad9d04a9d8feb5c654)
* [L-02C でインターネット接続する方法: Linuxist の Debian-Ubuntu 拾い食い日記](http://balachi.sblo.jp/article/174518387.html)

# 脚注