SORACOM Arcのセルラー回線を使ったブートストラップと回線の冗長化を試してみる

色々試しながら書いていた関係で、かなりまとまりのない文章になっています。まとめてから書こうかと思いましたが、紆余曲折の過程も残しておいた方がいいかと思いそのままにしました。

先に結論を書いておきます。

- Airのブートストラップを使うと簡単にArc回線接続設定が取得できる
- AirとArcは共存させない方が良い
- Airの通信にArcのVPN通信を載せることは(多分)できない
- 有線(もしくはWiFi)でのインターネット到達性を見て、繋がるならArc接続、繋がらないならAir接続に切り替えるのが現実的なSORACOMサービス回線の冗長化方法

# はじめに

2021/6/22 〜 24に開催されたSORACOM Discovery 2021にて発表された[SORACOM Arc](https://soracom.jp/services/arc/)は、WiFiや有線接続など、ソラコムじゃない回線でもSORACOMサービスを使うことができるサービスです。となると、**回線サービスのSORACOM Airは要らなくなるのでは？**とも考えられそうです。

確かにそういう一面もあり、すでにインターネットへのアクセス経路が確保されているところにおいては、SORACOM Arc一本でも問題ないかもしれません。ですが、IoTが活用される現場では、回線がない、もしくはあっても使えない(客先のLANなど)という状況はしばしば起こります。そういう場合に、**物理配線や設定作業などがほぼなく、幅広い場所で使える**セルラー回線の優位性は大きいです。

また、セルラー回線とインターネット回線を併用することによるメリットもあります。具体的には以下のようなものです。

### 回線の冗長化

重要なサービスであれば回線は冗長化しておきたいところです。IoTではデバイスとクラウドと繋がることにより価値を出しますが、繋がらなくなるとサービスが使えなくなります。

**有線やWiFiでのインターネットが使えなくなった場合に備えて、バックアップ回線としてのセルラー回線があると安心です。**

### 対応範囲の拡張

セルラー回線と有線やWiFiでのインターネットのと両方の接続方法に対応するデバイスとして作っておくことで、対応可能な現場が格段に広がります。

車などで使う際に、移動中はセルラーで、到着した拠点ではWiFiで、といった使い分けもできるでしょう。

### デバイス認証情報の安全な保持

クラウドを利用するサービスの場合、そのクラウドの認証情報をデバイス内に持つ必要があります。

SORACOM Arcにおいても、VPNの接続設定やSAM(SORACOM Access Management)ユーザー情報をデバイス内に持つ必要がありますが、デバイスが解析されると認証情報が持ち出されてしまう可能性があります。

一方SORACOMサービスではデバイスに認証情報を持たせずに各種クラウドサービスと連携させる仕組み持っています。これはSIMをセキュアエレメントとして使って安全に認証情報を保持させ、利用する、というセルラー通信の仕組みを利用しています。

特に、[セキュアプロビジョニングサービス SORACOM Krypton](https://soracom.jp/services/krypton/)を使うとSIMを使ってSORACOM Arc用の接続情報を取得することができるため、**デバイス内に認証情報を持つ必要がなく、**認証情報が持ち出されるリスクを大きく下げることができます。(SIMの盗難は考えられますが、[IMEIロック機能](https://blog.soracom.com/ja-jp/2021/01/22/imei-lock-logs/)などで影響を小さくできます)

今回はSIMを使ったSORACOM Arc接続を試してみましょう。

# セルラー通信を使ったブートストラップを試す

公式のドキュメントでセルラー通信を使ったSORACOM Arcのブートストラップが紹介されています。
https://users.soracom.io/ja-jp/docs/arc/bootstrap-cellular/

こちらはSORACOM Airの通信を利用するものです。ラズパイでやってみましょう。
まずラズパイを有線インターネットに接続します。
ルートを確認するとこのようになっています。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

この時点ではインターネットにはつながっているものの、SORACOMプラットフォームにはつながっていません。

```
pi@raspberrypi:~ $ ping 1.1.1.1 -c 4
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=56 time=12.9 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=56 time=12.8 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=56 time=13.2 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=56 time=12.7 ms

--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 7ms
rtt min/avg/max/mdev = 12.739/12.908/13.167/0.160 ms
pi@raspberrypi:~ $ ping pong.soracom.io -c 4
PING pong.soracom.io (100.127.100.127) 56(84) bytes of data.

--- pong.soracom.io ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 110ms
```

次にSORACOM Air回線に接続します。[公式の接続ガイド](https://users.soracom.io/ja-jp/guides/devices/general/raspberry-pi-dongle/)を参考に進めます。
[3G対応データ通信端末 AK-020](https://soracom.jp/store/5274/)に[特定地域向け IoT SIM (plan-D)](https://soracom.jp/store/13217/)を挿し、ラズパイのUSBポートに挿します。
その後公式の接続ツールである[setup_air.sh](https://soracom-files.s3.amazonaws.com/setup_air.sh)を使えば接続されます。

```
curl -O https://soracom-files.s3.amazonaws.com/setup_air.sh
sudo bash setup_air.sh
```

接続できると、ppp0というネットワークインタフェースが追加され、ルート情報が更新されます。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         0.0.0.0         0.0.0.0         U     0      0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     207    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

インターネットにもSORACOMプラットフォームにも接続できます。(10.64.64.64や169.254.0.0は特に気にしなくて良いです)

```
pi@raspberrypi:~ $ ping 1.1.1.1 -c 4
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=56 time=346 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=56 time=335 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=56 time=354 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=56 time=344 ms

--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 6ms
rtt min/avg/max/mdev = 335.273/344.840/354.455/6.846 ms
pi@raspberrypi:~ $ ping pong.soracom.io -c 4
PING pong.soracom.io (100.127.100.127) 56(84) bytes of data.
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=1 ttl=64 time=418 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=2 ttl=64 time=348 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=3 ttl=64 time=347 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=4 ttl=64 time=337 ms

--- pong.soracom.io ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 337.476/362.596/417.807/32.143 ms
```

つながるんですが、いまいちな結果です。というのは、せっかく有線インターネット接続があるのに、デフォルトルートがppp0であるためにインターネット接続でもSORACOM Airの回線を使ってしまうのですね。これを避けるためには、デフォルトルートをeth0にし、ppp0はSORACOMプラットフォームである100.127.0.0/16ルートに限定します。

```
sudo route add default eth0
sudo route del default ppp0
sudo route add -net 100.127.0.0 netmask 255.255.0.0 ppp0
```

するとルート情報はこのようになります。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     207    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

再度pingをするとこうなります。

```
pi@raspberrypi:~ $ ping 1.1.1.1 -c 4
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=56 time=13.7 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=56 time=12.5 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=56 time=12.9 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=56 time=12.7 ms

--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 8ms
rtt min/avg/max/mdev = 12.539/12.948/13.670/0.439 ms
pi@raspberrypi:~ $ ping pong.soracom.io -c 4
PING pong.soracom.io (100.127.100.127) 56(84) bytes of data.
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=1 ttl=64 time=61.8 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=2 ttl=64 time=102 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=3 ttl=64 time=78.3 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=4 ttl=64 time=79.2 ms

--- pong.soracom.io ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 8ms
rtt min/avg/max/mdev = 61.775/80.372/102.242/14.404 ms
```

インターネットとSORACOMの両方に接続できて、かつインターネット側は高速な有線接続が利用できていることがわかります。

前置きが長くなってしまいましたが、セルラー通信を使ったbootstrapを試してみましょう。
まず[Relasesページ](https://github.com/soracom/soratun/releases)からsoratunをダウンロードします。(適切なアーキテクチャのバイナリを選びます。ラズパイはarmv7lです)
ダウンロード後tar.gzを展開し、バイナリをパスの通ったディレクトリに移動します。

```
wget https://github.com/soracom/soratun/releases/download/v1.0.0/soratun_1.0.0_linux_armv7.tar.gz
tar xvf soratun_1.0.0_linux_armv7.tar.gz
cd soratun_1.0.0_linux_armv7
sudo cp soratun /usr/local/bin
```

インストールはこれだけです。ではセルラー回線を使ったブートストラップを実施してみましょう。

```
pi@raspberrypi: $ sudo soratun --config /etc/arc.json bootstrap cellular
2021/07/11 07:26:20 failed to bootstrap: Post "https://krypton.soracom.io:8036/v1/provisioning/soracom/arc/bootstrap": dial tcp 54.250.252.67:8036: i/o timeout
```

失敗しましたね。これはブートストラップに使っているSORACOM Kryptonのエンドポイントkrypton.soracom.ioは通常のSORACOMプラットフォームのIPアドレスレンジ(100.127.0.0/16)とは違うためです。([公式のドキュメントにも言及されています。](https://users.soracom.io/ja-jp/docs/arc/bootstrap-cellular/)豆知識として、SORACON Air回線でなければ利用できない100.127.0.0/16以外のエンドポイントは、2021/7/12現在krypton.soracom.ioとendorse.soracom.ioの2つですがこのIPアドレスは同じです)krypton.soracom.ioのルートを追加して、再度実行します。

```
pi@raspberrypi:~ $ sudo route add -net krypton.soracom.io netmask 255.255.255.255 ppp0
pi@raspberrypi:~ $ sudo soratun --config /etc/arc.json bootstrap cellular
2021/07/11 07:41:49 failed to bootstrap: 403 Forbidden: POST https://krypton.soracom.io:8036/v1/provisioning/soracom/arc/bootstrap: {"errorCode":"PXY0001","statusCode":400,"options":{"resourceType":"Subscriber","resourceId":"440103214059845","operatorId":"OP0039300749"}}
```

今度はアクセスはできましたが、権限エラーが発生してしまいました。
[ドキュメント](https://users.soracom.io/ja-jp/docs/arc/bootstrap-cellular/)の「ステップ1:
SIM グループを作成し SORACOM Krypton を有効にする」をやっていないからですね。

説明は省略しますが、ユーザーコンソールにてSIMグループのSORACOM　Krypton設定をONにするだけです。

```
pi@raspberrypi:~ $ sudo soratun --config /etc/arc.json bootstrap cellular
Created/updated configuration file: /etc/arc.json
pi@raspberrypi:~ $ sudo cat /etc/arc.json 
{
  "privateKey": "5yWqXXXXXXXXXXXXXXXXXXXX",
  "publicKey": "rd2aXXXXXXXXXXXXXXXXXXXX",
  "simId": "",
  "logLevel": 2,
  "enableMetrics": true,
  "interface": "soratun0",
  "arcSessionStatus": {
    "arcServerPeerPublicKey": "vqJbXXXXXXXXXXXXXXXXXXXX",
    "arcServerEndpoint": "35.XX.XX.XX:11010",
    "arcAllowedIPs": [
      "100.127.0.0/16"
    ],
    "arcClientPeerIpAddress": "10.XX.XX.XX"
  }
}
```

無事SORACOM Arcの接続設定を作ることができました。コンソールをみると、物理SIMに仮想SIMが結びついていることがわかります。

![スクリーンショット 2021-07-13 7.51.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/d0b9b03e-6b1f-5392-8587-f7579897fdb4.png)

生成された接続設定について、[認証キーを用いたブートストラップ](https://users.soracom.io/ja-jp/docs/arc/bootstrap-authkey/)との違いは2つあります。

- profile(SAMユーザー情報)がない
- simIdがない

セルラー回線を使ったブートストラップにおいては、セルラー回線で接続されていることを認証要素として用いているため、デバイス内にSAMユーザーの認証情報を持たせる必要がありません。また、仮想SIMは物理SIMと結びついた状態で登録されるため、あえて仮想SIMの情報を持たなくても良いということがわかります。認証情報を持たなくても良い分、セキュリティを向上できたと考えて良いでしょう。

では、この情報を使って接続してみましょう。

```
pi@raspberrypi:~ $ sudo soratun up --config /etc/arc.json 
DEBUG: (soratun0) 2021/07/11 07:59:22 device started
DEBUG: (soratun0) 2021/07/11 07:59:22 Routine: event worker - started
DEBUG: (soratun0) 2021/07/11 07:59:22 Interface up requested
...中略
DEBUG: (soratun0) 2021/07/11 07:59:22 set link up: soratun0
DEBUG: (soratun0) 2021/07/11 07:59:22 Interface up requested
DEBUG: (soratun0) 2021/07/11 07:59:22 add route: 100.127.0.0/16
DEBUG: (soratun0) 2021/07/11 07:59:22 peer(vqJb…F8Wc) - Received handshake response
```

特に問題なく接続できました。ついでにsystemdのサービス登録をして、バックグラウンドで動作するようにします。実行ファイルを/usr/local/bin/soratunに、設定ファイルを/etc/soratun.serviceに置いておいたので、サンプルのサービス定義ファイルがそのまま使えます。

```
sudo cp conf/soratun.service.sample /etc/systemd/system/soratun.service
sudo systemctl start soratun
```

接続確認してみましょう。

```
pi@raspberrypi:~ $ ping pong.soracom.io -c 4
PING pong.soracom.io (100.127.100.127) 56(84) bytes of data.
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=1 ttl=64 time=16.3 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=2 ttl=64 time=15.4 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=3 ttl=64 time=15.4 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=4 ttl=64 time=15.4 ms

--- pong.soracom.io ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 7ms
rtt min/avg/max/mdev = 15.409/15.631/16.254/0.359 ms
```

応答時間的に、インターネット回線でSORACOMに接続できていることがわかりますね。
ルート情報を確認すると以下のようになっていました。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
169.254.0.0     0.0.0.0         255.255.0.0     U     207    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

soratun接続前は100.127.0.0/16はppp0を通して接続することになっていたのが、100.127.0.0/16がsoratun0になっています。これはsoratun実行時に自動的にルート情報が書き換えられているのですね。とすると、soratunが落ちた場合は復元されるのでしょうか？

```
pi@raspberrypi:~ $ sudo systemctl stop soratun
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     207    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

おっと、復元されませんね。SORACOMへの通信経路も失われてしまいました。

```
pi@raspberrypi:~ $ ping pong.soracom.io -c 4
PING pong.soracom.io (100.127.100.127) 56(84) bytes of data.

--- pong.soracom.io ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 153ms
```

これはちょっと困りますね。ppp0は生きているのだから、soratunが落ちている場合はppp0の経路を使ってSORACOMとの通信をしてほしいところです。そうでないと回線冗長化の意味がないですよね。インターネット回線(eth0)、セルラー回線(ppp0)、Arc接続(soratun0)で表にすると、以下のようになっていてほしいところです。

| 番号 | eth0 | ppp0 | soratun0 | インターネット経路<br>0.0.0.0/0 | SORACOM経路<br>127.100.0.0/16 | SORACOM経路 |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 1 | ○ | ○ | ○ | eth0 | soratun0 | |
| 2 | ○ | ○ | × | eth0 | ppp0 | |
| 3 | ○ | × | ○ | eth0 | soratun0 | |
| 4 | ○ | × | × | eth0 | × | |
| 5 | × | ○ | ○ | ppp0 | ppp0 | soratun必要なし |
| 6 | × | ○ | × | ppp0| ppp0 | |
| 7 | × | × | ○ | × | × | ありえない |
| 8 | × | × | × | × | × | |

0.0.0.0/0に対してはeth0 > ppp0となり、127.100.0.0/16に対してはsoratun0 > ppp0となるようにルートの優先度(メトリクス)を設定すると良いでしょう。
こんな感じで、ppp0に高めのメトリクスを設定します。

```
sudo route add default metric 300 ppp0
sudo route add -net 100.127.0.0 netmask 255.255.0.0 metric 300 ppp0
```

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
0.0.0.0         0.0.0.0         0.0.0.0         U     300    0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     300    0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     207    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

これは2番にあたる状況です。0.0.0.0/0にはeth0で、100.127.0.0/16に対してはppp0でアクセスします。

soratunが有効になるとこうなります。

```
pi@raspberrypi:~ $ sudo systemctl start soratun
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
0.0.0.0         0.0.0.0         0.0.0.0         U     300    0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
100.127.0.0     0.0.0.0         255.255.0.0     U     300    0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     207    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

これは1番にあたる状況です。0.0.0.0/0にはeth0で、100.127.0.0/16に対してはsoratun0でアクセスします。soratunを落とすと、2番の状況に戻ります。soratunが落ちた場合は問題なさそうですね。

# 回線の冗長化確認

では、SORACOM回線とインターネット回線がそれぞれ落ちた際に問題なく接続できるかを確認しましょう。

SORACOM回線が落ちた場合を想定するため、USBモデムを抜いてみます。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    0      0        0 eth0
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

これは3番の状況です。ちゃんとできていますね。USBモデムを挿し直します。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         0.0.0.0         0.0.0.0         U     0      0        0 ppp0
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
169.254.0.0     0.0.0.0         255.255.0.0     U     224    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

ppp0に設定していた、0.0.0.0/0のMetric:300と、100.127.0.0/16のルートがなくなってしまいました。routeコマンドによる設定は一時的なもので、インタフェース自体が再生成される場合に元に戻してくれるわけではないのですね。
永続的な設定はNetworkManagerを使うなどの方法がありますが、今回はsetup_air.shで設定したこともあり、pppの設定ファイルを変更することで対応します。

まずデフォルトルートがeth0からppp0に変更されてしまう点については、/etc/ppp/peers/wvdial(setup_air.shにて生成されたファイル)にあるreplacedefaultroute設定によるものなので、これを削除します。以下のようになります。

```
noauth
name wvdial
usepeerdns
persist
mtu 1464
novj
novjccomp
maxfail 1
```

また、100.127.0.0/16とkrypton.soracom.ioに対するルート設定は/etc/ppp/ip-upに記載すると良いでしょう。このファイルは冒頭に

```
# This script is run by the pppd after the link is established.
# It uses run-parts to run scripts in /etc/ppp/ip-up.d, so to add routes,
# set IP address, run the mailq etc. you should create script(s) there.
```

と書かれているように、PPP接続が確立した時に呼び出され、ルート設定などをする際に使われるものです。
以下の記述を追記します。

```
# add soracom air route
route add default metric 300 ppp0
route add -net 100.127.0.0 netmask 255.255.0.0 metric 300 ppp0
route add -net krypton.soracom.io netmask 255.255.255.255 ppp0
```

これでPPP接続された際に、動的にルート設定がされるはずです。試してみましょう。まず接続する前です。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

3番の状況ですね。これは特に問題ありません。ではUSBモデムを挿してみます。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
0.0.0.0         0.0.0.0         0.0.0.0         U     300    0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
54.250.252.67   0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
100.127.0.0     0.0.0.0         255.255.0.0     U     300    0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     223    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

必要なルートが設定されたことがわかります。インターネットおよびSORACOMサービスへの疎通も問題ありませんし、soratunが停止した場合にはAir回線経由でSORACOMサービスに到達できます。

最後に、有線インターネット接続が失われた場合を確認しましょう。わかりやすいケースとして、LANケーブルを抜きます。この場合、以下のようなルートになりました。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         0.0.0.0         0.0.0.0         U     204    0        0 wwan0
0.0.0.0         0.0.0.0         0.0.0.0         U     300    0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
54.250.252.67   0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
100.127.0.0     0.0.0.0         255.255.0.0     U     300    0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     204    0        0 wwan0
```

あれ？なぜかwwan0がデフォルトルートになってしまっていますね。これがあるとppp0経由でアクセスができません。こいつはどこからやってきたんでしょうか？

LANを抜いた時点のログを確認すると以下の記載があります。

```
Jul 11 16:25:56 raspberrypi dhcpcd[708]: eth0: fe80::e22f:6dff:feca:53c3 is unreachable
Jul 11 16:25:56 raspberrypi dhcpcd[708]: eth0: carrier lost
Jul 11 16:25:56 raspberrypi dhcpcd[708]: eth0: deleting address 2408:212:6d86:3200:f10b:d772:7af7:7d55/64
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Withdrawing address record for 2408:212:6d86:3200:f10b:d772:7af7:7d55 on eth0.
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Leaving mDNS multicast group on interface eth0.IPv6 with address 2408:212:6d86:3200:f10b:d772:7af7:7d55.
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Joining mDNS multicast group on interface eth0.IPv6 with address fe80::14c2:4ebb:b664:a68.
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Registering new address record for fe80::14c2:4ebb:b664:a68 on eth0.*.
Jul 11 16:25:56 raspberrypi dhcpcd[708]: eth0: deleting route to 2408:212:6d86:3200::/64
Jul 11 16:25:56 raspberrypi dhcpcd[708]: eth0: deleting default route via fe80::e22f:6dff:feca:53c3
Jul 11 16:25:56 raspberrypi dhcpcd[708]: eth0: deleting address fe80::14c2:4ebb:b664:a68
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Withdrawing address record for fe80::14c2:4ebb:b664:a68 on eth0.
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Leaving mDNS multicast group on interface eth0.IPv6 with address fe80::14c2:4ebb:b664:a68.
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Interface eth0.IPv6 no longer relevant for mDNS.
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Withdrawing address record for 192.168.0.104 on eth0.
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Leaving mDNS multicast group on interface eth0.IPv4 with address 192.168.0.104.
Jul 11 16:25:56 raspberrypi avahi-daemon[333]: Interface eth0.IPv4 no longer relevant for mDNS.
Jul 11 16:25:56 raspberrypi dhcpcd[708]: wwan0: adding default route
Jul 11 16:25:56 raspberrypi dhcpcd[708]: eth0: deleting route to 192.168.0.0/24
Jul 11 16:25:56 raspberrypi dhcpcd[708]: eth0: deleting default route via 192.168.0.1
```

下から三行目に「wwan0: adding default route」とあり、LANのデフォルトルートが失われた時、代替のルートとしてwwan0が選ばれてしまったようです。原因はわからないですが、ひとまずルート情報を削除すると以下のようになりました。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         0.0.0.0         0.0.0.0         U     300    0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
54.250.252.67   0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
100.127.0.0     0.0.0.0         255.255.0.0     U     300    0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     204    0        0 wwan0
```

これは5番の状況ですね。インターネットにはppp0でアクセスし、ソラコムサービスにはsoratun0でアクセスします。ところが実際に試してみると、SORACOMサービスにはアクセスできませんでした。ログをみると、soratunの接続がうまくいっていないっぽいです。

```
Jul 11 16:28:50 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:28:50 assign IP address: 10.168.103.119
Jul 11 16:28:50 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:28:50 set link up: soratun0
Jul 11 16:28:50 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:28:50 add route: 100.127.0.0/16
Jul 11 16:28:50 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:28:50 Interface up requested
Jul 11 16:28:55 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:28:55 peer(vqJb…F8Wc) - Handshake did not complete after 5 seconds, retrying (try 2)
Jul 11 16:28:55 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:28:55 peer(vqJb…F8Wc) - Sending handshake initiation
Jul 11 16:29:00 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:29:00 peer(vqJb…F8Wc) - Handshake did not complete after 5 seconds, retrying (try 3)
Jul 11 16:29:00 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:29:00 peer(vqJb…F8Wc) - Sending handshake initiation
Jul 11 16:29:04 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:29:04 peer(vqJb…F8Wc) - Handshake did not complete after 5 seconds, retrying (try 4)
Jul 11 16:29:04 raspberrypi soratun[974]: DEBUG: (soratun0) 2021/07/11 16:29:04 peer(vqJb…F8Wc) - Sending handshake initiation
...中略
Jul 11 16:30:49 raspberrypi systemd[1]: soratun.service: Watchdog timeout (limit 2min)!
Jul 11 16:30:49 raspberrypi systemd[1]: soratun.service: Killing process 974 (soratun) with signal SIGABRT.
```

リンクアップはするものの、ハンドシェイクに失敗してしまうようです。こちらも原因は不明ですが、SORACOM Airの回線自体でSORACOM Arc接続をすることはできない、という症状のようですね。ただし、SORACOM Airの接続ができているのであればSORACOM Arcの接続をする必要はないので、こちらは有線インターネット接続がなくなったことを検知したらsoratunを止める、が良いのかもしれません。(6番のケース)soratunを止めると以下のようになります。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         0.0.0.0         0.0.0.0         U     300    0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
54.250.252.67   0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     300    0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     204    0        0 wwan0

pi@raspberrypi:~ $ ping 1.1.1.1 -c 4
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=56 time=87.7 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=56 time=86.7 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=56 time=84.9 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=56 time=81.7 ms

--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 7ms
rtt min/avg/max/mdev = 81.699/85.231/87.678/2.279 ms
PING pong.soracom.io (100.127.100.127) 56(84) bytes of data.
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=1 ttl=64 time=85.1 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=2 ttl=64 time=75.7 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=3 ttl=64 time=85.8 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=4 ttl=64 time=83.8 ms

pi@raspberrypi:~ $ ping pong.soracom.io -c 4
--- pong.soracom.io ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 8ms
rtt min/avg/max/mdev = 75.712/82.602/85.756/4.044 ms
```

SORACOM Air回線一本でわかりやすいですね。

であれば、有線接続がある状況でセルラー回線接続をする必要もあまりないのかもしれません。セルラー接続していると、電力や通信量を無駄に使うことになるので。

ルートの設定とか色々やりましたが、3番(インターネット回線 + SORACOM Arc接続)と6番(SORACOM Air接続)を回線の状況に応じて切り替えるのが実用的かな、という結論に達しました。

具体的にいえば、以下のような感じになります。

```check-route.sh
#!/bin/bash

# デフォルトルートがwwan0になっていたら削除する
if /usr/sbin/route -n | /usr/bin/grep "^0\.0\.0\.0.*wwan0$" ; then
  /usr/sbin/route del default wwan0
fi

# 有線でインターネットに到達できる場合
if /usr/sbin/ifconfig eth0 && /usr/bin/ping 1.1.1.1 -I eth0 -c 1 -w 3 ; then
  # Air接続していたら切断
  if /usr/bin/pgrep wvdial ; then /usr/sbin/ifdown wwan0 ; fi
  # Arc接続してなかったら接続
  if ! /usr/bin/systemctl is-active soratun ; then /usr/bin/systemctl start soratun ; fi
else # 有線でインターネットに到達できない場合
  # Arc接続していたら切断
  if /usr/bin/systemctl is-active soratun ; then /usr/bin/systemctl stop soratun ; fi
  # Air接続していなかったら接続
  if ! /usr/bin/pgrep wvdial ; then /usr/sbin/ifup wwan0 ; fi
fi
```

これをcronで定期的に実行などすれば、1分程度で切り替えできるまあまあの冗長性を確保できるはずです。

```cron
* * * * * /bin/bash /root/check-route.sh
```

共存した場合のルート設定を考えるのは大変なので、このくらいがわかりやすくていいのかもしれません。設定していた/etc/ppp/peers/wvdialのreplacedefaultrouteは元に戻し、/etc/ppp/ip-upのルート設定もなくします。

最後に一通りの動作を確認します。

まず、両方繋がっている場合はこのようになります。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         0.0.0.0         0.0.0.0         U     0    　　　　0        0 ppp0
0.0.0.0         192.168.0.1     0.0.0.0         UG    202  　　　　0        0 eth0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     204    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

ルートチェックスクリプトが流れると、Air接続が切られ、Arc接続が有効になりました。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         U     0    　　　　0        0 eth0
0.0.0.0         192.168.0.1     0.0.0.0         UG    202  　　　　0        0 eth0
100.127.0.0     0.0.0.0         255.255.0.0.    U     0      0        0 soratun0
169.254.0.0     0.0.0.0         255.255.0.0     U     204    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

有線接続が不調であるケースとして、有線のインターネット側を外しました。これでインターフェースは生きてるけど、インターネットには接続できない、という状況になります。ルートチェックスクリプトが流れると、Arc接続が切られ、Air接続がされました。

```
pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         0.0.0.0         0.0.0.0         U     0    　　　　0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     204    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

いい感じですね。デフォルトルートがppp0に変わったので、問題なくインターネット通信も問題なくできます。紆余曲折ありましたが、ひとまずラズパイでのAir + Arcの回線冗長化はできたのではないかと思います。

回線冗長化はデバイス内でのルート設定が思いのほか大変ですね。DHCPとの組み合わせで、デフォルトルートが自動で変わるなど予想しづらい動きが色々と起こりました。色々やりましたが、これでもまだ完璧かどうかはわからないです。実運用の際はさまざまなケースを試した方がいいでしょうね。

# 余談

AirとArcの回線を両方つなぎ、ルート的にArcが優先されていると、SORACOM Napterでの接続ができないようです。ルート設定がこのようになっているとします。

```
root@raspberrypi:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    202    0        0 eth0
0.0.0.0         0.0.0.0         0.0.0.0         U     300    0        0 ppp0
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
100.127.0.0     0.0.0.0         255.255.0.0     U     0      0        0 soratun0
100.127.0.0     0.0.0.0         255.255.0.0     U     300    0        0 ppp0
169.254.0.0     0.0.0.0         255.255.0.0     U     204    0        0 wwan0
192.168.0.0     0.0.0.0         255.255.255.0   U     202    0        0 eth0
```

Arcが優先されているので、Arc接続でNapterができるはずです。

実際はこの時パケットはAir接続(ppp0)に届いています。

```
root@raspberrypi:~# tcpdump -i ppp0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ppp0, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
16:08:56.474279 IP 100.127.10.16.41431 > 10.170.108.132.ssh: Flags [S], seq 3683329635, win 29200, options [mss 1338,sackOK,TS val 2463825229 ecr 0,nop,wscale 7], length 0
16:08:57.474230 IP 100.127.10.16.41431 > 10.170.108.132.ssh: Flags [S], seq 3683329635, win 29200, options [mss 1338,sackOK,TS val 2463826230 ecr 0,nop,wscale 7], length 0
16:08:59.494156 IP 100.127.10.16.41431 > 10.170.108.132.ssh: Flags [S], seq 3683329635, win 29200, options [mss 1338,sackOK,TS val 2463828246 ecr 0,nop,wscale 7], length 0
16:09:03.654175 IP 100.127.10.16.41431 > 10.170.108.132.ssh: Flags [S], seq 3683329635, win 29200, options [mss 1338,sackOK,TS val 2463832410 ecr 0,nop,wscale 7], length 0
16:09:11.844062 IP 100.127.10.16.41431 > 10.170.108.132.ssh: Flags [S], seq 3683329635, win 29200, options [mss 1338,sackOK,TS val 2463840598 ecr 0,nop,wscale 7], length 0
16:09:27.973988 IP 100.127.10.16.41431 > 10.170.108.132.ssh: Flags [S], seq 3683329635, win 29200, options [mss 1338,sackOK,TS val 2463856725 ecr 0,nop,wscale 7], length 0
```

ですが、戻りのパケットはArc接続(soratun0)で返しています。

```
root@raspberrypi:~# tcpdump -i soratun0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on soratun0, link-type RAW (Raw IP), capture size 262144 bytes
16:11:46.992887 IP 10.170.108.132.ssh > 100.127.10.16.38245: Flags [S.], seq 407996457, ack 2663752679, win 64296, options [mss 1380,sackOK,TS val 741765612 ecr 2463995745,nop,wscale 7], length 0
16:11:48.002870 IP 10.170.108.132.ssh > 100.127.10.16.38245: Flags [S.], seq 407996457, ack 2663752679, win 64296, options [mss 1380,sackOK,TS val 741766622 ecr 2463995745,nop,wscale 7], length 0
16:11:49.019048 IP 10.170.108.132.ssh > 100.127.10.16.38245: Flags [S.], seq 407996457, ack 2663752679, win 64296, options [mss 1380,sackOK,TS val 741767639 ecr 2463995745,nop,wscale 7], length 0
16:11:50.012914 IP 10.170.108.132.ssh > 100.127.10.16.38245: Flags [S.], seq 407996457, ack 2663752679, win 64296, options [mss 1380,sackOK,TS val 741768632 ecr 2463995745,nop,wscale 7], length 0
16:11:52.059034 IP 10.170.108.132.ssh > 100.127.10.16.38245: Flags [S.], seq 407996457, ack 2663752679, win 64296, options [mss 1380,sackOK,TS val 741770679 ecr 2463995745,nop,wscale 7], length 0
16:11:54.142760 IP 10.170.108.132.ssh > 100.127.10.16.38245: Flags [S.], seq 407996457, ack 2663752679, win 64296, options [mss 1380,sackOK,TS val 741772762 ecr 2463995745,nop,wscale 7], length 0
```

多分SORACOM Napterから見た時はAirの方を優先しているのかな？と思われます。
行きと帰りのルートが違うからなのか、Arc接続が本来IPアドレスが違う(Arcに割り当てられたIPは10.168.103.11なのにAir接続の10.170.108.132で返している)からなのか、詳しい原因はわかりませんが、今のところAirとArcを混在させた場合Napterが使えなくなるのは確かです。そういう意味でも、冗長化とはいえどちらか一方を使った方が良いということになるかと思います。

# 終わりに

SORACOM Arcは単体で使っても便利ですが、SIMと合わせて使うことで回線を冗長化したり、接続情報をデバイスに保存しなくて済むなどのメリットがあります。

WiFi接続のバックアップにAirの回線を使う、セルラー回線が届かない室内にWiFi回線を使うなど、SORACOMが使える状況がさらに増えますね。

今まで使えなかったところにも活用を広げていけそうです。