Raspberry Pi Zero に USB ケーブル経由で ssh 接続する

無印の Raspberry Pi Zero は Zero W/WH などと違って無線 LAN 経由での接続ができません。しかし、Zero W/WH も同じですが、USB OTG 経由での ssh 接続が可能です。USB ケーブル 1 本での接続が可能であるため特に無線 LAN 接続ができない Raspberry Pi Zero を使い電子工作などで遊んだりする際に便利な機能です。

Windows/macOS だとそのまま ```ssh pi@raspberrypi.local``` で接続可能なのですが一部の Linux ディストリビューション (私が試したのは Kali Linux) だとひと手間必要だったのでその際のメモです。

# Raspberry Pi Zero の設定

[ここ](https://qiita.com/Liesegang/items/dcdc669f80d1bf721c21) とか[ここ](https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget/ethernet-gadget) が詳しいです。ちなみに私の手元の Windows ではドライバのインストールも必要ありませんでした。

https://qiita.com/Liesegang/items/dcdc669f80d1bf721c21

基本的には config.txt の最後に dtoverlay=dwc2 を追加し、 

``` config.txt
[all]
#dtoverlay=vc4-fkms-v3d
dtoverlay=dwc2
```

cmdline.txt の rootwait の後に modules-load=dwc2,g_ether を追加した後に Raspberry Pi Zero を起動するだけです。Raspberry Pi Zero への ssh 接続を許可するには ssh.txt という空のファイルも作ります。

``` cmdline.txt
console=serial0,115200 console=tty1 root=PARTUUID=334dade7-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet init=/usr/lib/raspi-config/init_resize.sh splash plymouth.ignore-serial-consoles modules-load=dwc2,g_ether
```

[ここ](https://gist.github.com/gbaman/50b6cca61dd1c3f88f41) とか [ここ](https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget/serial-gadget) とか見るとシリアル接続などもできるようです。

# ssh を実行する Linux 側の設定

私が追加で設定が必要だった箇所ですが 2 点ありました。

## IP アドレスの確認

まず１点目は Linux 側で IP アドレスが取れているか確認をする必要があります。Raspberry Pi Zero の PWR IN では無く USB ポートを Linux と接続するとそこから給電して起動します。期待する動作としては usb0 などに IPv4 アドレスが割り振られることですが Kali Linux のデフォルトの状態では以下のように IP アドレスが割り振られません。

```
┌──(test㉿kali)-[/]
└─$ ip addr
4: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 82:d4:2c:aa:ba:a3 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::114a:d7cf:d319:98dc/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
```

/var/log/syslog を見るとそのうち Timeout で DHCPv4 経由でのアドレスの取得を諦めています。

```
Mar 30 20:32:47 kali NetworkManager[455]: <warn>  [1617150767.3963] dhcp4 (usb0): request timed out
```

これを解決するにはネットワーク接続を Link-Local Only にする必要があります。GUI からだとネットワークの接続 (nm-connection-editor) から usb0 の接続の設定を開き IPv4 Settings -> Method: Automatic (DHCP) となっているプルダウンメニューを Link-Local Only に変更します。試してないですが nmcli からだと[ここ](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/networking_guide/sec-configuring_ip_networking_with_nmcli) や [ここ](https://www.linuxquestions.org/questions/linux-embedded-and-single-board-computer-78/networkmanager-and-ipv4ll-link-local-4175672529/) あたりでしょうか。

変更後 ip コマンドなどで確認すると以下のように IP アドレス (169.254/16) が割り振られているのが確認できます。Local-Link Only と言うのは [ここ](https://en.wikiversity.org/wiki/Link-local_address/Linux) とか [ここ](https://tools.ietf.org/html/rfc3927) が詳しいようです。

```
┌──(test㉿kali)-[~]
└─$ ip addr                                                                                                                              255 ⨯
<snip>
3: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 9e:9a:92:5e:77:65 brd ff:ff:ff:ff:ff:ff
    inet 169.254.150.242/16 brd 169.254.255.255 scope link noprefixroute usb0
       valid_lft forever preferred_lft forever
    inet6 fe80::114a:d7cf:d319:98dc/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
```

## avahi-daemon の起動

2点目は名前解決ができるかを確認する必要があります。IPv4 アドレスは割り振られたもののディストリビューションによっては ```ssh pi@raspberrypi.local``` と実行しても raspberrypi.local の名前解決ができず以下のようなエラーとなります。

```
┌──(test㉿kali)-[~]
└─$ ssh -l pi raspberrypi.local                                                                                                          130 ⨯
ssh: Could not resolve hostname raspberrypi.local: Name or service not known
```

これは私が試した Kali Linux 上では avahi-daemon がデフォルトで動作していないためでした。

```
┌──(test㉿kali)-[~]
└─$ sudo service avahi-daemon status                                                                                                     255 ⨯
● avahi-daemon.service - Avahi mDNS/DNS-SD Stack
     Loaded: loaded (/lib/systemd/system/avahi-daemon.service; disabled; vendor preset: disabled)
     Active: inactive (dead)
TriggeredBy: ● avahi-daemon.socket
```

インストールはされているため単に起動してあげると、

```
┌──(test㉿kali)-[~]
└─$ sudo service avahi-daemon start                                                                                                        2 ⨯

┌──(test㉿kali)-[~]
└─$ sudo service avahi-daemon status
● avahi-daemon.service - Avahi mDNS/DNS-SD Stack
     Loaded: loaded (/lib/systemd/system/avahi-daemon.service; disabled; vendor preset: disabled)
     Active: active (running) since Tue 2021-03-30 20:08:36 EDT; 8s ago
TriggeredBy: ● avahi-daemon.socket
   Main PID: 1798 (avahi-daemon)
     Status: "avahi-daemon 0.8 starting up."
      Tasks: 2 (limit: 4507)
     Memory: 1.2M
        CPU: 64ms
     CGroup: /system.slice/avahi-daemon.service
             ├─1798 avahi-daemon: running [kali.local]
             └─1800 avahi-daemon: chroot helper
```

無事名前解決ができ ssh 接続ができました。

```
┌──(test㉿kali)-[~]
└─$ ssh pi@raspberrypi.local
Warning: Permanently added the ECDSA host key for IP address '169.254.10.235' to the list of known hosts.
pi@raspberrypi.local's password:
```

Linux 起動時に自動的に avahi-daemon を起動したい場合はこんな感じ。

```
┌──(test㉿kali)-[~]
└─$ sudo systemctl enable avahi-daemon.service
Synchronizing state of avahi-daemon.service with SysV service script with /lib/systemd/systemd-sysv-install.
```

avahi-daemon というのは mDNS/DNS-SD プロトコルを使用してローカルで接続されているサービスを見つけるためのデーモンで要するに Apple Bonjour のことのようです。[ここ](https://www.avahi.org/) とか [ここ](https://tools.ietf.org/html/rfc6762) とか [ここ](https://tools.ietf.org/html/rfc6763) が詳しいようです。

Widnows もRaspberry Pi Zero に USB ケーブル経由で ssh 接続するには Bonjour のインストールが必要と書かれている資料もありどうも上記が理由のようです。単純に DHCP 経由で IP アドレスを取っているものと思ってましたが意外と複雑というか新しいプロトコルを使っていましたw