Elixirでネットマスクをプレフィックス長に変換

[Nerves]: https://hexdocs.pm/nerves
[Elixir]: https://elixir-lang.org/
[Erlang]: https://www.erlang.org/
[IEx]: https://elixir-lang.org/getting-started/introduction.html#interactive-mode
[アドレスプリフィックス]: https://www.nic.ad.jp/ja/basics/terms/address-prefix.html
[IPv4アドレス]: https://ja.wikipedia.org/wiki/IPv4
[IPアドレス]: https://ja.wikipedia.org/wiki/IP%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9
[Classless Inter-Domain Routing]: https://ja.wikipedia.org/wiki/Classless_Inter-Domain_Routing

## IPアドレス

> [IPアドレスは、IPネットワーク上の情報機器を識別するために指定するネットワーク層における識別用の番号である。データリンク層のMACアドレスを物理アドレスということに対応して、論理アドレスとも呼ばれる。IPのバージョン（IPv4とIPv6）に応じて、IPv4のIPアドレス（IPv4アドレス）とIPv6のIPアドレス（IPv6アドレス）がある。][IPアドレス]

IPネットワーク上の情報機器に割り当てられるインターネット上の住所のようなもの。

## [アドレスプリフィックス]

IPアドレスは前から何ビット目かまでの部分と残りの部分の2つに分割されます。
- [アドレスプリフィックス]（ネットワークアドレス）
- ホスト識別子 （ホストアドレス）

## ネットマスク

ネットマスクにより、前から何ビット目までがネットワークのアドレスを表すかを示します。

ネットマスクが`255.0.0.0` (二進数で`11111111 00000000 00000000 00000000`)の場合、プレフィックス長は8ビット。

ネットマスクが`255.255.0.0` (二進数で`11111111 11111111 00000000 00000000`)の場合、プレフィックス長は16ビット。

ネットマスクが`255.255.255.0` (二進数で`11111111 11111111 11111111 00000000`)の場合、プレフィックス長は24ビット。

## [CIDR記法][Classless Inter-Domain Routing]

[IPアドレス]は、プレフィックス長と一緒に「`a.b.c.d/プレフィックス長`」と表記されることがあります。(例、`192.168.1.3/24`)

ですので、ネットマスクをプレフィックス長に変換したい場合もあるでしょう。

## [Elixir]でネットマスクをプレフィックス長に変換

[Elixir]のIoTフレームワークである[Nerves]でネットマスクをプレフィックス長に変換する面白いコードがあるのでご紹介いたします。

https://github.com/nerves-project/nerves_motd/blob/ff87501ec66c3a684755d083e7bcb77df707d644/lib/nerves_motd/utils.ex#L74-L82

「ネットマスクをプレフィックス長に変換する」というのは、単に前から二進数の`1`の数を数えるだけです。

早速[IEx]を開きます。

```
iex
```

以下のモジュールをiexに貼り付けます。

```elixir
defmodule MyIp do
  def leading_ones(ip_binary, sum \\ 0)
  def leading_ones(<<0b11111111, rest::binary>>, sum), do: leading_ones(rest, sum + 8)
  def leading_ones(<<0b11111110, _rest::binary>>, sum), do: sum + 7
  def leading_ones(<<0b11111100, _rest::binary>>, sum), do: sum + 6
  def leading_ones(<<0b11111000, _rest::binary>>, sum), do: sum + 5
  def leading_ones(<<0b11110000, _rest::binary>>, sum), do: sum + 4
  def leading_ones(<<0b11100000, _rest::binary>>, sum), do: sum + 3
  def leading_ones(<<0b11000000, _rest::binary>>, sum), do: sum + 2
  def leading_ones(<<0b10000000, _rest::binary>>, sum), do: sum + 1
  def leading_ones(_, sum), do: sum
end
```

遊びます。

```elixir
<<255, 255, 255, 255>> |> MyIp.leading_ones()
<<255, 255, 255, 254>> |> MyIp.leading_ones()
<<255, 255, 255, 252>> |> MyIp.leading_ones()
<<255, 255, 255, 248>> |> MyIp.leading_ones()
<<255, 255, 255, 240>> |> MyIp.leading_ones()
<<255, 255, 255, 224>> |> MyIp.leading_ones()
<<255, 255, 255, 192>> |> MyIp.leading_ones()
<<255, 255, 255, 128>> |> MyIp.leading_ones()
<<255, 255, 255, 0>> |> MyIp.leading_ones()
<<255, 255, 254, 0>> |> MyIp.leading_ones()
<<255, 255, 252, 0>> |> MyIp.leading_ones()
<<255, 255, 248, 0>> |> MyIp.leading_ones()
<<255, 255, 240, 0>> |> MyIp.leading_ones()
<<255, 255, 224, 0>> |> MyIp.leading_ones()
<<255, 255, 192, 0>> |> MyIp.leading_ones()
<<255, 255, 128, 0>> |> MyIp.leading_ones()
<<255, 255, 0, 0>> |> MyIp.leading_ones()
<<255, 254, 0, 0>> |> MyIp.leading_ones()
<<255, 252, 0, 0>> |> MyIp.leading_ones()
<<255, 248, 0, 0>> |> MyIp.leading_ones()
<<255, 240, 0, 0>> |> MyIp.leading_ones()
<<255, 224, 0, 0>> |> MyIp.leading_ones()
<<255, 192, 0, 0>> |> MyIp.leading_ones()
<<255, 128, 0, 0>> |> MyIp.leading_ones()
<<255, 0, 0, 0>> |> MyIp.leading_ones()
<<254, 0, 0, 0>> |> MyIp.leading_ones()
<<252, 0, 0, 0>> |> MyIp.leading_ones()
<<248, 0, 0, 0>> |> MyIp.leading_ones()
<<240, 0, 0, 0>> |> MyIp.leading_ones()
<<224, 0, 0, 0>> |> MyIp.leading_ones()
<<192, 0, 0, 0>> |> MyIp.leading_ones()
<<128, 0, 0, 0>> |> MyIp.leading_ones()
```

:tada:

いろんなやり方があると思いますが、ここではバイナリデータパターンマッチが利用されています。

関数使用者のインプットを信用すれば単にリスト全体の`1`数えるだけでも十分かもしれませんが、プレフィックス（接頭辞）という意味を明示するために律儀に前から数えた方が良いのでしょう。

```elixir
for <<bit::1 <- <<255, 128, 0, 0>> >>, reduce: 0 do
  count ->
    case bit do
      1 -> count + 1
      0 -> count
    end
end
```

Elixirのバイナリデータパターンマッチについては以下の記事の解説がおすすめです。

https://qiita.com/the_haigo/items/68f52a6f9623d2da7d26



## ご参考までに

https://qiita.com/piacerex/items/e0b6e46b1325bb931122

https://qiita.com/torifukukaiou/items/1edb3e961acf002478fd

https://qiita.com/piacerex/items/e5590fa287d3c89eeebf