備忘録: WSL2 Ubuntu上で NervesとLAN接続してみる

前回の続き。WSL2 Ubuntu上に Nervesの開発環境が無事セットアップできたものの、NervesとのLAN接続をチェックしていないかった。この備忘録は、そのフォローアップ。

#1.Vintage Netって?
暫く Nervesから遠ざかっていた間に、ネットワーク・ライブラリがごっそりと変わっているではないか!! 以前は  nerves_networkというちょっと小難しいライブラリだったが、[Vintage Net](https://github.com/nerves-networking/vintage_net)という設計が体系的にスッキリしたライブラリになった。ネットワーク・ライブラリの再設計の背景が README.mdにゴニョゴニョと書かれているが、まあそー言うことらしい。

ふむふむ、Myアプリへのインストールは至って簡単:

1. mix.exsの依存モジュールリストに nerves_packが見当たらなければ追加する
2. config/target.exsに "config :vintage_net"の項目を追加し、ネットワークの設定を記述する

と、ここまで書いたが、最近の Nervesツールチェインだと、"mix nerves.new"が一通りの設定を用意してくれるではないか。手ぶらでOKか(^^)v

ええっと、手作業の設定が必要なケースは… 既にある Myアプリを nerves_packから Vintage Netに移行させる場合かな。おや? 12/11/2019以前のシステムだと、Kernelと BusyBoxのリビルドが必要なようだ。憶えておこう。

#2.Wired接続
前回と同じく [nerves_examples](https://github.com/nerves-project/nerves_examples)/blinkyを題材にLAN接続を試してみる。

手始めはLANケーブルによる Wired接続……
LANケーブルで Raspberry Piをルーターに繋いで終わりσ(^_^;)

先にも触れた通り "mix nerves.new"が一通りの設定を済ませてくれているので、Wired接続ならば何も修正する必要がない。

```elixir:config/target.exs
# Configure the network using vintage_net
# See https://github.com/nerves-networking/vintage_net for more information
config :vintage_net,
  regulatory_domain: "JP",  # modify "US"->"JP"
  config: [
    {"usb0", %{type: VintageNetDirect}},
    {"eth0",
     %{
       type: VintageNetEthernet,
       ipv4: %{method: :dhcp}
     }},
    {"wlan0", %{type: VintageNetWiFi}}
  ]

config :mdns_lite,
  # The `host` key specifies what hostnames mdns_lite advertises.  `:hostname`
  # advertises the device's hostname.local. For the official Nerves systems, this
  # is "nerves-<4 digit serial#>.local".  mdns_lite also advertises
  # "nerves.local" for convenience. If more than one Nerves device is on the
  # network, delete "nerves" from the list.

  host: [:hostname, "nerves"],
  ttl: 120,

  # Advertise the following services over mDNS.
  services: [
    %{
      name: "SSH Remote Login Protocol",
      protocol: "ssh",
      transport: "tcp",
      port: 22
    },
    %{
      name: "Secure File Transfer Protocol over SSH",
      protocol: "sftp-ssh",
      transport: "tcp",
      port: 22
    },
    %{
      name: "Erlang Port Mapper Daemon",
      protocol: "epmd",
      transport: "tcp",
      port: 4369
    }
  ]
```
sshでリモートログインして[*謎1]、VintageNet.infoで確かめて見よう。
ふむ、よさそうだ(^_-)
![コメント 2020-07-04 113105.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14158/d141167c-736b-7ad6-1eb3-751d824208c9.png)

おっと、"./upload.h"スクリプトもチェックしておかなければ…
![コメント 2020-07-04 121140.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14158/d7189056-8eae-7cc3-1adb-d371d1c55385.png)

[*謎1]My Windows10 PCでは、どういう訳か WiFi経由の mDNSがときどき機能しなくなる。仕方がないのでそんな時は IP直打ち(T_T)

#3.WiFi接続
次は WiFi接続の試行。"config/targte.exs"の "wlan0"インターフェイスの項目に、下記のように WiFiルーターの ssid/pskを追記するだけだった。

```elixir:config/target.exs
<<略>>
    {"wlan0",
     %{
       type: VintageNetWiFi,
       vintage_net_wifi: %{
         networks: [
           %{
             key_mgmt: :wpa_psk,
             ssid: "BBIQRT-XXXXXX",
             psk: "***********"
           }
         ]
       },
       ipv4: %{method: :dhcp},
     }}
<<略>>
```
う～む、ssid/pskをハードコーディングするなんて設計上どうよと思ってしまうが、Vintage Netになってその解決案が登場した。それが次の Wizard。

#4.WiFi Wizard
Vintage Netには、Nervesを WiFiアクセスポイントとして動作させるモードがある。WiFi接続に必要なssid/pskが未設定の場合、NervesをWiFiアクセスポイントとして立ち上げ、そこに接続したPCのブラウザでssid/pskを設定できるようにしたのが "VintageNet WiFi Configuration Wizard"だ。

VintageNetWizardライブラリは、Wizardが呼び出された後の仕事を一手に引き受けてくれる。しかし、どのようなタイミング/条件で WiFi Wizardを呼び出すかは、アプリケーションの実装に委ねられている。ということで、WiFi Wizardを利用するには、"config/target.exs"の設定と共にアプリ起動部(lib/blinky.ex)の改造が必要になる。

下記の実装例では、

has_wifi? (ターゲット・ボードが WiFiインターフェイスを持っている) 
　かつ {wifi_configured? (WiFiのコンフィグがない) または has_networks? (ネットワークのコンフィグがない)}
　ならば (WiFi Wizardを起動する)

としている。WiFi Wizardが一度実行され、WiFiインターフェイスの設定が正しく終われば、その記録が記憶媒体(SDカード等)に書き込まれるので、次回のシステム起動からは WiFi Wizardを実行しないように仕組まれている。

```elixir:blinky.ex
<<略>>

  @ifname "wlan0"

  def start(_type, _args) do
    maybe_start_wifi_wizard()

    led_list = Application.get_env(:blinky, :led_list)
    Logger.debug("list of leds to blink is #{inspect(led_list)}")
    spawn(fn -> blink_list_forever(led_list) end)
    {:ok, self()}
  end

  defp maybe_start_wifi_wizard() do
    with true <- has_wifi?() || :no_wifi,
         true <- wifi_configured?() || :not_configured,
         true <- has_networks?() || :no_networks do
    else
      :no_wifi ->
        Logger.error(
          "[#{inspect(__MODULE__)}] Device does not support WiFi - Skipping wizard start"
        )
      
      _ ->
        VintageNetWizard.run_wizard(on_exit: {__MODULE__, :on_wizard_exit, []})
    end
  end

  # Does the target have WiFi interface?
  defp has_wifi?() do
    @ifname in VintageNet.all_interfaces()
  end

  # Have the WiFi interface configured?
  defp wifi_configured?() do
    @ifname in VintageNet.configured_interfaces()
  end

  # Is network parameter specified?
  defp has_networks?() do
    VintageNet.get_configuration(@ifname).vintage_net_wifi.networks != []
  end

  def on_wizard_exit() do
    Logger.info("[Blinky] - WiFi Wizard stopped")
  end

<<略>>
```
"config/target.exs"の設定のポイントは、:vintage_net,config: のリストから "wlan0"インターフェイスの設定を全て削除すること。そうすることで、"wlan0"インターフェイスは WiFiアクセス・ポイントとしてコンフィグされる。

```elixir:config/target.exs
config :vintage_net,
  regulatory_domain: "JP",
  config: [
    {"usb0", %{type: VintageNetDirect}},
    {"eth0",
     %{
       type: VintageNetEthernet,
       ipv4: %{
         method: :dhcp
       }
     }}
#    {"wlan0",
#     %{
#       type: VintageNetWiFi,
#       vintage_net_wifi: %{
#         networks: [
#           %{
#             key_mgmt: :wpa_psk,
#             ssid: "BBIQRT-68a447-1",
#             psk: "ed6Ja53K6Lfb5"
#           }
#         ]
#       },
#       ipv4: %{method: :dhcp},
#     }}
  ]

  config :vintage_net_wizard,
    ssid: "hello_blinky",
    dns_name: "hello_blinky.config"
```

さて、Nervesのファームウェアを差し替えて起動してみよう…
PCの WiFiアクセスポイント・リストに新たに"hello_blinky"が追加されるので、このアクセスポイントにPCを接続し直す。するとブラウザが勝手に開き、下の様な画面か表示される。画面で Nervesの繋ぎ先を選び、pskを入力し、[Apply configuration]のボタンを押せば WiFiの設定完了だ。Nervesはその後、WiFiアクセスポイント・モードを抜け、選択した繋ぎ先に改めて WiFi接続する。

![コメント 2020-07-04 123134.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14158/9c4b3bb1-93dc-d166-0cd6-af407aaf80ee.png)

"wlan0"がどのように設定されたかを確かめて見よう。
ふむ、よさそうだ。/root/vintage_netに設定記録も残されている。

![コメント 2020-07-04 203112.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14158/b57f50a3-d26d-5a95-7324-7b96f11f606c.png)

#4. まとめ
結局、Nervesの開発環境としての WSL2のLAN周りの確認と言うよりは、Vintage Netライブラリをあれこれと突くのが楽しくて遊んでしまった感が満載となったorz

本題のLAN周りの確認だが、一点を除きネイティブなUbuntu(Linux Mint)と比べて遜色なしと判断した。

※ 除いた一点は、My Window10 PCは WiFi接続時に mDNSがまともに機能しないことが多いという点だ。この現象は、コマンド・プロンプト、PowerShellでも発生するので、WSL2ではなくWindow10の課題だろう。ファイアウォールを切ってみても、デバイス・ドライバを更新してみても改善なし。Wiresharkでパケットを観察すると、mDNSのクエリ・リクエストがマルチキャストされるもののレスポンスがないと言う状態。ところが、PCをWired接続するとさくさくとmDNSが通るではないか
……謎だ(^^;)


#5. 次回予告
「WSL2 Ubuntu上でNerves」シリーズ(?)は次回が最終回。
Nervesシステムのリビルドの環境として WSL2を突いてみる。