LoginSignup
5
2

More than 5 years have passed since last update.

Elixirでrouting tableを作った

Last updated at Posted at 2017-12-24

Elixirでsoft ware routerを作っているので、今のところできているrouting tableについて実装をメモ的に残します。

動作環境

  • docker container

    • OS: debian 8
  • elixir : 1.5.2

実装

routing tableの構成

name description
id routing tableのレコードの識別
destination 送信先IP
netmask net mask
nexthop 次の宛先。直接つながってる場合は「connected」が入る
interface 送信するinterface
metric メトリック
routeType スタティックかダイナミックか
sourceOfRoute ルーティングプロトコル
routeAge レコードが追加された時間
routeInformation ルーティングの情報
mtu MTU

保存先はETSを使う。データのIOでどれぐらいかかるか検証する必要あるかもしれないですけど、とりあえずETSで。
初期化時に直接接続されているネットワークをrouting tableに追加します。

defmodule Routing do
    def init() do
    table = :ets.new(:routing_table, [:set, :protected, :named_table])
    :interface.get_interfaces()
    |> Enum.each(fn({name, interface}) -> :ets.insert_new(:routing_table, {name,
        {:netmask, Tuple.to_list(interface[:netmask])}, {:dest, Tuple.to_list(interface[:addr])}, {:nexthop, :connected}, name, 0, "static", "link", :calendar.local_time(), "-", 1500}) end)
end

interfaceの取得はErlangで取得してる。interfaceは"eth"がつくものに限定しています。

get_interface() ->
    {ok, IfLists} = inet:getifaddrs(),
    Listen = lists:filter(fun(Elm) -> interface_list(Elm) end, IfLists).

interface_list(Elm) ->
    {Name, _} = Elm,
    string:find(Name, "eth") =/= nomatch.

routing tableから送信先のIPから次に送信する宛先を決定します。

:ets.match(:user_lookup, {:"_", :"$1", :"$2", :"$3", :"_", :"_", :"_", :"_", :"_", :"_", :"_"})
            |> Enum.map(fn(record) -> [{:address, List.zip([dest, record[:netmask], record[:dest]])}, {:nexthop, record[:nexthop]}] end)
            |> Enum.filter(fn(record) -> record[:address]
              |> Enum.all?(fn(octed) -> (elem(octed, 1) &&& elem(octed, 0)) == (elem(octed, 1) &&& elem(octed, 2)) end)
            end)
            |> Enum.map(fn(record) -> [{:dest, (for octed <- record[:address], do: elem(octed, 0))}, {:nexthop, record[:nexthop]}] end)

複雑になってしまったんで、軽く説明を付けておきます。

宛先IPとnetmaskとrouting tableに登録されている宛先のIPでオクテットごとにTupleを作っていきます。

# 例
# 宛先IP [192, 168, 0, 10]
# netmask [255, 255, 255, 0]
# routing tableに登録されている宛先IP [192, 168, 0, 0](直接接続されているnetwork)
# result : [{192, 255, 192}, {168, 255, 168}, {0, 255, 0}, {0, 0, 0}]
|> Enum.map(fn(record) -> [{:address, List.zip([dest, record[:netmask], record[:dest]])}, {:nexthop, record[:nexthop]}] end)

まとめたTupleから宛先とrouting tableに登録されている宛先のIPがnetmaskでマスクした結果が一致しているものを取得

|> Enum.filter(fn(record) -> record[:address]
              |> Enum.all?(fn(octed) -> (elem(octed, 1) &&& elem(octed, 0)) == (elem(octed, 1) &&& elem(octed, 2)) end)
            end)

宛先にマッチしたrouting tableのrecordでrouting tableに登録されている宛先のIPとnexthopを返します。

|> Enum.map(fn(record) -> [{:dest, (for octed <- record[:address], do: elem(octed, 0))}, {:nexthop, record[:nexthop]}] end)

これで宛先が決まりました。まだ実装してないですが、netmaskのロンゲストマッチ、コストでソートができてないため、それは今後実装予定。

とりあえず、これでETSにrouting tableのレコードの追加、検索ができるようになりました。
次は、宛先がARP tableに次に送信するホストの情報があるか調べて、なかった場合はARPのrequestを送って登録処理を実装します。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2