Raspberry Pi Zero の USB Ether を Network Manager でつなぐ

[SabreberryDAC ZERO](http://nw-electric.way-nifty.com/blog/sbdaczero.html) と Raspberry Pi ZERO (以下 RPi Zero)とモバイル・バッテリーの組み合わせでポータブル音楽プレーヤーを作って楽しんでいるが、RPi Zero にはLANコネクターが無いので、音源ファイルを入れ替えたりするのに不便だ。
RPi Zero に無線LANの付いたバージョンも発売予定だが、技適マークの関係なのか未だ [2017/07/07] に発売になっていない。

RPi Zero には USB ケーブルでホストPCとネットワーク接続できるようにする機能があるので、これを有効にして音源ファイルの入れ替えやプレイリストの編集などはホストPCから行えるような環境を整えることにした。

ホストPCとして MacBook や Windows を使う手順は他でも紹介されているようだが、私の気に入っている ubuntu MATE 16.04 LTS に接続するには少し手間がかかるようなので、Network Manager の勉強も兼ねて書いておくことにする。

ちなみに、spotify や radiotunes などのネットワーク・ストリーム音楽プレーヤーとして使う場合は、RPi 2 なり RPi 3 の有線LANを使った構成に変えたほうがいいだろう。ホストPCをずっと動かしておくという選択肢もあるが、その場合はホストPCにMPDを入れるなどすべきだ。 

## 軽量 MPD ディストリビューション

SabreberryDAC ZERO専用の Raspbian ディストリビューションを入れるのが楽なので、[配布の案内](http://nw-electric.way-nifty.com/blog/2017/07/mpd-4432.html) に従ってダウンロードして 7zip で展開しておく。

SDカードへの書き込みは ubuntu 等の Linux 機でもできるのだが、SDカードスロットの関係で Windows 10 PC に Win32DiskImager を使って書き込むことにした。

>ちなみに、Windows へのこういったユーティリティ的なソフトのインストールは、個別に対応すると、インストール手順がそれぞれ異なっていたりアド・ウエアが付いていたりと気を使うので、[Chocolatey](https://chocolatey.org/) というパッケージ・マネージャーのような仕組みを使ってインストールすると、更新があっても楽に対応できる。Win32DiskImager は [Chocolatey Gallery | Win32 Disk Imager](https://chocolatey.org/packages/win32diskimager) にあるコマンドを PowerShell のプロンプトから入れればインストールできる。

Disk Image の書き込みと初回の起動は特別なことは何もない。
可能であれば HDMI 端子にLCDモニターを繋いで起動時の様子を見ても構わないが、 軽量 MPD ディストリビューションは、Raspbian Lite ベースなのでデスクトップ環境が入っておらず、何もすることはない。

SabreberryDAC ZEROのSTOPボタンを2秒以上押し続けるとシャットダウンが始まる。これは RPi Zero の基板上にある LED が点滅から消灯することで確認できる。

## USB Ether gadget による ssh 接続

[Raspberry Pi ZeroをUSBケーブル1本で接続](http://qiita.com/longroof/items/fdae51c3189651bb9c1a) や [[Linux] USB Ether gadget(g_ether) の使い方等](http://qiita.com/chromabox/items/d4368ccebb0f8822ce74) にあるように、/boot/cmdline.txt と /boot/config.txt に設定を加えることで、ホストPCと接続した際に Ethernet アダプタとして検出されて、ネットの先に RPi Zero がつながっているような形でアクセスできる。

この時に使う USB ケーブルには注意する必要がある。標準 USB(A) とマイクロ USB のケーブルだが、充電用に電源しか配線されていないものが多く出回っており、USB Ether gadget ではデータ線まで結線されたものでないと使えない。

RPi Zeroには電源用のUSBコネクタとインターフェース用のUSBのコネクタがあるが、USB Ether gadget の接続には電源を別に繋いでおく必要はなく、インターフェース用の1本だけで電源も供給することができる。

SDカードは /boot の部分が FAT になっているので、cmdline.txt や config.txt の修正は Windows PCからでもできるようになっている。

cmdline.txt は 1行のテキストになっていて、rootwait というような記述で終わっていると思うが、その行の最後に続けて以下のように書き足す。

```
modules-load=dwc2,g_ether g_ether.host_addr=a6:34:44:45:00:2c g_ether.dev_addr=3e:b9:c9:06:f4:4d
```
mac address は [Random Gegerator](http://www.miniwebtool.com/mac-address-generator/) のようなサイトで2つ作ってそれを使っても構わない。省略すると毎回ランダムな値で動作するようだが、後述のインターフェース名が起動ごとに変わったり、Network Manager のコネクション名が際限なく増えていったりと、何度も接続すると問題になるので適当な値で固定しておく方が良いようだ。

> ちなみに [MAC address - Wikipedia](https://en.wikipedia.org/wiki/MAC_address) の図が分かりやすいが、
1st octet の b1 が 1 になっているアドレス (*2,*6,*e で始まるアドレス) であれば、locally administered となっているので、IPアドレスの 192.168.xx.xx と同じように同一セグメント内に重複がなければ使って良いと思われる。

config.txt には、`dtoverlay=dwc2` という記述を最後の行に加える。config.txt は、初期状態だとほとんどがコメントになっており、MPDとで必要な記述は以下のみで構わない。

```
dtoverlay=hifiberry-dac
dtoverlay=dwc2
```

## インターフェース名の固定

ここまでの設定で、USBケーブルを ubuntu の入ったホストPCにつなぐと Network Manager で "有線接続 2" というような接続名で認識されるようになるが、インターフェース名が、enp0s29f0u1 というようにホストPCの環境によって異なる名前になってしまい、スクリプトなどを書く場合に扱いにくいので、udev の仕組みで名前を固定することにする。

ホストPC側に /etc/udev/rules.d/99-ether-gadget-name.rules というファイルを作成し、内容を以下のようにする。

```
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="a6:34:44:45:00:2c", NAME="rpiz-usb-ether0"
```

mac address は先の cmdline.txt の g_ether.host_addr に設定したものを設定する。
複数台の RPi Zero をUSBハブを介して繋ぐようなことがあれば、別の mac address を設定して NAME="rpiz-usb-ether1" といったように続けていけばよい。

## Network Manager の準備

Network Manager は通常 GUI の設定画面があるのだが、nmcli というコマンドライン・インターフェースも用意されていて、これで説明する方が確実である。

```
nmcli con add type ethernet con-name rpiz-usb0 ifname rpiz-usb-ether0
nmcli con modify rpiz-usb0 ipv4.method shared
```
何をやっているかというと、rpiz-usb0 という接続名を作って IPv4設定の方式を「他のコンピューターへ共有」としている。

これをやらずに USBケーブルを繋ぐと、接続名が "有線接続 2" というような名前になり、IPv4設定の方式は「自動 (DHCP)」となっているので、ホストPCとRPi Zeroとの間にはDHCPサーバーが存在しないため、いつまでたっても繋がらないことになる。ここを「ローカルへのリンク専用」に切り替えれば繋がるが、その場合 RPi Zero からホストPCを経由してインターネットにアクセスすることができなくなる。

nmcli の ipv4.method で「ローカルへのリンク専用」を指定するには link-local とすればよい。この状態でホストPCの有線接続とブリッジを組むことで RPi Zero もインターネットに接続できるようにすることも可能だ。これは市販の WiFiルーターでルーター・モードとブリッジ・モードの切り替えをホストPCでやっているようなものだが、ホストPCのインターネット接続が無線LANの場合は、USB Ether gadget とブリッジを組むことができない、という制限がある。

## mDNS による ssh 接続

/etc/udev/rules.d/99-ether-gadget-name.rules を準備して、nmcli で接続名の準備が整ったら、RPi Zero を USB で繋ぐ。しばらくすると Network Manager から「rpiz-usb0 が接続されました」といったようなポップアップが出るので、指定のインターフェース名になっているか確認する。

```
$ ls -l /sys/class/net
合計 0
lrwxrwxrwx 1 root root 0  7月  5 11:55 enp6s0 -> ../../devices/pci0000:00/0000:00:1c.1/0000:06:00.0/net/enp6s0
lrwxrwxrwx 1 root root 0  7月  5 11:55 lo -> ../../devices/virtual/net/lo
lrwxrwxrwx 1 root root 0  7月  7 16:41 rpiz-usb-ether0 -> ../../devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/net/rpiz-usb-ether0
$ netstat -nr
カーネルIP経路テーブル
受信先サイト    ゲートウェイ    ネットマスク   フラグ   MSS Window  irtt インタフェース
0.0.0.0         192.168.50.6    0.0.0.0         UG        0 0          0 enp6s0
10.42.0.0       0.0.0.0         255.255.255.0   U         0 0          0 rpiz-usb-ether0
169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 enp6s0
192.168.50.0    0.0.0.0         255.255.255.0   U         0 0          0 enp6s0
```

rpiz-usb-ether0 が出来ているようなら `ping raspberrypi.local` とすれば RPi Zero から応答がある。IPアドレスを調べなくても名前で繋がるのは、RPi Zero 側に mDNS の実装である avahi がインストールされているのと、ホストPCの ubuntu MATE にも標準で mDNS による名前解決をサポートしているからである。macOS では元祖 bonjour があるので問題ないが、Windows PC の場合は iTunes を入れるなどする必要がある。

ping に問題がなければ ssh pi@raspberrypi.local パスワード raspberry でつないでから、必ず passwd コマンドでパスワードを変えておく。

> ちなみに、avahi は IPv6 もサポートしているので IPv6 のリンクローカル・アドレスだけでつながっても良さそうなものだが、うまくいかないようだ。

## パーティションの拡大

[Raspiが起動時に自動的にパーティションを拡大するのを潰す](http://d.hatena.ne.jp/kinneko/20170703/p1)
にあるように、最近のディストリビューションでは自動的にパーティションを拡大してくれると思っていた。

軽量 MPD ディストリビューション の配布サイトにある 2017-06-21-raspbian-mpdplay2.7z というSDカード・イメージの起動前の cmdline.txt には、以下のような項目がある。

```
quiet init=/usr/lib/raspi-config/init_resize.sh
```

一度起動すれば上記の項目は消されてしまうのだが、なぜかパーティションは拡大されていない。
[SDカード・イメージ配布ページの説明](http://nw-electric.way-nifty.com/blog/2017/07/mpd-4432.html) でもそう書いてあるが、ssh で入って手動で raspi-config を起動して Advanced Option : Expand Filesystem を実行して再起動する必要があった。

## その他の設定

1. pi ユーザーのパスワード変更。初期値 raspberry のままになっていると乗っ取られる恐れがある
2. タイムゾーンの変更。raspi-config でも変更できる。日本にしておいた方が無難
<pre>ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
echo "Asia/Tokyo" >/etc/timezone</pre>
3. ホスト名の変更。複数のRPiをネットにつなぐ場合は、初期値 raspberrypi ではだめなので、/etc/hostname と /etc/hosts にある raspberrypi の名前を適当なものに変更する

## MPD と SabreberryDAC ZERO について

ホストPCのブラウザから http://raspberrypi.local/ にアクセスすると ympd のインターフェースにアクセスできる。音源ファイルの転送方法や再生手順は [作者のblog](http://nw-electric.way-nifty.com/blog/2017/07/mpd-4432.html) にある。

ハイレゾにも対応できる高級機でも使われているようなDACチップ (ES9023P) を使い、ヘッドフォンアンプなどにも手を抜かずに設計されていて、価格からは想像もできない音が楽しめている。違いが分かるような音源がなかなか無いのが問題といえば問題だが。

## ブリッジ接続

ホストPC以外のコンピューターから RPi Zero にアクセスしたい場合、ssh の ProxyCommand 等を使って多段接続すればよいので、あえてブリッジでつなぐ必要性も無いと思われるが、勉強がてらやってみたので書いておくことにする。

インターネット接続が enp6s0 というインターフェース名で "有線接続 1" でつながっているとしたら、以下のコマンドでブリッジ接続に変更できる。(実際はシェル・スクリプトなどにしてしまえば確実だが、途中操作があって難しいかもしれない)

1. すでに接続されていたらシャットダウンしてUSBケーブルを取り外す
2. `nmcli con modify rpiz-usb0 ipv4.method link-local` を実行
3. USBケーブルを挿し直す
4. rpiz-usb0 がオンラインになったら下記を実行  
<pre>nmcli con down "有線接続 1"
nmcli con down rpiz-usb0
nmcli con add type bridge con-name bridge-br0 ifname br0
nmcli con modify bridge-br0 bridge.stp no
nmcli con add type bridge-slave con-name bridge-slave-enp6s0 ifname enp6s0 master bridge-br0
nmcli con add type bridge-slave con-name bridge-slave-rpiz-usb-ether0 ifname rpiz-usb-ether0 master bridge-br0`</pre>

元に戻す手順は以下になる。

1. 以下を実行  
<pre>nmcli con down bridge-br0
nmcli con del bridge-slave-enp6s0
nmcli con del bridge-slave-rpiz-usb-ether0
nmcli con del bridge-br0
nmcli con up "有線接続 1"
nmcli con up rpiz-usb0</pre>
2. RPi-ZeroをシャットダウンしてUSBケーブルを取り外す
3. `nmcli con modify rpiz-usb0 ipv4.method shared` を実行

いちいち RPi Zero のシャットダウンが必要なのは、リンクローカルのアドレス以外をDHCPで割り当てられると戻らなくなるからで、何か良い方法があるのかもしれない。
また、インターネット接続が有線LANの場合でしかブリッジに設定できないので注意する。