Raspberry Piをheadlessで接続するWi-Fiを切り替える

## 概要
最近ラズパイを触り始めました。
なかなかドンピシャの情報が出てこなかったのでメモしておきます。

この記事では、スマホ・PCでWi-Fiを切り替えるような操作を、SSH接続したターミナルから行う方法を紹介します。
最終的に下のGIFのような感じで切り替えできるようになります(以下)。

1. PC、ラズパイとも602HWa-527CF0(10.0.0.0/24)に接続した状態でSSH
2. ラズパイでarrowsRX_8018(192.168.43.0/24)に接続
3. SSHが途切れるのでPCでも同じWi-Fiに接続して再度SSH
4. ラズパイで602HWa-527CF0に接続しなおす
5. 最初のSSHが復帰

こんな環境を最初からheadlessで構築していきます！
(実際は手間取って画面つないでごちゃごちゃやってしまいましたが…)

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378557/45bf4606-bc3a-861f-e20b-9496f30e00cb.gif)

使用機器

- Raspberry Pi 3 B+
- Wi-Fi
	- 602HW(Pocket WiFi)
	- arrows RX(楽天モバイル)

## SDカード作成 〜 初回起動・ssh接続

ここらへんは多くの情報があるので、参考ドキュメントと合わせてさらっと。

1. OSイメージを書き込む。
	- [Installing operating system images \- Raspberry Pi Documentation](https://www.raspberrypi.org/documentation/installation/installing-images/)
2. ssh接続を有効にするため、空の`ssh`ファイルを`boot`パーティションに配置。
	- [SSH \(Secure Shell\) \- Raspberry Pi Documentation](https://www.raspberrypi.org/documentation/remote-access/ssh/README.md)
3. 初回起動時に自動でWi-Fi接続させるため、SSIDとPSKを記述した`wpa_supplicant.conf`ファイルを`boot`パーティションに配置。
	- [Setting up a Raspberry Pi headless \- Raspberry Pi Documentation](https://www.raspberrypi.org/documentation/configuration/wireless/headless.md)
	- PSKは生のパスワードでも`wpa_passphrase`コマンドで生成した文字列でもセキュリティ的に大差なさそう
 
4. ラズパイ起動、3で設定したネットワーク内でのIPアドレスを探してSSH接続する。
	- [IP Address \- Raspberry Pi Documentation](https://www.raspberrypi.org/documentation/remote-access/ip-address.md)
	- ⇑で紹介されてる方法以外に`ip neigh | grep -i 'B8:27:EB'`でもいける。`B8:27:EB`はRaspberry Pi FoundationのOUI。
	- ルーターによってはLAN内の端末同士で通信するために設定が必要。今回使用しているPocket WiFiでは「プライバシーセパレーター」をオフにする。

## NetworkManager 設定
無事ssh接続できたら早速NetworkManagerのインストール・設定を行います。

```bash
sudo apt update
sudo apt upgrade -y
sudo apt install -y network-manager
```

この段階では`dhcpcd`が動いているので、まだネットワークデバイスがNetworkManagerの管理下にありません。(下の`unavailable`)

```
$ nmcli d
DEVICE  TYPE      STATE        CONNECTION 
eth0    ethernet  unavailable  --         
wlan0   wifi      unavailable  --         
lo      loopback  unmanaged    --   
```
> Do not enable the systemd units shipped with the [dhclient](https://www.archlinux.org/packages/?name=dhclient) and [dhcpcd](https://www.archlinux.org/packages/?name=dhcpcd) packages.
> [NetworkManager \- ArchWiki](https://wiki.archlinux.org/index.php/NetworkManager)

続いてconnectionを追加します。今回はメインWi-FiとサブWi-Fiを切り替えようという趣旨なので、メインWi-Fiの優先度を高くします。

- メイン

```bash
sudo nmcli con add ifname wlan0 type wifi \
con-name pokefi \
ssid 602HWa-527CF0 \
wifi-sec.key-mgmt wpa-psk \
wifi-sec.psk yourpassword \
connection.autoconnect-priority 100 \
ipv4.method manual \
ipv4.gateway 10.0.0.1 \
ipv4.addresses 10.0.0.10/24
```

- サブ

```bash
sudo nmcli con add ifname wlan0 type wifi \
con-name rakuten \
ssid arrowsRX_8018 \
wifi-sec.key-mgmt wpa-psk \
wifi-sec.psk yourpassword \
ipv4.method manual \
ipv4.gateway 192.168.43.199 \
ipv4.addresses 192.168.43.10/24
```
オプションやプロパティはman page や以下参照。
[NetworkManager Reference Manual: NetworkManager Reference Manual](https://developer.gnome.org/NetworkManager/stable/index.html)
いくつかピックアップすると、

- `802-11-wireless-security`の代わりに`wifi-sec`の短縮形が利用可能

> nmcli also accepts wifi-sec and wifi strings instead of 802-11-wireless-security. <br>[nmcli: NetworkManager Reference Manual](https://developer.gnome.org/NetworkManager/stable/nmcli.html)

- `wifi-sec.psk`は`wpa_supplicant.conf`ファイルと同様、生のパスワードでも`wpa_passphrase`コマンドで生成した文字列でも可
- `connection.autoconnect-priority`で優先度を指定。デフォルトは0。
	- [connection: NetworkManager Reference Manual](https://developer.gnome.org/NetworkManager/stable/settings-connection.html)

これで設定完了なので、最後に`dhcpcd`を無効化して再起動します。

```bash
sudo systemctl disable dhcpcd
sudo reboot
```
現在のSSHは切断されるので、メインWi-Fiのconnectionで指定したIPアドレス(今回は`10.0.0.10`)で接続できたら成功です。

```bash
$ nmcli d
DEVICE  TYPE      STATE        CONNECTION 
wlan0   wifi      connected    pokefi     
eth0    ethernet  unavailable  --         
lo      loopback  unmanaged    --         
$ nmcli c
NAME                UUID                                  TYPE      DEVICE 
pokefi              ae7b29bc-4d0f-4ee2-8bd7-b329eafd40d2  wifi      wlan0  
rakuten             e72eed56-4eb7-4557-a150-9cb15f423992  wifi      --     
Wired connection 1  5c64ba5e-9947-3472-9567-99ed0168eb8e  ethernet  -- 
```
お疲れ様でした。

## Android端末テザリングのDHCPについて

1. Wi-Fiテザリングでは`192.168.43.0 / 24`となる(変更不可)
	- [AndroidでWIFIテザリング（ホットスポット）のDHCP設定を構成する方法](https://qastack.jp/android/46499/how-configure-the-dhcp-settings-of-wifi-tethering-hotspot-on-android)
1. gatewayのIPアドレスは最近のバージョンでランダムになったらしい(変更不可)

上記2.については、テザリングをオンオフすると変わってしまいます。
今回サブWi-Fiとして使うarrowsRXは常に給電しつつテザリングオンの状態なのでそうそう変わらないとは思いますが、ちょっと懸念は残る設定になってしまいました。(いつの間にかgatewayが変わったことに気づかず、「なぜか繋がらない！？」となりそう…)

余談ですが、上述のサブWi-Fiの設定で`ipv4.gateway`を除いて設定してみました。すると`curl`コマンドでURLを指定すると通信するのにIPアドレスを指定すると失敗するという、よく分からない挙動を見せました。
これはこれでハマりポイントになりそうなので、gatewayはちゃんと設定して変わったら都度都度変更するという運用にしようと思います。

## あとがき
**速度制限が辛い！**　というのが今回のきっかけでした。
通信はもっぱらPocket WiFi使ってましたが、こいつが一日に3GB通信すると死んでしまったり、勝手に何度も再起動したりする曲者でして。
緊急時はスマホテザリングでしのいだりしてましたが、こちらも格安SIMなのであまり使いたくない…。
そんな折、なんとなく楽天モバイルを契約したので、こいつをサブWi-Fiとして使おうと思い立ち、ラズパイでも任意のタイミングでWi-Fi接続を切り替えられるようにしよう！という経緯です。

自宅、職場・学校などでのWi-Fiをそれぞれ設定するだけであれば`wpa_supplicant.conf`に追記していけばいいのですが、同時に複数Wi-Fiが利用可能な状態で切り替える方法についての情報があまりなかったので、ご参考になれば幸いです。

ちなみに楽天モバイルの通信状況はいまのところいい感じです😁