Refactored testcases
This commit is contained in:
parent
2c0b0024a1
commit
892fd0fbae
4 changed files with 201 additions and 446 deletions
|
|
@ -32,6 +32,9 @@ defmodule Flay do
|
||||||
{:noreply, %{state|reply_to: from}}
|
{:noreply, %{state|reply_to: from}}
|
||||||
end
|
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
|
def handle_cast({:flow_install, flow_opts, tester_pid}, state) do
|
||||||
send_flow_mod_add(state.datapath_id, flow_opts)
|
send_flow_mod_add(state.datapath_id, flow_opts)
|
||||||
flow_opts_to_ofp_print(flow_opts)
|
flow_opts_to_ofp_print(flow_opts)
|
||||||
|
|
@ -42,6 +45,10 @@ defmodule Flay do
|
||||||
send(state.tester_pid, error)
|
send(state.tester_pid, error)
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
def handle_info(%PacketIn{} = pktin, state) do
|
||||||
|
send(state.tester_pid, pktin)
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
def handle_info(%PortDesc.Reply{} = desc, state) do
|
def handle_info(%PortDesc.Reply{} = desc, state) do
|
||||||
GenServer.reply(state.reply_to, desc)
|
GenServer.reply(state.reply_to, desc)
|
||||||
{:noreply, %{state|reply_to: nil}}
|
{:noreply, %{state|reply_to: nil}}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
172
test/flog_test.exs
Normal file
172
test/flog_test.exs
Normal file
|
|
@ -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
|
||||||
29
test/pf.ex
29
test/pf.ex
|
|
@ -9,14 +9,28 @@ defmodule Pf do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_link(ifname, pid) do
|
def inject!(pid, packet) do
|
||||||
ifname = String.to_charlist(ifname)
|
GenServer.cast(pid, {:inject, packet})
|
||||||
GenServer.start_link(__MODULE__, [ifname, pid])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def init([ifname, pid]) do
|
def start_link(ifname) do
|
||||||
{:ok, pid} = :epcap.start_link(interface: ifname, chroot: 'priv/tmp', inject: true)
|
ifname = String.to_charlist(ifname)
|
||||||
%State{pcap_ref: pid, ifname: ifname, tester_pid: pid}
|
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
|
end
|
||||||
|
|
||||||
def handle_cast({:inject, packet}, state) do
|
def handle_cast({:inject, packet}, state) do
|
||||||
|
|
@ -31,7 +45,8 @@ defmodule Pf do
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_info({:packet, _dlt, _time, _len, data}, state) do
|
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}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
def handle_info(_info, state) do
|
def handle_info(_info, state) do
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue