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

A96a5f977a4d024000d82e590554f847?s=47 kobatako
March 31, 2019

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

A96a5f977a4d024000d82e590554f847?s=128

kobatako

March 31, 2019
Tweet

Transcript

  1. 2.

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

    גࣜձࣾFusic • ࢓ࣄ : PHPɺGolangɺAWS • झຯ : ElixirɺErlangɺΠϯϑϥ͍Ζ͍Ζ • Twitter : kobatako_
  2. 19.

    ϧʔςΟϯάʹؔ͢Δ΋ͷ • ϧʔςΟϯάςʔϒϧͷྫ ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ 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. 26.

    ܾΊΔͱ͖ͷ༏ઌॱ • ϩϯήετϚονͷྫ • ૹ৴ઌ͕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. 30.

    ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ 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. 31.

    ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ 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. 32.

    ৘ใݯ Ѽઌϧʔτ αϒωοτϚεΫ 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. 37.

    ϧʔςΟϯάॲཧ ૹ৴ઌ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. 38.

    ϧʔςΟϯάॲཧ % ୈҰҾ਺ : ϧʔςΟϯάςʔϒϧ಺ͷશϦετʢ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. 39.

    ϧʔςΟϯάॲཧ % ୈҰҾ਺ : ϧʔςΟϯάςʔϒϧ಺ͷશϦετʢ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. 42.

    ϧʔςΟϯάॲཧ Ѽઌ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. 43.

    ϧʔςΟϯάॲཧ ݱࡏͷϧʔτ৘ใΑΓαϒωοτϚεΫ͕௕͍ϧʔτ৘ใ͕
 ͋Δ৔߹ɺҰ൪༏ઌ౓͕ߴ͍΋ͷͱ͢Δ %% fetch to destination route % ୈҰҾ਺

    : ϧʔτ৘ใʢarrayʣ % ୈೋҾ਺ : ݱࡏͷҰ൪༏ઌ౓͕ߴ͍ϧʔτ৘ใ fetch_dest_route([{_, _, Subnet, _, _}=Route| Tail],{_, _, NowSubnet, _, _}) when Subnet > NowSubnet -> fetch_dest_route(Tail, Route); αϒωοτϚεΫͷൺֱ
  12. 44.

    ϧʔςΟϯάॲཧ ݱࡏͷϧʔτ৘ใͱൺֱ͠αϒωοτϚεΫ͕౳͘͠
 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. 45.

    ϧʔςΟϯάॲཧ ݱࡏͷϧʔτ৘ใͱൺֱ͠αϒωοτϚεΫͱ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. 47.

    ϧʔςΟϯάॲཧ 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. 48.

    ϧʔςΟϯάॲཧ 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. 55.

    FirewallϞδϡʔϧ ύέοτͷड৴ -͕*1͔֬ೝ νΣοΫαϜͷ֬ೝ 55-Λ 55-͕͔Ͳ͏͔֬ೝ νΣοΫαϜͷ࠶ܭࢉ ωΫετϗοϓͷ."$ ΞυϨεͷऔಘ -ͷϔομʔΛ࡞੒

    ύέοτΛૹ৴ -ͷॲཧ ϧʔςΟϯάςʔϒϧ ͔Βϧʔτ৘ใΛऔಘ ͜͜ʹFirewallͷॲཧΛPipelineͱͯ͠௥Ճ͢Δ
  17. 56.

    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. 57.

    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. 58.

    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. 59.

    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. 60.

    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. 61.

    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. 63.

    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. 64.

    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. 65.

    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. 66.

    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. 67.

    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. 68.

    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. 69.

    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. 70.

    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. 71.

    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ʹ
  32. 72.