From 892fd0fbaeeeb75a1a05dfad8e3f634572babbcd Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 21 Nov 2017 11:29:26 +0900 Subject: [PATCH] Refactored testcases --- test/flay.ex | 7 + test/flay_test.exs | 439 --------------------------------------------- test/flog_test.exs | 172 ++++++++++++++++++ test/pf.ex | 29 ++- 4 files changed, 201 insertions(+), 446 deletions(-) delete mode 100644 test/flay_test.exs create mode 100644 test/flog_test.exs diff --git a/test/flay.ex b/test/flay.ex index d218886..88490b1 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -32,6 +32,9 @@ defmodule Flay do {:noreply, %{state|reply_to: from}} end + def handle_cast({:register_pid, tester_pid}, state) do + {:noreply, %{state|tester_pid: tester_pid}} + end def handle_cast({:flow_install, flow_opts, tester_pid}, state) do send_flow_mod_add(state.datapath_id, flow_opts) flow_opts_to_ofp_print(flow_opts) @@ -42,6 +45,10 @@ defmodule Flay do send(state.tester_pid, error) {:noreply, state} end + def handle_info(%PacketIn{} = pktin, state) do + send(state.tester_pid, pktin) + {:noreply, state} + end def handle_info(%PortDesc.Reply{} = desc, state) do GenServer.reply(state.reply_to, desc) {:noreply, %{state|reply_to: nil}} diff --git a/test/flay_test.exs b/test/flay_test.exs deleted file mode 100644 index 8597de1..0000000 --- a/test/flay_test.exs +++ /dev/null @@ -1,439 +0,0 @@ -defmodule FlayTest do - use ExUnit.Case - use Bitwise - - @vlan_trunk_port 1 - @access_port 2 - @vxlan_port 3 - @bootnet_vid 0x1000 ||| 5 - @user_vid 0x1000 ||| 123 - @mcast {"010000000000", "010000000000"} - @mac "010203040506" - @sdl_vmac "000000000001" - @trusted_macs [ - "0800274d3297", - "0800274d3298", - "0800274d3299" - ] - - import Record - # Extract Erlang record for klarna/brod - for {name, schema} <- extract_all(from_lib: "pkt/include/pkt.hrl") do - defrecord(name, schema) - end - - setup do - Code.load_file("test/flay.ex") - Code.load_file("test/pf.ex") - Application.put_env(:tres, :protocol, :tcp, persistent: true) - Application.put_env(:tres, :port, 6633, persistent: true) - Application.put_env(:tres, :mac_connections, 1, persistent: true) - Application.put_env(:tres, :mac_acceptors, 1, persistent: true) - Application.put_env(:tres, :callback_module, Flay, persistent: true) - Application.put_env(:tres, :callback_args, self(), persistent: true) - Application.start(:binpp) - Application.start(:pkt) - Application.start(:epcap) - Application.start(:ranch) - Application.start(:tres) - wait_for_connected() - end - - describe "switch:merged_handler" do - test "OFPMP_DESC" do - %Openflow.Multipart.Desc.Reply{} = GenServer.call(Flay, :desc_stats, 5000) - end - - test "Flow install" do - options = [cookie: 0x8000000000000000] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new( - in_port: @vlan_trunk_port, - eth_dst: "ffffffffffff", - vlan_vid: {0x0000, 0x1fff}, - eth_type: 0x88cc - ) - ins = Openflow.Instruction.ApplyActions.new(Openflow.Action.Output.new(:controller)) - options = [ - cookie: 0x4000000000000000, - table_id: 0, - priority: 200, - match: match, - instructions: [ins] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # LLDP Packet to vlan trunk port mac_address - src = "00000000000a" - src_mac = <<(String.to_integer(src, 16))::48>> - dst = "ffffffffffff" - dst_mac = <<(String.to_integer(dst, 16))::48>> - lldp = lldp(pdus: [ - {:chassis_id, :mac_address, src_mac}, - {:port_id, :mac_address, src_mac}, - {:ttl, 120} - ]) - ether = ether(shost: src_mac, dhost: dst_mac, type: 0x88cc) - _packet = <<(:pkt.ether(ether))::bytes, (:pkt.lldp(lldp))::bytes>> - - %Openflow.Multipart.PortDesc.Reply{ports: ports} = GenServer.call(Flay, :port_desc_stats, 5000) - for port <- ports do - match = Openflow.Match.new(in_port: port.number, eth_src: port.hw_addr) - options = [ - cookie: 0x4000000000000000, - table_id: 0, - priority: 201, - match: match, - instructions: [] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # ARP Packet to vlan_trunk port mac address - arp = arp(arp_sha: src_mac, arp_sha: dst_mac) - ether = ether(shost: src_mac, dhost: dst_mac, type: 0x0806) - _packet = <<(:pkt.ether(ether))::bytes, (:pkt.arp(arp))::bytes>> - end - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "switch:uplink_escalation_flow" do - test "Flow install" do - match = Openflow.Match.new(eth_type: 0x0806) - action = Openflow.Action.Output.new(:controller) - ins = Openflow.Instruction.ApplyActions.new(action) - options = [ - cookie: 0x2000000000000000, - table_id: 0, - priority: 10, - match: match, - instructions: ins - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) - action = Openflow.Action.Output.new(:controller) - ins = Openflow.Instruction.ApplyActions.new(action) - options = [ - cookie: 0x2000000000000000, - table_id: 0, - priority: 10, - match: match, - instructions: ins - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port) - options = [ - cookie: 0x2000000000000000, - table_id: 0, - priority: 11, - match: match, - instructions: [] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "associate:register_bootstrap_rule" do - test "Flow install" do - cookie = :crypto.strong_rand_bytes(8) |> :binary.decode_unsigned(:big) - match = Openflow.Match.new(in_port: @access_port, eth_src: @mac) - actions = [ - Openflow.Action.PushVlan.new, - Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}), - Openflow.Action.Output.new(@vlan_trunk_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [table_id: 0, - priority: 50, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match, - instructions: [ins]] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # trusted mac - match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @bootnet_vid, eth_dst: @mcast) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [table_id: 0, - priority: 50, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match, - instructions: [ins]] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @bootnet_vid, eth_dst: @mac) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [table_id: 0, - priority: 50, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match, - instructions: [ins]] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port, eth_src: @mac) - options = - [table_id: 0, - priority: 29, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # Trunk Port, w/Trusted MAC -> Pop VLAN -> Access Port - for trusted <- @trusted_macs do - match = Openflow.Match.new( - in_port: @vlan_trunk_port, - eth_src: trusted, - eth_dst: @mac, - vlan_vid: @bootnet_vid - ) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [table_id: 0, - priority: 50, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match, - instructions: ins] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(eth_src: trusted) - options = - [table_id: 0, - priority: 29, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - end - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "associate:register_usernet_rule" do - test "Flow install" do - # BOOTNET_USE_USER_NETWORK - # - # Reply from SDL to the authorized client - cookie = :crypto.strong_rand_bytes(8) |> :binary.decode_unsigned(:big) - match = Openflow.Match.new(in_port: @vxlan_port, eth_dst: @mac) - action = Openflow.Action.Output.new(@access_port) - ins = Openflow.Instruction.ApplyActions.new(action) - options = [ - cookie: cookie, - table_id: 0, - priority: 40, - idle_timeout: 32_768, - hard_timeout: 32_768, - match: match, - instructions: [ins] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # HTTP request to the `CaptivePortal` via the SDL controller - match = Openflow.Match.new( - eth_type: 0x0800, - vlan_vid: {0x1000, 0x1000}, - ip_proto: 6, - tcp_dst: 443, - ipv4_dst: {192,168,5,4} - ) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(@vxlan_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = [ - cookie: 0x2000000000000001, - table_id: 1, - priority: 30, - match: match, - instructions: [ins] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new( - eth_type: 0x0800, - vlan_vid: {0x1000, 0x1000}, - ip_proto: 6, - tcp_dst: 80, - ipv4_dst: {192,168,5,5} - ) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(@vxlan_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = [ - cookie: 0x2000000000000001, - table_id: 1, - priority: 30, - match: match, - instructions: [ins] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # Access Port, w/sMAC -> Push VLAN -> Table1 - # Table1, w/dMAC, w/VLAN -> Pop VLAN -> Access Port - match = Openflow.Match.new(in_port: @access_port, eth_src: @mac) - actions = [ - Openflow.Action.PushVlan.new, - Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}) - ] - insts = [ - Openflow.Instruction.ApplyActions.new(actions), - Openflow.Instruction.GotoTable.new(1) - ] - options = [ - cookie: cookie, - table_id: 0, - priority: 20, - idle_timeout: 32_768, - hard_timeout: 32_768, - flags: [:send_flow_rem], - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port, eth_src: @mac) - options = [ - cookie: cookie, - table_id: 0, - priority: 19, - idle_timeout: 32_768, - hard_timeout: 32_768, - flags: [:send_flow_rem], - match: match, - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(eth_src: @mac) - actions = [Openflow.Action.Output.new(:controller)] - insts = [Openflow.Instruction.ApplyActions.new(actions)] - options = [ - cookie: cookie, - table_id: 0, - priority: 18, - idle_timeout: 32_768, - hard_timeout: 32_768, - flags: [:send_flow_rem], - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(vlan_vid: {0x1000, 0x1000}, eth_dst: @mac) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - insts = [Openflow.Instruction.ApplyActions.new(actions)] - options = [ - cookie: cookie, - table_id: 1, - priority: 50, - idle_timeout: 32_768, - hard_timeout: 32_768, - flags: [:send_flow_rem], - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # VLAN 毎に登録する共通ルール - # Table1, w/VLAN -+-> Trunk - # +-> Pop VLAN -> Access Ports - # Trunk Port, w/VLAN -> Table1 - match = Openflow.Match.new(vlan_vid: @user_vid, eth_dst: @mcast) - actions = [ - Openflow.Action.Output.new(@vlan_trunk_port), - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - insts = [Openflow.Instruction.ApplyActions.new(actions)] - options = [ - cookie: 0x200000000000001, - table_id: 1, - priority: 60, - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(vlan_vid: @user_vid) - actions = [ - Openflow.Action.Output.new(@vlan_trunk_port), - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - insts = [Openflow.Instruction.ApplyActions.new(actions)] - options = [ - cookie: 0x200000000000001, - table_id: 1, - priority: 20, - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @user_vid) - insts = [Openflow.Instruction.GotoTable.new(1)] - options = [ - cookie: 0x200000000000001, - table_id: 0, - priority: 50, - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - defp wait_for_connected do - case Process.whereis(Flay) do - nil -> - wait_for_connected() - pid when is_pid(pid) -> - :ok - end - end -end diff --git a/test/flog_test.exs b/test/flog_test.exs new file mode 100644 index 0000000..857d17f --- /dev/null +++ b/test/flog_test.exs @@ -0,0 +1,172 @@ +defmodule FlogTest do + use ExUnit.Case, async: true + use Bitwise + + @vlan_trunk_port "veth0" # FIXME: + @access_port "veth3" # FIXME: + @vxlan_port "veth4" # FIXME: + #@bootnet_vid 0x1000 ||| 5 + #@user_vid 0x1000 ||| 123 + @mcast {"010000000000", "010000000000"} + #@bcast "ffffffffffff" + #@mac "010203040506" + #@sdl_vmac "000000000001" + #@trusted_macs [ + # "0800274d3297", + # "0800274d3298", + # "0800274d3299" + #] + + import Record + # Extract Erlang record for msantos/pkt + for {name, schema} <- extract_all(from_lib: "pkt/include/pkt.hrl") do + defrecord(name, schema) + end + + Code.load_file("test/flay.ex") + Code.load_file("test/pf.ex") + + setup_all do + setup_applications() + wait_for_connected() + ports = get_ports_desc() + vlan_trunk = Enum.find(ports, fn(port) -> port.name == @vlan_trunk_port end) + vxlan_port = Enum.find(ports, fn(port) -> port.name == @vxlan_port end) + port = Enum.find(ports, fn(port) -> port.name == @access_port end) + options = [ + vlan_trunk: vlan_trunk, + vxlan_port: vxlan_port, + port: port + ] + {:ok, options} + end + + describe("switch:merged_handler:table=0,priority=0,cookie=0x8000000000000000,actions=drop") do + test "Install Flow", state do + options = [ + cookie: 0x8000000000000000, + table_id: 0, + priority: 0 + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:merged_handler:" <> + "table=0,priority=200,cookie=0x4000000000000000,in_port=%d,dl_dst=%s,dl_vlan=0x0000/0x1fff,dl_type=%s," <> + "actions=controller") do + test "Install Flow", state do + match = Openflow.Match.new( + in_port: state.vlan_trunk.number, + eth_dst: @bcast, + vlan_vid: {0x0000, 0x1fff}, + eth_type: 0x88cc + ) + action = Openflow.Action.Output.new(:controller) + ins = Openflow.Instruction.ApplyActions.new(action) + options = + [cookie: 0x4000000000000000, + table_id: 0, + priority: 200, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:merged_handler:" <> + "table=0,priority=201,cookie=0x4000000000000000,in_port=%d,dl_src=%s,actions=drop") do + test "Install Flow", state do + for port <- [state.vlan_trunk, state.port] do + match = Openflow.Match.new(in_port: port.number, eth_src: port.hw_addr) + options = + [cookie: 0x4000000000000000, + table_id: 0, + priority: 201, + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + end + + describe("switch:uplink_escalation_flow:" <> + "table=0,priority=10,cookie=0x2000000000000000,arp,actions=controller") do + test "Install Flow" do + match = Openflow.Match.new(eth_type: 0x0806) + action = Openflow.Action.Output.new(:controller) + ins = Openflow.Instruction.ApplyActions.new(action) + options = + [cookie: 0x2000000000000000, + table_id: 0, + priority: 10, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:uplink_escalation_flow:" <> + "table=0,priority=10,cookie=0x2000000000000000,udp,udp_dst=67,actions=controller") do + test "Install Flow" do + match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) + action = Openflow.Action.Output.new(:controller) + ins = Openflow.Instruction.ApplyActions.new(action) + options = + [cookie: 0x2000000000000000, + table_id: 0, + priority: 10, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:uplink_escalation_flow:" <> + "table=0,priority=11,cookie=0x2000000000000000,in_port={trunk_port},actions=drop") do + test "Install Flow", state do + match = Openflow.Match.new(in_port: state.vlan_trunk.number) + options = + [cookie: 0x2000000000000000, + table_id: 0, + priority: 11, + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + # private functions + + defp setup_applications do + Application.put_env(:tres, :protocol, :tcp, persistent: true) + Application.put_env(:tres, :port, 6633, persistent: true) + Application.put_env(:tres, :mac_connections, 1, persistent: true) + Application.put_env(:tres, :mac_acceptors, 1, persistent: true) + Application.put_env(:tres, :callback_module, Flay, persistent: true) + Application.put_env(:tres, :callback_args, self(), persistent: true) + Application.start(:binpp) + Application.start(:pkt) + Application.start(:epcap) + Application.start(:ranch) + Application.start(:tres) + end + + defp wait_for_connected do + case Process.whereis(Flay) do + nil -> + wait_for_connected() + pid when is_pid(pid) -> + :ok + end + end + + defp get_ports_desc do + port_desc = GenServer.call(Flay, :port_desc_stats, 5000) + port_desc.ports + end +end diff --git a/test/pf.ex b/test/pf.ex index 5762578..408e4fa 100644 --- a/test/pf.ex +++ b/test/pf.ex @@ -9,14 +9,28 @@ defmodule Pf do ] end - def start_link(ifname, pid) do - ifname = String.to_charlist(ifname) - GenServer.start_link(__MODULE__, [ifname, pid]) + def inject!(pid, packet) do + GenServer.cast(pid, {:inject, packet}) end - def init([ifname, pid]) do - {:ok, pid} = :epcap.start_link(interface: ifname, chroot: 'priv/tmp', inject: true) - %State{pcap_ref: pid, ifname: ifname, tester_pid: pid} + def start_link(ifname) do + ifname = String.to_charlist(ifname) + GenServer.start_link(__MODULE__, [ifname, self()]) + end + + def init([ifname, tester_pid]) do + {:ok, epcap_pid} = + :epcap.start_link( + interface: ifname, + promiscuous: true, + inject: true + ) + state = %State{ + pcap_ref: epcap_pid, + ifname: ifname, + tester_pid: tester_pid + } + {:ok, state} end def handle_cast({:inject, packet}, state) do @@ -31,7 +45,8 @@ defmodule Pf do end def handle_info({:packet, _dlt, _time, _len, data}, state) do - send(state.tester_pid, data) + pkt = :pkt.decapsulate(data) + send(state.tester_pid, {pkt, self()}) {:noreply, state} end def handle_info(_info, state) do