Work on test case for secure channel
This commit is contained in:
parent
e52fe31b79
commit
2c0b0024a1
11 changed files with 603 additions and 16 deletions
|
|
@ -4,7 +4,7 @@ use Mix.Config
|
||||||
|
|
||||||
config :tres,
|
config :tres,
|
||||||
protocol: :tcp,
|
protocol: :tcp,
|
||||||
port: 6633,
|
port: 6653,
|
||||||
max_connections: 10,
|
max_connections: 10,
|
||||||
num_acceptors: 10,
|
num_acceptors: 10,
|
||||||
callback_module: Tres.ExampleHandler,
|
callback_module: Tres.ExampleHandler,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ defmodule Openflow.Action.PushVlan do
|
||||||
|
|
||||||
def ofpat, do: 17
|
def ofpat, do: 17
|
||||||
|
|
||||||
def new(ethertype) do
|
def new(ethertype \\ 0x8100) do
|
||||||
%PushVlan{ethertype: ethertype}
|
%PushVlan{ethertype: ethertype}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ defmodule Tres.MessageHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp send_flow_mod_modify(datapath_id, options) do
|
defp send_flow_mod_modify(datapath_id, options) do
|
||||||
command = Tres.Utils.flow_command(options, :modify)
|
command = Tres.Utils.flow_command(:modify, options)
|
||||||
flow_mod = %Openflow.FlowMod{
|
flow_mod = %Openflow.FlowMod{
|
||||||
cookie: options[:cookie] || 0,
|
cookie: options[:cookie] || 0,
|
||||||
table_id: options[:table_id] || 0,
|
table_id: options[:table_id] || 0,
|
||||||
|
|
@ -36,7 +36,7 @@ defmodule Tres.MessageHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp send_flow_mod_delete(datapath_id, options) do
|
defp send_flow_mod_delete(datapath_id, options) do
|
||||||
command = Tres.Utils.flow_command(options, :delete)
|
command = Tres.Utils.flow_command(:delete, options)
|
||||||
flow_mod = %Openflow.FlowMod{
|
flow_mod = %Openflow.FlowMod{
|
||||||
cookie: options[:cookie] || 0,
|
cookie: options[:cookie] || 0,
|
||||||
cookie_mask: options[:cookie_mask] || 0,
|
cookie_mask: options[:cookie_mask] || 0,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ defmodule Tres.SecureChannel do
|
||||||
@hello_handshake_timeout 1000
|
@hello_handshake_timeout 1000
|
||||||
@features_handshake_timeout 1000
|
@features_handshake_timeout 1000
|
||||||
@ping_timeout 5000
|
@ping_timeout 5000
|
||||||
@transaction_timeout 5000
|
# @transaction_timeout 5000
|
||||||
|
|
||||||
@ping_interval 5000
|
@ping_interval 5000
|
||||||
@ping_fail_max_count 10
|
@ping_fail_max_count 10
|
||||||
|
|
@ -193,12 +193,16 @@ defmodule Tres.SecureChannel do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_message(_in_xact = true, message, state_data) do
|
defp handle_message(_in_xact = true, message, state_data) do
|
||||||
[{:xact_entry, _xid, prev_message, _orig}|_] = XACT_KV.get(state_data.xact_kv_ref, message.xid)
|
case XACT_KV.get(state_data.xact_kv_ref, message.xid) do
|
||||||
|
[{:xact_entry, _xid, prev_message, _orig}|_] ->
|
||||||
new_message = Openflow.append_body(prev_message, message)
|
new_message = Openflow.append_body(prev_message, message)
|
||||||
XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message)
|
XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message)
|
||||||
|
_ ->
|
||||||
|
XACT_KV.delete(state_data.xact_kv_ref, message.xid)
|
||||||
end
|
end
|
||||||
defp handle_message(_in_xact = false, message, %SecureChannelState{handler_pid: handler_pid}) do
|
end
|
||||||
send(handler_pid, message)
|
defp handle_message(_in_xact = false, message, state_data) do
|
||||||
|
send(state_data.handler_pid, message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# WATING state
|
# WATING state
|
||||||
|
|
|
||||||
6
mix.exs
6
mix.exs
|
|
@ -7,7 +7,8 @@ defmodule Tres.Mixfile do
|
||||||
elixir: "~> 1.5",
|
elixir: "~> 1.5",
|
||||||
start_permanent: Mix.env == :prod,
|
start_permanent: Mix.env == :prod,
|
||||||
compilers: [:erlang] ++ Mix.compilers,
|
compilers: [:erlang] ++ Mix.compilers,
|
||||||
deps: deps()]
|
deps: deps(),
|
||||||
|
aliases: [test: "test --no-start"]]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Run "mix help compile.app" to learn about applications.
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
|
@ -20,6 +21,7 @@ defmodule Tres.Mixfile do
|
||||||
defp deps do
|
defp deps do
|
||||||
[{:ranch, "~> 1.4.0"},
|
[{:ranch, "~> 1.4.0"},
|
||||||
{:binpp, github: "jtendo/binpp", branch: "master"},
|
{:binpp, github: "jtendo/binpp", branch: "master"},
|
||||||
{:pkt, github: "msantos/pkt", ref: "3afb196"}]
|
{:pkt, github: "msantos/pkt", ref: "3afb196", only: :test, override: true},
|
||||||
|
{:epcap, github: "msantos/epcap", branch: "master", only: :test}]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
1
mix.lock
1
mix.lock
|
|
@ -1,4 +1,5 @@
|
||||||
%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]},
|
%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]},
|
||||||
|
"epcap": {:git, "https://github.com/msantos/epcap.git", "9566f0420a4dcf1292c1a1afd9339c35dbdfd041", [branch: "master"]},
|
||||||
"flow": {:hex, :flow, "0.12.0", "32c5a5f3ff6693e004b6c17a8c64dce2f8cdaf9564912d79427176013a586ab6", [], [{:gen_stage, "~> 0.12.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm"},
|
"flow": {:hex, :flow, "0.12.0", "32c5a5f3ff6693e004b6c17a8c64dce2f8cdaf9564912d79427176013a586ab6", [], [{:gen_stage, "~> 0.12.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"gen_stage": {:hex, :gen_stage, "0.12.2", "e0e347cbb1ceb5f4e68a526aec4d64b54ad721f0a8b30aa9d28e0ad749419cbb", [:mix], [], "hexpm"},
|
"gen_stage": {:hex, :gen_stage, "0.12.2", "e0e347cbb1ceb5f4e68a526aec4d64b54ad721f0a8b30aa9d28e0ad749419cbb", [:mix], [], "hexpm"},
|
||||||
"gen_state_machine": {:hex, :gen_state_machine, "2.0.1", "85efd5a0376929c3a4246dd943e17564a2908c7ddd7acd242d84594e785d83f8", [], [], "hexpm"},
|
"gen_state_machine": {:hex, :gen_state_machine, "2.0.1", "85efd5a0376929c3a4246dd943e17564a2908c7ddd7acd242d84594e785d83f8", [], [], "hexpm"},
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ is_exists(Tid, Xid) ->
|
||||||
%% Private functions
|
%% Private functions
|
||||||
|
|
||||||
ms_for_exists(Xid) ->
|
ms_for_exists(Xid) ->
|
||||||
ets:fun2ms(fun(#?ENTRY{xid = TXid}) when TXid < Xid -> true end).
|
ets:fun2ms(fun(#?ENTRY{xid = TXid}) when TXid == Xid -> true end).
|
||||||
|
|
||||||
ms_for_get(Xid) ->
|
ms_for_get(Xid) ->
|
||||||
ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E end).
|
ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E end).
|
||||||
|
|
@ -56,6 +56,6 @@ ms_for_update(Xid, Msg) ->
|
||||||
ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Msg} end).
|
ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Msg} end).
|
||||||
|
|
||||||
ms_for_handle_error(Tid, Xid, Error) ->
|
ms_for_handle_error(Tid, Xid, Error) ->
|
||||||
[Orig|_] = get(Tid, Xid),
|
[#?ENTRY{orig = Orig}|_] = get(Tid, Xid),
|
||||||
Error1 = maps:merge(Error, #{data => Orig}),
|
Error1 = maps:merge(Error, #{data => Orig}),
|
||||||
ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Error1} end).
|
ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Error1} end).
|
||||||
|
|
|
||||||
106
test/flay.ex
Normal file
106
test/flay.ex
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
defmodule Flay do
|
||||||
|
use GenServer
|
||||||
|
use Tres.Controller
|
||||||
|
|
||||||
|
import Logger
|
||||||
|
|
||||||
|
defmodule State do
|
||||||
|
defstruct [
|
||||||
|
datapath_id: nil,
|
||||||
|
tester_pid: nil,
|
||||||
|
conn_ref: nil,
|
||||||
|
reply_to: nil
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_link(datapath, args) do
|
||||||
|
GenServer.start_link(__MODULE__, [datapath, args], name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def init(args) do
|
||||||
|
state = init_controller(args)
|
||||||
|
init_bridge(state.datapath_id)
|
||||||
|
{:ok, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_call(:port_desc_stats, from, state) do
|
||||||
|
send_message(PortDesc.Request.new, state.datapath_id)
|
||||||
|
{:noreply, %{state|reply_to: from}}
|
||||||
|
end
|
||||||
|
def handle_call(:desc_stats, from, state) do
|
||||||
|
send_message(Desc.Request.new, state.datapath_id)
|
||||||
|
{:noreply, %{state|reply_to: from}}
|
||||||
|
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)
|
||||||
|
{:noreply, %{state|tester_pid: tester_pid}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info(%ErrorMsg{} = error, state) do
|
||||||
|
send(state.tester_pid, error)
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
def handle_info(%PortDesc.Reply{} = desc, state) do
|
||||||
|
GenServer.reply(state.reply_to, desc)
|
||||||
|
{:noreply, %{state|reply_to: nil}}
|
||||||
|
end
|
||||||
|
def handle_info(%Desc.Reply{} = desc, state) do
|
||||||
|
GenServer.reply(state.reply_to, desc)
|
||||||
|
{:noreply, %{state|reply_to: nil}}
|
||||||
|
end
|
||||||
|
# `Catch all` function is required.
|
||||||
|
def handle_info(info, state) do
|
||||||
|
:ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}")
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
# private functions
|
||||||
|
|
||||||
|
defp flow_opts_to_ofp_print(flow_opts) do
|
||||||
|
flow_opts
|
||||||
|
|> FlowMod.new
|
||||||
|
|> Openflow.to_binary
|
||||||
|
|> binary_to_space_delimited_hex
|
||||||
|
|> ofp_print_cmd
|
||||||
|
|> IO.inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ofp_print_cmd(print_args) do
|
||||||
|
IO.inspect("\n")
|
||||||
|
{result, _code} = System.cmd("ovs-ofctl", ["ofp-print", "#{print_args}"])
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
defp binary_to_space_delimited_hex(binary) do
|
||||||
|
binary
|
||||||
|
|> split_to_hex_string
|
||||||
|
|> Enum.join(" ")
|
||||||
|
|> String.downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
defp split_to_hex_string(binary) do
|
||||||
|
for <<int <- binary>>, do: integer_to_hex(int)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp integer_to_hex(int) do
|
||||||
|
case Integer.to_string(int, 16) do
|
||||||
|
<<d>> -> <<48, d>>
|
||||||
|
dd -> dd
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp init_controller([datapath_id, tester_pid]) do
|
||||||
|
conn_ref = SwitchRegistry.monitor(datapath_id)
|
||||||
|
%State{
|
||||||
|
datapath_id: datapath_id,
|
||||||
|
tester_pid: tester_pid,
|
||||||
|
conn_ref: conn_ref
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp init_bridge(datapath_id) do
|
||||||
|
send_flow_mod_delete(datapath_id, table_id: :all)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,5 +1,439 @@
|
||||||
defmodule FlayTest do
|
defmodule FlayTest do
|
||||||
use ExUnit.Case
|
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
|
end
|
||||||
|
|
|
||||||
40
test/pf.ex
Normal file
40
test/pf.ex
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
defmodule Pf do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
defmodule State do
|
||||||
|
defstruct [
|
||||||
|
ifname: nil,
|
||||||
|
pcap_ref: nil,
|
||||||
|
tester_pid: nil
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_link(ifname, pid) do
|
||||||
|
ifname = String.to_charlist(ifname)
|
||||||
|
GenServer.start_link(__MODULE__, [ifname, pid])
|
||||||
|
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}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_cast({:inject, packet}, state) do
|
||||||
|
:epcap.send(state.pcap_ref, packet)
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
def handle_cast(:stop, state) do
|
||||||
|
{:stop, :normal, state}
|
||||||
|
end
|
||||||
|
def handle_cast(_req, state) do
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info({:packet, _dlt, _time, _len, data}, state) do
|
||||||
|
send(state.tester_pid, data)
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
def handle_info(_info, state) do
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1 +1 @@
|
||||||
ExUnit.start()
|
ExUnit.start(trace: true)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue