Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ネットワークのことを知るため ソフトウェアルータを 自作した話

Avatar for kobatako kobatako
March 31, 2019

ネットワークのことを知るため ソフトウェアルータを 自作した話

Avatar for kobatako

kobatako

March 31, 2019
Tweet

More Decks by kobatako

Other Decks in Technology

Transcript

  1. ࣗݾ঺հ • ໊લ : খݪ ਸ׮ʢ͜͹Δ ͔ͨͻΖʣ • ॴଐ :

    גࣜձࣾFusic • ࢓ࣄ : PHPɺGolangɺAWS • झຯ : ElixirɺErlangɺΠϯϑϥ͍Ζ͍Ζ • Twitter : kobatako_
  2. ϧʔςΟϯάʹؔ͢Δ΋ͷ • ϧʔςΟϯάςʔϒϧͷྫ ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ AD ϝτϦοΫ ωΫετϗοϓ ग़ྗIF

    C 192.168.0.0 255.255.255.0 0 0 - Eth0 C 192.168.10.0 255.255.255.0 0 0 - Eth1 S 192.168.30.0 255.255.255.0 1 0 192.168.10.1 Eth1 R 192.168.40.0 255.255.255.0 120 1 192.168.0.1 Eth0 R 192.168.40.0 255.255.255.0 120 4 192.168.10.1 Eth1
  3. ܾΊΔͱ͖ͷ༏ઌॱ • ϩϯήετϚονͷྫ • ૹ৴ઌ͕192.168.10.1ʹରͯ͠ϧʔςΟϯάςʔϒϧ͕
 Լهͷ৔߹ • ѼઌωοτϫʔΫ : 192.168.0.0/16

    • ѼઌωοτϫʔΫ : 192.168.10.0/24 
 -> 192.168.10.0ͷѼઌϧʔτΛ࣋ͭϧʔτ৘ใ͕
 ࢀর͞ΕΔ ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ AD ϝτϦοΫ ωΫετϗοϓ ग़ྗIF S 192.168.0.0 255.255.0.0 1 0 192.168.0.1 Eth0 R 192.168.10.0 255.255.255.0 120 0 192.168.10.1 Eth1
  4. ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ AD ϝτϦοΫ ωΫετϗοϓ ग़ྗIF C 192.168.0.0 255.255.255.0

    0 0 - Eth0 C 192.168.10.0 255.255.255.0 0 0 - Eth1 S 192.168.30.0 255.255.255.0 1 0 192.168.10.1 Eth1 R 192.168.40.0 255.255.255.0 120 1 192.168.0.1 Eth0 R 192.168.40.0 255.255.255.0 120 4 192.168.10.1 Eth1 ܾΊΔͱ͖ͷ༏ઌॱ 1. ϩϯήετϚον
  5. ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ AD ϝτϦοΫ ωΫετϗοϓ ग़ྗIF C 192.168.0.0 255.255.255.0

    0 0 - Eth0 C 192.168.10.0 255.255.255.0 0 0 - Eth1 S 192.168.30.0 255.255.255.0 1 0 192.168.10.1 Eth1 R 192.168.40.0 255.255.255.0 120 1 192.168.0.1 Eth0 R 192.168.40.0 255.255.255.0 120 4 192.168.10.1 Eth1 ܾΊΔͱ͖ͷ༏ઌॱ 2. AD஋Λൺֱ
  6. ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ AD ϝτϦοΫ ωΫετϗοϓ ग़ྗIF C 192.168.0.0 255.255.255.0

    0 0 - Eth0 C 192.168.10.0 255.255.255.0 0 0 - Eth1 S 192.168.30.0 255.255.255.0 1 0 192.168.10.1 Eth1 R 192.168.40.0 255.255.255.0 120 1 192.168.0.1 Eth0 R 192.168.40.0 255.255.255.0 120 4 192.168.10.1 Eth1 ܾΊΔͱ͖ͷ༏ઌॱ 3. ϝτϦοΫͷൺֱ
  7. ϧʔςΟϯάॲཧ ૹ৴ઌIP͕ϧʔςΟϯάςʔϒϧͷѼઌϧʔτʹ ౰ͯ͸·ͬͯΔ΋ͷͷϦετΛऔಘ % ୈҰҾ਺ : ϧʔςΟϯάςʔϒϧ಺ͷશϦετʢarrayʣ % ୈೋҾ਺ :

    ѼઌͷIPʢintegerʣ % ୈࡾҾ਺ : ૹ৴ઌީิʢѼઌͱ൑அ͞Εͨʣͷϧʔτ৘ใʢarrayʣ match_dest_ip([], _, List) -> List; match_dest_ip([{_, _, _, Ip, _, Subnetmask, Ad, Metric, Nexthop, _, If}| Tail], DestIp, List) when is_integer(DestIp) -> case DestIp band Subnetmask of Ip -> match_dest_ip(Tail, DestIp, [{If, Nexthop, Subnetmask, Ad, Metric}|List]); _ -> match_dest_ip(Tail, DestIp, List) end.
  8. ϧʔςΟϯάॲཧ % ୈҰҾ਺ : ϧʔςΟϯάςʔϒϧ಺ͷશϦετʢarrayʣ % ୈೋҾ਺ : ѼઌͷIPʢintegerʣ %

    ୈࡾҾ਺ : ૹ৴ઌީิʢѼઌͱ൑அ͞Εͨʣͷϧʔτ৘ใʢarrayʣ match_dest_ip([], _, List) -> List; match_dest_ip([{_, _, _, Ip, _, Subnetmask, Ad, Metric, Nexthop, _, If}| Tail], DestIp, List) when is_integer(DestIp) -> case DestIp band Subnetmask of Ip -> match_dest_ip(Tail, DestIp, [{If, Nexthop, Subnetmask, Ad, Metric}|List]); _ -> match_dest_ip(Tail, DestIp, List) end. 1. ѼઌIPͱαϒωοτϚεΫͷandॲཧ ૹ৴ઌIP͕ϧʔςΟϯάςʔϒϧͷѼઌϧʔτʹ ౰ͯ͸·ͬͯΔ΋ͷͷϦετΛऔಘ
  9. ϧʔςΟϯάॲཧ % ୈҰҾ਺ : ϧʔςΟϯάςʔϒϧ಺ͷશϦετʢarrayʣ % ୈೋҾ਺ : ѼઌͷIPʢintegerʣ %

    ୈࡾҾ਺ : ૹ৴ઌީิʢѼઌͱ൑அ͞Εͨʣͷϧʔτ৘ใʢarrayʣ match_dest_ip([], _, List) -> List; match_dest_ip([{_, _, _, Ip, _, Subnetmask, Ad, Metric, Nexthop, _, If}| Tail], DestIp, List) when is_integer(DestIp) -> case DestIp band Subnetmask of Ip -> match_dest_ip(Tail, DestIp, [{If, Nexthop, Subnetmask, Ad, Metric}|List]); _ -> match_dest_ip(Tail, DestIp, List) end. 2. 1ͷ݁ՌͱIp͕౳͍͔͠Ͳ͏͔ ૹ৴ઌIP͕ϧʔςΟϯάςʔϒϧͷѼઌϧʔτʹ ౰ͯ͸·ͬͯΔ΋ͷͷϦετΛऔಘ
  10. ϧʔςΟϯάॲཧ ѼઌIP͕௚઀ܨ͕͍ͬͯΔωοτϫʔΫʹؚ·Ε͍ͯΔ΋ͷ ϧʔτ৘ใͷѼઌ͕ʮNEXTHOP_DIRECTʯͷ৔߹͸௚઀ܨ͕͍ͬͯΔωοτϫʔΫ಺ʹ
 ଐ͍ͯ͠Δʢ৘ใݯ͕ʮCʯͷ΋ͷʣ AD͕0ͷ৔߹͸௚઀ܨ͕͍ͬͯΔωοτϫʔΫ಺ʹଐ͍ͯ͠Δʢ৘ใݯ͕ʮCʯͷ΋ͷʣ %% fetch to destination route

    % ୈҰҾ਺ : ϧʔτ৘ใʢarrayʣ % ୈೋҾ਺ : ݱࡏͷҰ൪༏ઌ౓͕ߴ͍ϧʔτ৘ใ fetch_dest_route([{_, ?NEXTHOP_DIRECT, _, _, _}=Route| _], _) -> Route; % ad 0 fetch_dest_route([{_, _, _, 0, _}=Route| _], _) -> Route; ௚઀ܨ͕͍ͬͯΔωοτϫʔΫ
  11. ϧʔςΟϯάॲཧ ݱࡏͷϧʔτ৘ใΑΓαϒωοτϚεΫ͕௕͍ϧʔτ৘ใ͕
 ͋Δ৔߹ɺҰ൪༏ઌ౓͕ߴ͍΋ͷͱ͢Δ %% fetch to destination route % ୈҰҾ਺

    : ϧʔτ৘ใʢarrayʣ % ୈೋҾ਺ : ݱࡏͷҰ൪༏ઌ౓͕ߴ͍ϧʔτ৘ใ fetch_dest_route([{_, _, Subnet, _, _}=Route| Tail],{_, _, NowSubnet, _, _}) when Subnet > NowSubnet -> fetch_dest_route(Tail, Route); αϒωοτϚεΫͷൺֱ
  12. ϧʔςΟϯάॲཧ ݱࡏͷϧʔτ৘ใͱൺֱ͠αϒωοτϚεΫ͕౳͘͠
 AD஋͕খ͍͞৔߹Ұ൪༏ઌ౓͕ߴ͍΋ͷͱ͢Δ %% fetch to destination route % ୈҰҾ਺

    : ϧʔτ৘ใʢarrayʣ % ୈೋҾ਺ : ݱࡏͷҰ൪༏ઌ౓͕ߴ͍ϧʔτ৘ใ fetch_dest_route([{_, _, Subnet, Ad, _}=Route| Tail], {_, _, Subnet, NowAd, _}) when Ad < NowAd -> fetch_dest_route(Tail, Route); AD஋Λൺֱ
  13. ϧʔςΟϯάॲཧ ݱࡏͷϧʔτ৘ใͱൺֱ͠αϒωοτϚεΫͱAD஋͕౳͘͠
 ϝτϦοΫ͕খ͍͞৔߹Ұ൪༏ઌ౓͕ߴ͍΋ͷͱ͢Δ %% fetch to destination route % ୈҰҾ਺

    : ϧʔτ৘ใʢarrayʣ % ୈೋҾ਺ : ݱࡏͷҰ൪༏ઌ౓͕ߴ͍ϧʔτ৘ใ fetch_dest_route([{_, _, Subnet, Ad, Metric}=Route| Tail],{_, _, Subnet, Ad, NowMetric}) when Metric < NowMetric -> fetch_dest_route(Tail, Route); ϝτϦοΫΛൺֱ
  14. ϧʔςΟϯάॲཧ ARPςʔϒϧ͔ΒωΫετϗοϓͷIPʹඥͮ͘ɺMACΞυϨεΛ
 औಘ͢Δ get_mac_addr({_, Nexthop}) -> % ARPςʔϒϧ͔ΒωΫετϗοϓʹඥͮ͘MACΞυϨεΛऔಘ case brook_arp_table:fetch_dest_mac_addr(Nexthop,

    false) of % ଘࡏͯ͠ͳ͍৔߹͸ɺʮundefinedʯΛฦ͢ [] -> undefined; % ଘࡏͯ͠Δ৔߹͸ɺMACΞυϨεΛฦ͢ [{arp_table, _, _, DestMac, _}| _] -> DestMac end.
  15. ϧʔςΟϯάॲཧ L2ϔομʔͷૹ৴ઌMACΞυϨεΛωΫετϗοϓɺ
 ૹ৴ݩMACΞυϨεΛࣗ෼ࣗ਎ʢΠϯλʔϑΣΠεʣ΁ͱ
 ॻ͖׵͑Δ ip_request(FD, #{source_mac := SourceMac, dest_mac :=

    DestMac}=Opt, Data) -> Ethernet = ethernet_to_binary(#ethernet_header{ source_mac_addr=tuple_to_list(SourceMac), dest_mac_addr=DestMac, type=?TYPE_IP} ), request(FD, <<Ethernet/bitstring, Data/bitstring>>, Opt) L2ͷύέοτϔομʔΛ࡞੒
  16. FirewallϞδϡʔϧ ύέοτͷड৴ -͕*1͔֬ೝ νΣοΫαϜͷ֬ೝ 55-Λ 55-͕͔Ͳ͏͔֬ೝ νΣοΫαϜͷ࠶ܭࢉ ωΫετϗοϓͷ."$ ΞυϨεͷऔಘ -ͷϔομʔΛ࡞੒

    ύέοτΛૹ৴ -ͷॲཧ ϧʔςΟϯάςʔϒϧ ͔Βϧʔτ৘ใΛऔಘ ͜͜ʹFirewallͷॲཧΛPipelineͱͯ͠௥Ճ͢Δ
  17. FirewallϞδϡʔϧ • ४උɿErlangଆͰPipelineॲཧΛ࣮ߦͰ͖ΔΑ͏ʹ͢Δ save_before_ip_pipeline(Func) when is_function(Func, 2) -> mnesia:transaction(fun() ->

    mnesia:write(pipeline, #pipeline{type=?BEFORE_IP, module=undefined, func=Func}, write) end) before_ip_pipeline(Data, Opt) -> Filter = mnesia:dirty_match_object(pipeline, {'_', ?BEFORE_IP, '$1', '$2'} ), pipeline(Filter, Data, Opt) pipeline([#pipeline{module=undefined, func=Func}| Tail], Data0, Opt0) -> case apply(Func, [Data0, Opt0]) of {error, Msg} -> {error, Msg}; {ok, Data, Opt} -> pipeline(Tail, Data, Opt) end;
  18. FirewallϞδϡʔϧ • ४උɿErlangଆͰPipelineॲཧΛ࣮ߦͰ͖ΔΑ͏ʹ͢Δ save_before_ip_pipeline(Func) when is_function(Func, 2) -> mnesia:transaction(fun() ->

    mnesia:write(pipeline, #pipeline{type=?BEFORE_IP, module=undefined, func=Func}, write) end) before_ip_pipeline(Data, Opt) -> Filter = mnesia:dirty_match_object(pipeline, {'_', ?BEFORE_IP, '$1', '$2'} ), pipeline(Filter, Data, Opt) pipeline([#pipeline{module=undefined, func=Func}| Tail], Data0, Opt0) -> case apply(Func, [Data0, Opt0]) of {error, Msg} -> {error, Msg}; {ok, Data, Opt} -> pipeline(Tail, Data, Opt) end; PipelineʹॲཧΛొ࿥
  19. FirewallϞδϡʔϧ • ४උɿErlangଆͰPipelineॲཧΛ࣮ߦͰ͖ΔΑ͏ʹ͢Δ save_before_ip_pipeline(Func) when is_function(Func, 2) -> mnesia:transaction(fun() ->

    mnesia:write(pipeline, #pipeline{type=?BEFORE_IP, module=undefined, func=Func}, write) end) before_ip_pipeline(Data, Opt) -> Filter = mnesia:dirty_match_object(pipeline, {'_', ?BEFORE_IP, '$1', '$2'} ), pipeline(Filter, Data, Opt) pipeline([#pipeline{module=undefined, func=Func}| Tail], Data0, Opt0) -> case apply(Func, [Data0, Opt0]) of {error, Msg} -> {error, Msg}; {ok, Data, Opt} -> pipeline(Tail, Data, Opt) end; Pipelineͷ࣮ߦ
  20. FirewallϞδϡʔϧ • ४උɿErlangଆͰPipelineॲཧΛ࣮ߦͰ͖ΔΑ͏ʹ͢Δ save_before_ip_pipeline(Func) when is_function(Func, 2) -> mnesia:transaction(fun() ->

    mnesia:write(pipeline, #pipeline{type=?BEFORE_IP, module=undefined, func=Func}, write) end) before_ip_pipeline(Data, Opt) -> Filter = mnesia:dirty_match_object(pipeline, {'_', ?BEFORE_IP, '$1', '$2'} ), pipeline(Filter, Data, Opt) pipeline([#pipeline{module=undefined, func=Func}| Tail], Data0, Opt0) -> case apply(Func, [Data0, Opt0]) of {error, Msg} -> {error, Msg}; {ok, Data, Opt} -> pipeline(Tail, Data, Opt) end; Pipelineʹొ࿥ͨ͠ॲཧΛ࣮ߦ͍ͯ͘͠
  21. FirewallϞδϡʔϧ • ४උɿErlangଆͰPipelineॲཧΛ࣮ߦͰ͖ΔΑ͏ʹ͢Δ save_before_ip_pipeline(Func) when is_function(Func, 2) -> mnesia:transaction(fun() ->

    mnesia:write(pipeline, #pipeline{type=?BEFORE_IP, module=undefined, func=Func}, write) end) before_ip_pipeline(Data, Opt) -> Filter = mnesia:dirty_match_object(pipeline, {'_', ?BEFORE_IP, '$1', '$2'} ), pipeline(Filter, Data, Opt) pipeline([#pipeline{module=undefined, func=Func}| Tail], Data0, Opt0) -> case apply(Func, [Data0, Opt0]) of {error, Msg} -> {error, Msg}; {ok, Data, Opt} -> pipeline(Tail, Data, Opt) end; FuncʢϝιουʣΛ [Data0, Opt0] ͷҾ਺Ͱ࣮ߦ͢Δ
  22. FirewallϞδϡʔϧ • ४උɿErlangଆͰPipelineॲཧΛ࣮ߦͰ͖ΔΑ͏ʹ͢Δ save_before_ip_pipeline(Func) when is_function(Func, 2) -> mnesia:transaction(fun() ->

    mnesia:write(pipeline, #pipeline{type=?BEFORE_IP, module=undefined, func=Func}, write) end) before_ip_pipeline(Data, Opt) -> Filter = mnesia:dirty_match_object(pipeline, {'_', ?BEFORE_IP, '$1', '$2'} ), pipeline(Filter, Data, Opt) pipeline([#pipeline{module=undefined, func=Func}| Tail], Data0, Opt0) -> case apply(Func, [Data0, Opt0]) of {error, Msg} -> {error, Msg}; {ok, Data, Opt} -> pipeline(Tail, Data, Opt) end; Τϥʔͷ৔߹͸ΤϥʔͱͳΓɺύέοτͷ ϧʔςΟϯάΛߦΘͳ͍ ਖ਼ৗͷ৔߹͸ɺ࣍ͷPipelineॲཧΛ࣮ߦ
  23. FirewallϞδϡʔϧ • Firewallͷ৚݅ΛϝλϓϩάϥϜͰ࣮૷ firewall :default do allow( source_ip: {192, 168,

    20, 0}, source_netmask: {255, 255, 255, 0}, protocol: :ip ) allow( source_ip: {192, 168, 40, 0}, source_netmask: {255, 255, 255, 0}, protocol: :tcp ) deny() end
  24. FirewallϞδϡʔϧ • Firewallͷ৚݅ΛϝλϓϩάϥϜͰ࣮૷ firewall :default do allow( source_ip: {192, 168,

    20, 0}, source_netmask: {255, 255, 255, 0}, protocol: :ip ) allow( source_ip: {192, 168, 40, 0}, source_netmask: {255, 255, 255, 0}, protocol: :tcp ) deny() end ڐՄ͢Δύέοτ৘ใ ʢૹ৴ݩIPͱ͔ϓϩτίϧͱ͔ʣ
  25. FirewallϞδϡʔϧ • Firewallͷ৚݅ΛϝλϓϩάϥϜͰ࣮૷ firewall :default do allow( source_ip: {192, 168,

    20, 0}, source_netmask: {255, 255, 255, 0}, protocol: :ip ) allow( source_ip: {192, 168, 40, 0}, source_netmask: {255, 255, 255, 0}, protocol: :tcp ) deny() end શͯͷύέοτΛڋ൱͢Δ
  26. FirewallϞδϡʔϧ • Firewallॲཧͷొ࿥ defmacro firewall_through(identifier) do quote do identifier =

    unquote(identifier) :brook_pipeline.save_before_ip_pipeline(Eshe.Firewall.firewall_filter(identifier)) end end def firewall_filter(identifier) do filter = fetch_filter(Eshe.Supervisor.route_firewall(), identifier) fn data, option -> case is_allow_filter(filter, data) do :ok -> {:ok, data, option} error -> {:error, error} end end end
  27. FirewallϞδϡʔϧ • Firewallॲཧͷొ࿥ defmacro firewall_through(identifier) do quote do identifier =

    unquote(identifier) :brook_pipeline.save_before_ip_pipeline(Eshe.Firewall.firewall_filter(identifier)) end end def firewall_filter(identifier) do filter = fetch_filter(Eshe.Supervisor.route_firewall(), identifier) fn data, option -> case is_allow_filter(filter, data) do :ok -> {:ok, data, option} error -> {:error, error} end end end firewall_filterΛొ࿥͢Δ
  28. FirewallϞδϡʔϧ • Firewallॲཧͷొ࿥ defmacro firewall_through(identifier) do quote do identifier =

    unquote(identifier) :brook_pipeline.save_before_ip_pipeline(Eshe.Firewall.firewall_filter(identifier)) end end def firewall_filter(identifier) do filter = fetch_filter(Eshe.Supervisor.route_firewall(), identifier) fn data, option -> case is_allow_filter(filter, data) do :ok -> {:ok, data, option} error -> {:error, error} end end end Erlangଆ΁ॲཧΛฦ͠ɺΤϥʔͷ৔߹͸ ύέοτͷॲཧΛऴྃͤ͞Δ
  29. FirewallϞδϡʔϧ • Firewallͷ൑ఆॲཧ def match( %{protocol: protocol} = record, <<version::size(4),

    len::size(4), _head::size(88), source_ip::size(32), dest_ip::size(32), other::binary>> ) when protocol in [:tcp, :udp] do {source_port, dest_port} = fetch_port(len, other) with res <- match_ip([], record[:dest_ip], record[:dest_netmask], dest_ip), res <- match_ip(res, record[:source_ip], record[:source_netmask], source_ip), res <- match_port(res, record[:source_port], source_port), res <- match_port(res, record[:dest_port], dest_port), res <- Enum.filter(res, &(&1 != nil)), {:ok, _value} <- Enum.fetch(res, 0) do Enum.all?(res, fn r -> r == true end) else _ -> false end end
  30. FirewallϞδϡʔϧ • Firewallͷ൑ఆॲཧ def match( %{protocol: protocol} = record, <<version::size(4),

    len::size(4), _head::size(88), source_ip::size(32), dest_ip::size(32), other::binary>> ) when protocol in [:tcp, :udp] do {source_port, dest_port} = fetch_port(len, other) with res <- match_ip([], record[:dest_ip], record[:dest_netmask], dest_ip), res <- match_ip(res, record[:source_ip], record[:source_netmask], source_ip), res <- match_port(res, record[:source_port], source_port), res <- match_port(res, record[:dest_port], dest_port), res <- Enum.filter(res, &(&1 != nil)), {:ok, _value} <- Enum.fetch(res, 0) do Enum.all?(res, fn r -> r == true end) else _ -> false end end ૹ৴ݩͱૹ৴ઌͷIP ૹ৴ݩͱૹ৴ઌͷϙʔτ
  31. FirewallϞδϡʔϧ • Firewallͷ൑ఆॲཧ def match( %{protocol: protocol} = record, <<version::size(4),

    len::size(4), _head::size(88), source_ip::size(32), dest_ip::size(32), other::binary>> ) when protocol in [:tcp, :udp] do {source_port, dest_port} = fetch_port(len, other) with res <- match_ip([], record[:dest_ip], record[:dest_netmask], dest_ip), res <- match_ip(res, record[:source_ip], record[:source_netmask], source_ip), res <- match_port(res, record[:source_port], source_port), res <- match_port(res, record[:dest_port], dest_port), res <- Enum.filter(res, &(&1 != nil)), {:ok, _value} <- Enum.fetch(res, 0) do Enum.all?(res, fn r -> r == true end) else _ -> false end end શͯͷൺֱ݁Ռ͕౳͍͠৔߹ͷΈtrueʹ