nerves_pack（vintage_net含む）を使ってNervesのネットワーク設定をした〜SSHログインまで〜

# 概要
Nervesを使ってネットワークの設定をしようとすると設定方法が分かりづらかったり不安定だったりと多少なりとも不満がありました。やはり同じようなことを思っている人たちがいたようでNervesのネットワークまわりを再設計するぜ〜って気持ちで作ったのが`vintage_net`だと思ってます。…きっと。

SSHログインまでなら`vintage_net`だけでもいいんですが次の特徴を持った`nerves_pack`というパッケージがあったのでこちらを使って設定してみました

- `nerves_pack`は`nerves_init_gadget`のかわりになるため同時に利用はできません
- プロジェクト作成時のデフォルトは`nerves_init_gadget`になるので、プロジェクト作成時に`nerves_pack`を使うように明示的に引数を設定します（後述）
- ネットワークの設定
 - `vintage_net`を利用
- mDNSの設定
 - `MdnsLite`を利用
- SSH拡張
 - disableできる
 - 待受ポート番号を変更できる
 - upload.shはこれまでどおり利用可

これからしばらく`nerves_pack`シリーズになると思います :smile:


# 環境
ハードウェアとネットワークはこのような構成です。
今回はホスト名flamboise03を置き換えることにします。

## ハードウェア
- Mac OSX
- NervesをRaspberryPi 3で稼働

## ネットワーク
- Mac OSX
  - 有線LAN→メンテナンス用、固定IPアドレス（192.168.5.100/24）

- RaspberryPi 3(flamboise03)
  - 有線LAN（eth0）→メンテナンス用、固定IPアドレス（192.168.5.57/24）
  - 無線LAN（wlan0）→インターネット接続用、DHCP（192.168.46/24から自動割当）
![ネットワーク構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51020/95e29daf-01e9-ac3b-3e5e-3d4448db4f19.png)



# プロジェクト作成

以下のコマンドを入力してNervesプロジェクトを作成します。今回は`vintagenet003`というプロジェクト名にしました。
`--nerves-pack`の引数を忘れると作り直しなので注意してください！

``` shell:fish
~> mix nerves.new vintagenet003 --nerves-pack

~> cd vintagenet003
~> mix deps.get
~> mix firmware
```

## `mix firmware`時のエラー
`mix firmware`した際、エラーが発生する場合があります。エラーメッセージをよく読むと入力したらいいコマンドを書いてくれているので実行します。@nishiuchikazuma の場合はbrewで以下の2つのコマンドを入力すると`mix firmware`できるようになりました。

``` shell:fish
~> brew install homebrew/core/make
~> brew install gnu-sed
```



# ネットワーク設定

`config/target.exs`にネットワークの設定を記述します。

- SSHの設定はこれまでどおりでOK
- デフォルトの`config/target.exs`に手を加えた箇所は2点
 - SSH鍵の設定　`config: :nerves_firmware_ssh`
 - ネットワークの設定　`config: :vintaget_net`の"eth0"と"wlan0"

``` elixir:config/target.exs（コメントをのぞく）
import Config

config :shoehorn,
  init: [:nerves_runtime, :nerves_pack],
  app: Mix.Project.config()[:app]

config :nerves_runtime, :kernel, use_system_registry: false

config :nerves_firmware_ssh,
  authorized_keys: [
    "公開鍵A",
    "公開鍵B"
  ]

config :vintage_net,
  regulatory_domain: "JP",
  config: [
    {"usb0", %{type: VintageNetDirect}},
    {"eth0",
     %{
       type: VintageNetEthernet,
       ipv4: %{
        method: :static,
        address: "192.168.5.57",
        prefix_length: 24,
       }
      }
    },
    {"wlan0", %{type: VintageNetWiFi,
      vintage_net_wifi: %{
        networks: [%{key_mgmt: :wpa_psk, ssid: "SSIDをここに書く", psk: "共通鍵をここに書く"}]
      },
      ipv4: %{method: :dhcp}
      }
    }
  ]

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

  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
    }
  ]
```


# 確認
`mix firmware.burn`を実行してSDカードにイメージを焼いてSSHログインできるかどうか確認します。

``` shell:fish
~> mix firmware.burn

~> ping 192.168.5.57
~> ssh 192.168.5.57
```

## `VintageNet.info()`と`ifconfig`
ネットワーク設定の確認にはこのようなコマンドがあります。

### `ifconfig`
IPアドレス、ネットマスク、MACアドレスを確認するコマンド。DHCPで割り当てられたIPアドレスをサクッと確認するのに便利。

```elixir
iex> ifconfig
lo: flags=[:up, :loopback, :running]
    inet 127.0.0.1  netmask 255.0.0.0
    inet ::1  netmask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
    hwaddr 00:00:00:00:00:00

eth0: flags=[:up, :broadcast, :running, :multicast]
    inet 192.168.5.57  netmask 255.255.255.0  broadcast 192.168.5.57
    inet fe80::ba27:ebff:fef1:6bfc  netmask ffff:ffff:ffff:ffff::
    hwaddr b8:27:eb:f1:6b:fc

wlan0: flags=[:up, :broadcast, :running, :multicast]
    inet 192.168.46.28  netmask 255.255.255.0  broadcast 192.168.46.255
    inet 2001:a5b5:3fff:3e7:ba27:ebff:fea4:3ea9  netmask ffff:ffff:ffff:ffff::
    inet fe80::ba27:ebff:fea4:3ea9  netmask ffff:ffff:ffff:ffff::
    hwaddr b8:27:eb:a4:3e:a9
```

### `VintageNet.info/0`

各インタフェースの状態を取得するコマンド。

```elixir
iex> VintageNet.info          
VintageNet 0.7.5

All interfaces:       ["eth0", "lo", "wlan0"]
Available interfaces: ["wlan0"]

Interface eth0
  Type: VintageNetEthernet
  Present: true
  State: :configured
  Connection: :lan
  Configuration:
    %{
      ipv4: %{address: {192, 168, 5, 57}, method: :static, prefix_length: 24},
      type: VintageNetEthernet
    }

Interface usb0
  Type: VintageNetDirect
  Present: nil
  State: :retrying
  Connection: :disconnected
  Configuration:
    %{type: VintageNetDirect, vintage_net_direct: %{}}

Interface wlan0
  Type: VintageNetWiFi
  Present: true
  State: :configured
  Connection: :internet
  Configuration:
    %{
      ipv4: %{method: :dhcp},
      type: VintageNetWiFi,
      vintage_net_wifi: %{
        networks: [
          %{
            key_mgmt: :wpa_psk,
            mode: :infrastructure,
            psk: "....",
            ssid: "SSIDがここに表示される"
          }
        ]
      }
    }
:ok
```


### `VintageNet.IP.subnet_mask_to_prefix_length/1`　おまけ
サブネットマスクを計算する関数。

```elixir
iex> VintageNet.IP.subnet_mask_to_prefix_length({255, 255, 255, 248})
{:ok, 29}
``` 


### `VintageNet.get_by_prefix/1` おまけ
ネットワーク関係の情報を表示する関数。

- ルーティングテーブルは表示しない
- inet6って文字が見えるのでIPv6対応がすすんでそう
- インタフェースのIPアドレスを取得したい時に利用できる

```elixir
iex> VintageNet.get_by_prefix([])
[
  {["available_interfaces"], ["wlan0"]},
  {["connection"], :internet},
  {["interface", "eth0", "addresses"],
   [
     %{
       address: {192, 168, 5, 55},
       family: :inet,
       netmask: {255, 255, 255, 0},
       prefix_length: 24,
       scope: :universe
     }
   ]},
  {["interface", "eth0", "connection"], :lan},
  {["interface", "eth0", "lower_up"], true},
  {["interface", "eth0", "mac_address"], "b8:27:eb:f3:62:d3"},
  {["interface", "eth0", "present"], true},
  {["interface", "eth0", "state"], :configured},
  {["interface", "eth0", "type"], VintageNetEthernet},
  {["interface", "lo", "addresses"],
   [
     %{
       address: {0, 0, 0, 0, 0, 0, 0, 1},
       family: :inet6,
       netmask: {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535},
       prefix_length: 128,
       scope: :host
     },
     %{
       address: {127, 0, 0, 1},
       family: :inet,
       netmask: {255, 0, 0, 0},
       prefix_length: 8,
       scope: :host
     }
   ]},
  {["interface", "lo", "lower_up"], true},
  {["interface", "lo", "mac_address"], "00:00:00:00:00:00"},
  {["interface", "lo", "present"], true},
  {["interface", "usb0", "connection"], :disconnected},
  {["interface", "usb0", "state"], :retrying},
  {["interface", "usb0", "type"], VintageNetDirect},
  {["interface", "wlan0", "addresses"],
   [
     %{
       address: {192, 168, 0, 8},
       family: :inet,
       netmask: {255, 255, 255, 0},
       prefix_length: 24,
       scope: :universe
     }
   ]},
  {["interface", "wlan0", "connection"], :internet},
  {["interface", "wlan0", "lower_up"], true},
  {["interface", "wlan0", "mac_address"], "b8:27:eb:a6:37:86"},
  {["interface", "wlan0", "present"], true},
  {["interface", "wlan0", "state"], :configured},
  {["interface", "wlan0", "type"], VintageNetWiFi},
  {["interface", "wlan0", "wifi", "access_points"],
   [
     %VintageNetWiFi.AccessPoint{
       band: :wifi_2_4_ghz,
       bssid: "＊＊＊＊＊＊＊＊＊＊＊＊",
       channel: 1,
       flags: [:wpa2_psk_ccmp, :wps, :ess],
       frequency: 2412,
       signal_dbm: -62,
       signal_percent: 63,
       ssid: "＊＊＊＊＊＊＊＊＊＊＊＊"
     },
     %VintageNetWiFi.AccessPoint{
       band: :wifi_2_4_ghz,
       bssid: "＊＊＊＊＊＊＊＊＊＊＊＊",
       channel: 3,
       flags: [:wpa2_psk_ccmp, :wps, :ess],
       frequency: 2422,
       signal_dbm: -63,
       signal_percent: 61,
       ssid: "＊＊＊＊＊＊＊＊＊＊＊＊"
     },
     %VintageNetWiFi.AccessPoint{
       band: :wifi_5_ghz,
       bssid: "＊＊＊＊＊＊＊＊＊＊＊＊",
       channel: 44,
       flags: [:wpa2_psk_ccmp, :wps, :ess],
       frequency: 5220,
       signal_dbm: -86,
       signal_percent: 31,
       ssid: "＊＊＊＊＊＊＊＊＊＊＊＊"
     }
     ………野良APをたくさん表示………
   ]},
  {["interface", "wlan0", "wifi", "clients"], []},
  {["interface", "wlan0", "wifi", "current_ap"],
   %VintageNetWiFi.AccessPoint{
     band: :wifi_2_4_ghz,
     bssid: "00:3a:9d:d0:09:02",
     channel: 1,
     flags: [:wpa2_psk_ccmp, :wps, :ess],
     frequency: 2412,
     signal_dbm: -62,
     signal_percent: 63,
     ssid: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>
   }}
]
```



# まとめ
`vintaget_net`を含んだ`nerves_pack`を使ってSSHログインまでできるようになりました！


# 参考URL
- [nerves-project / nerves_pack](https://github.com/nerves-project/nerves_pack)
- [nerves-networking / vintage_net](https://github.com/nerves-networking/vintage_net)
- [nerves-project / nerves_bootstrap #mix nerves.new](https://github.com/nerves-project/nerves_bootstrap#mix-nervesnew)