Confession of guilt
This commit is contained in:
parent
b592219bd3
commit
2974723807
8 changed files with 507 additions and 13 deletions
|
|
@ -1047,6 +1047,25 @@ defmodule Openflow.Enums do
|
||||||
add: 0,
|
add: 0,
|
||||||
delete: 1,
|
delete: 1,
|
||||||
clear: 2
|
clear: 2
|
||||||
|
],
|
||||||
|
|
||||||
|
table_feature_prop_type: [
|
||||||
|
instructions: 0,
|
||||||
|
instructions_miss: 1,
|
||||||
|
next_tables: 2,
|
||||||
|
next_tables_miss: 3,
|
||||||
|
write_actions: 4,
|
||||||
|
write_actions_miss: 5,
|
||||||
|
apply_actions: 6,
|
||||||
|
apply_actions_miss: 7,
|
||||||
|
match: 8,
|
||||||
|
wildcards: 10,
|
||||||
|
write_setfield: 12,
|
||||||
|
write_setfield_miss: 13,
|
||||||
|
apply_setfield: 14,
|
||||||
|
apply_setfield_miss: 15,
|
||||||
|
experimenter: 0xfffe,
|
||||||
|
experimenter_miss: 0xffff
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ defmodule Openflow.Instruction.Experimenter do
|
||||||
|
|
||||||
alias __MODULE__
|
alias __MODULE__
|
||||||
|
|
||||||
def new(exp_id, data) do
|
def new(exp_id, data \\ "") do
|
||||||
%Experimenter{exp_id: exp_id, data: data}
|
%Experimenter{exp_id: exp_id, data: data}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,27 +33,54 @@ defmodule Openflow.Match do
|
||||||
end
|
end
|
||||||
|
|
||||||
def codec_header(oxm_field) when is_atom(oxm_field) do
|
def codec_header(oxm_field) when is_atom(oxm_field) do
|
||||||
|
oxm_field = case has_mask(oxm_field) do
|
||||||
|
1 ->
|
||||||
|
string = to_string(oxm_field)
|
||||||
|
"masked_" <> field = string
|
||||||
|
String.to_atom(field)
|
||||||
|
0 ->
|
||||||
|
oxm_field
|
||||||
|
end
|
||||||
case Openflow.Match.Field.vendor_of(oxm_field) do
|
case Openflow.Match.Field.vendor_of(oxm_field) do
|
||||||
oxm_class when oxm_class in [:nxm_0, :nxm_1, :openflow_basic, :packet_register] ->
|
oxm_class when oxm_class in [:nxm_0, :nxm_1, :openflow_basic, :packet_register] ->
|
||||||
oxm_class_int = Openflow.Enums.to_int(oxm_class, :oxm_class)
|
oxm_class_int = Openflow.Enums.to_int(oxm_class, :oxm_class)
|
||||||
oxm_field_int = Openflow.Enums.to_int(oxm_field, oxm_class)
|
oxm_field_int = Openflow.Enums.to_int(oxm_field, oxm_class)
|
||||||
oxm_length = div(Openflow.Match.Field.n_bits_of(oxm_field), 8)
|
oxm_length = div(Openflow.Match.Field.n_bits_of(oxm_field), 8)
|
||||||
<<oxm_class_int::16, oxm_field_int::7, 0::1, oxm_length::8>>
|
has_mask = has_mask(oxm_field)
|
||||||
experimenter when experimenter in [:nicira_ext_match, :onf_ext_match] ->
|
<<oxm_class_int::16, oxm_field_int::7, has_mask::1, oxm_length::8>>
|
||||||
|
experimenter when experimenter in [:nicira_ext_match, :onf_ext_match, :hp_ext_match] ->
|
||||||
oxm_class_int = 0xffff
|
oxm_class_int = 0xffff
|
||||||
experimenter_int = Openflow.Enums.to_int(experimenter, :experimenter_oxm_vendors)
|
experimenter_int = Openflow.Enums.to_int(experimenter, :experimenter_oxm_vendors)
|
||||||
oxm_field_int = Openflow.Enums.to_int(oxm_field, experimenter)
|
oxm_field_int = Openflow.Enums.to_int(oxm_field, experimenter)
|
||||||
oxm_length = div(Openflow.Match.Field.n_bits_of(oxm_field) + 4, 8)
|
oxm_length = div(Openflow.Match.Field.n_bits_of(oxm_field) + 4, 8)
|
||||||
<<oxm_class_int::16, oxm_field_int::7, 0::1, oxm_length::8, experimenter_int::32>>
|
has_mask = has_mask(oxm_field)
|
||||||
|
<<oxm_class_int::16, oxm_field_int::7, has_mask::1, oxm_length::8, experimenter_int::32>>
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
def codec_header(<<oxm_class_int::16, oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8>>) do
|
def codec_header(<<oxm_class_int::16, oxm_field_int::7, oxm_has_mask::1, _oxm_length::8>>) do
|
||||||
oxm_class = Openflow.Enums.to_atom(oxm_class_int, :oxm_class)
|
oxm_class = Openflow.Enums.to_atom(oxm_class_int, :oxm_class)
|
||||||
Openflow.Enums.to_atom(oxm_field_int, oxm_class)
|
case oxm_has_mask do
|
||||||
|
0 -> Openflow.Enums.to_atom(oxm_field_int, oxm_class)
|
||||||
|
1 ->
|
||||||
|
field_str =
|
||||||
|
oxm_field_int
|
||||||
|
|> Openflow.Enums.to_atom(oxm_class)
|
||||||
|
|> to_string
|
||||||
|
String.to_atom("masked_" <> field_str)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
def codec_header(<<0xffff::16, oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, experimenter_int::32>>) do
|
def codec_header(<<0xffff::16, oxm_field_int::7, oxm_has_mask::1, _oxm_length::8, experimenter_int::32>>) do
|
||||||
experimenter = Openflow.Enums.to_atom(experimenter_int, :experimenter_oxm_vendors)
|
experimenter = Openflow.Enums.to_atom(experimenter_int, :experimenter_oxm_vendors)
|
||||||
Openflow.Enums.to_atom(oxm_field_int, experimenter)
|
Openflow.Enums.to_atom(oxm_field_int, experimenter)
|
||||||
|
case oxm_has_mask do
|
||||||
|
0 -> Openflow.Enums.to_atom(oxm_field_int, experimenter)
|
||||||
|
1 ->
|
||||||
|
field_str =
|
||||||
|
oxm_field_int
|
||||||
|
|> Openflow.Enums.to_atom(experimenter)
|
||||||
|
|> to_string
|
||||||
|
String.to_atom("masked_" <> field_str)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def header_size(<<_oxm_class_int::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _::bytes>>),
|
def header_size(<<_oxm_class_int::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _::bytes>>),
|
||||||
|
|
@ -151,4 +178,16 @@ defmodule Openflow.Match do
|
||||||
match_class = Openflow.Match.Field.vendor_of(field_name)
|
match_class = Openflow.Match.Field.vendor_of(field_name)
|
||||||
%{class: match_class, field: field_name, has_mask: false, value: value_bin}
|
%{class: match_class, field: field_name, has_mask: false, value: value_bin}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp has_mask(oxm_field) when is_atom(oxm_field) do
|
||||||
|
has_mask? =
|
||||||
|
oxm_field
|
||||||
|
|> to_string
|
||||||
|
|> String.match?(~r/^masked_/)
|
||||||
|
if has_mask? do
|
||||||
|
1
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
297
lib/openflow/multipart/table_features/body.ex
Normal file
297
lib/openflow/multipart/table_features/body.ex
Normal file
|
|
@ -0,0 +1,297 @@
|
||||||
|
defmodule Openflow.Multipart.TableFeatures.Body do
|
||||||
|
defstruct [
|
||||||
|
table_id: 0,
|
||||||
|
name: "",
|
||||||
|
metadata_match: 0,
|
||||||
|
metadata_write: 0,
|
||||||
|
config: [],
|
||||||
|
max_entries: 0,
|
||||||
|
instructions: nil,
|
||||||
|
instructions_miss: nil,
|
||||||
|
next_tables: nil,
|
||||||
|
next_tables_miss: nil,
|
||||||
|
write_actions: nil,
|
||||||
|
write_actions_miss: nil,
|
||||||
|
apply_actions: nil,
|
||||||
|
apply_actions_miss: nil,
|
||||||
|
match: nil,
|
||||||
|
wildcards: nil,
|
||||||
|
write_setfield: nil,
|
||||||
|
write_setfield_miss: nil,
|
||||||
|
apply_setfield: nil,
|
||||||
|
apply_setfield_miss: nil
|
||||||
|
]
|
||||||
|
|
||||||
|
alias __MODULE__
|
||||||
|
|
||||||
|
@max_table_name_len 32
|
||||||
|
@prop_header_length 4
|
||||||
|
@table_features_length 64
|
||||||
|
|
||||||
|
@instructions 0
|
||||||
|
@instructions_miss 1
|
||||||
|
@next_tables 2
|
||||||
|
@next_tables_miss 3
|
||||||
|
@write_actions 4
|
||||||
|
@write_actions_miss 5
|
||||||
|
@apply_actions 6
|
||||||
|
@apply_actions_miss 7
|
||||||
|
@match 8
|
||||||
|
@wildcards 10
|
||||||
|
@write_setfield 12
|
||||||
|
@write_setfield_miss 13
|
||||||
|
@apply_setfield 14
|
||||||
|
@apply_setfield_miss 15
|
||||||
|
|
||||||
|
@prop_keys [
|
||||||
|
:instructions,
|
||||||
|
:instructions_miss,
|
||||||
|
:next_tables,
|
||||||
|
:next_tables_miss,
|
||||||
|
:write_actions,
|
||||||
|
:write_actions_miss,
|
||||||
|
:apply_actions,
|
||||||
|
:apply_actions_miss,
|
||||||
|
:match,
|
||||||
|
:wildcards,
|
||||||
|
:write_setfield,
|
||||||
|
:write_setfield_miss,
|
||||||
|
:apply_setfield,
|
||||||
|
:apply_setfield_miss
|
||||||
|
]
|
||||||
|
|
||||||
|
def new(options) do
|
||||||
|
%Body{
|
||||||
|
table_id: options[:table_id] || 0,
|
||||||
|
name: options[:name] || "",
|
||||||
|
metadata_match: options[:metadata_match] || 0,
|
||||||
|
metadata_write: options[:metadata_write] || 0,
|
||||||
|
config: options[:config] || [],
|
||||||
|
max_entries: options[:max_entries] || 0,
|
||||||
|
instructions: options[:instructions],
|
||||||
|
instructions_miss: options[:instructions_miss],
|
||||||
|
next_tables: options[:next_tables],
|
||||||
|
next_tables_miss: options[:next_tables_miss],
|
||||||
|
write_actions: options[:write_actions],
|
||||||
|
write_actions_miss: options[:write_actions_miss],
|
||||||
|
apply_actions: options[:apply_actions],
|
||||||
|
apply_actions_miss: options[:apply_actions_miss],
|
||||||
|
match: options[:match],
|
||||||
|
wildcards: options[:wildcards],
|
||||||
|
write_setfield: options[:write_setfield],
|
||||||
|
write_setfield_miss: options[:write_setfield_miss],
|
||||||
|
apply_setfield: options[:apply_setfield],
|
||||||
|
apply_setfield_miss: options[:apply_setfield_miss]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(binary) do
|
||||||
|
do_read([], binary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_binary(features) do
|
||||||
|
do_to_binary("", features)
|
||||||
|
end
|
||||||
|
|
||||||
|
# private functions
|
||||||
|
|
||||||
|
defp do_to_binary(acc, []), do: acc
|
||||||
|
defp do_to_binary(acc, [table|rest]) do
|
||||||
|
do_to_binary(<<acc::bytes, (encode(table))::bytes>>, rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_read(acc, ""), do: Enum.reverse(acc)
|
||||||
|
defp do_read(acc, <<length::16, _::bytes>> = binary) do
|
||||||
|
<<features_bin::size(length)-bytes, rest::bytes>> = binary
|
||||||
|
do_read([decode(features_bin)|acc], rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decode(<<_length::16, table_id::8, _::size(5)-unit(8),
|
||||||
|
name_bin::size(@max_table_name_len)-bytes,
|
||||||
|
metadata_match::64, metadata_write::64,
|
||||||
|
config_int::32, max_entries::32, props_bin::bytes>>) do
|
||||||
|
name = Openflow.Utils.decode_string(name_bin)
|
||||||
|
config = Openflow.Enums.int_to_flags(config_int, :table_config)
|
||||||
|
body = %Body{table_id: table_id,
|
||||||
|
name: name,
|
||||||
|
metadata_match: metadata_match,
|
||||||
|
metadata_write: metadata_write,
|
||||||
|
config: config,
|
||||||
|
max_entries: max_entries}
|
||||||
|
decode_props(body, props_bin)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode(table) do
|
||||||
|
filter_fn = fn(key) -> not is_nil(Map.get(table, key)) end
|
||||||
|
keys = Enum.filter(@prop_keys, filter_fn)
|
||||||
|
props_bin = encode_props("", table, keys)
|
||||||
|
length = @table_features_length + byte_size(props_bin)
|
||||||
|
%Body{table_id: table_id,
|
||||||
|
name: name,
|
||||||
|
metadata_match: metadata_match,
|
||||||
|
metadata_write: metadata_write,
|
||||||
|
config: config,
|
||||||
|
max_entries: max_entries} = table
|
||||||
|
config_int = Openflow.Enums.flags_to_int(config, :table_config)
|
||||||
|
name_bin = Openflow.Utils.encode_string(name, @max_table_name_len)
|
||||||
|
<<length::16, table_id::8, 0::size(5)-unit(8),
|
||||||
|
name_bin::bytes, metadata_match::64, metadata_write::64,
|
||||||
|
config_int::32, max_entries::32, props_bin::bytes>>
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decode_props(body, ""), do: body
|
||||||
|
defp decode_props(body, <<type_int::16, length::16, tail::bytes>>)
|
||||||
|
when type_int == @instructions or type_int == @instructions_miss do
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
value_length = length - @prop_header_length
|
||||||
|
<<instructions_bin::size(value_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
|
||||||
|
instructions = decode_instructions([], instructions_bin)
|
||||||
|
type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type)
|
||||||
|
body
|
||||||
|
|> struct(%{type => instructions})
|
||||||
|
|> decode_props(rest)
|
||||||
|
end
|
||||||
|
defp decode_props(body, <<type_int::16, length::16, tail::bytes>>)
|
||||||
|
when type_int == @next_tables or type_int == @next_tables_miss do
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
value_length = length - @prop_header_length
|
||||||
|
<<next_tables_bin::size(value_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
|
||||||
|
next_tables = for <<table_id::8 <- next_tables_bin>>, do: table_id
|
||||||
|
type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type)
|
||||||
|
body
|
||||||
|
|> struct(%{type => next_tables})
|
||||||
|
|> decode_props(rest)
|
||||||
|
end
|
||||||
|
defp decode_props(body, <<type_int::16, length::16, tail::bytes>>)
|
||||||
|
when type_int == @write_actions or
|
||||||
|
type_int == @write_actions_miss or
|
||||||
|
type_int == @apply_actions or
|
||||||
|
type_int == @apply_actions_miss do
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
value_length = length - @prop_header_length
|
||||||
|
<<actions_bin::size(value_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
|
||||||
|
actions = decode_actions([], actions_bin)
|
||||||
|
type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type)
|
||||||
|
body
|
||||||
|
|> struct(%{type => actions})
|
||||||
|
|> decode_props(rest)
|
||||||
|
end
|
||||||
|
defp decode_props(body, <<type_int::16, length::16, tail::bytes>>)
|
||||||
|
when type_int == @match or
|
||||||
|
type_int == @wildcards or
|
||||||
|
type_int == @write_setfield or
|
||||||
|
type_int == @write_setfield_miss or
|
||||||
|
type_int == @apply_setfield or
|
||||||
|
type_int == @apply_setfield_miss do
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
value_length = length - @prop_header_length
|
||||||
|
<<matches_bin::size(value_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
|
||||||
|
matches = decode_matches([], matches_bin)
|
||||||
|
type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type)
|
||||||
|
body
|
||||||
|
|> struct(%{type => matches})
|
||||||
|
|> decode_props(rest)
|
||||||
|
end
|
||||||
|
defp decode_props(body, <<_type_int::16, length::16, tail::bytes>>) do
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
value_length = length - @prop_header_length
|
||||||
|
<<_::size(value_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
|
||||||
|
decode_props(body, rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_props(acc, _table, []), do: acc
|
||||||
|
defp encode_props(acc, table, [type|rest])
|
||||||
|
when type == :instructions or type == :instructions_miss do
|
||||||
|
type_int = Openflow.Enums.to_int(type, :table_feature_prop_type)
|
||||||
|
instructions_bin = encode_instructions("", Map.get(table, type))
|
||||||
|
length = @prop_header_length + byte_size(instructions_bin)
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
body = <<instructions_bin::bytes, 0::size(pad_length)-unit(8)>>
|
||||||
|
encode_props(<<acc::bytes, type_int::16, length::16, body::bytes>>, table, rest)
|
||||||
|
end
|
||||||
|
defp encode_props(acc, table, [type|rest])
|
||||||
|
when type == :next_tables or type == :next_tables_miss do
|
||||||
|
type_int = Openflow.Enums.to_int(type, :table_feature_prop_type)
|
||||||
|
next_tables_bin = to_string(Map.get(table, type))
|
||||||
|
length = @prop_header_length + byte_size(next_tables_bin)
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
body = <<next_tables_bin::bytes, 0::size(pad_length)-unit(8)>>
|
||||||
|
encode_props(<<acc::bytes, type_int::16, length::16, body::bytes>>, table, rest)
|
||||||
|
end
|
||||||
|
defp encode_props(acc, table, [type|rest])
|
||||||
|
when (type == :write_actions or
|
||||||
|
type == :write_actions_miss or
|
||||||
|
type == :apply_actions or
|
||||||
|
type == :apply_actions_miss) do
|
||||||
|
type_int = Openflow.Enums.to_int(type, :table_feature_prop_type)
|
||||||
|
actions_bin = encode_actions("", Map.get(table, type))
|
||||||
|
length = @prop_header_length + byte_size(actions_bin)
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
body = <<actions_bin::bytes, 0::size(pad_length)-unit(8)>>
|
||||||
|
encode_props(<<acc::bytes, type_int::16, length::16, body::bytes>>, table, rest)
|
||||||
|
end
|
||||||
|
defp encode_props(acc, table, [type|rest])
|
||||||
|
when (type == :match or
|
||||||
|
type == :wildcards or
|
||||||
|
type == :write_setfield or
|
||||||
|
type == :write_setfield_miss or
|
||||||
|
type == :apply_setfield or
|
||||||
|
type == :apply_setfield_miss) do
|
||||||
|
type_int = Openflow.Enums.to_int(type, :table_feature_prop_type)
|
||||||
|
matches_bin = encode_matches("", Map.get(table, type))
|
||||||
|
length = @prop_header_length + byte_size(matches_bin)
|
||||||
|
pad_length = Openflow.Utils.pad_length(length, 8)
|
||||||
|
body = <<matches_bin::bytes, 0::size(pad_length)-unit(8)>>
|
||||||
|
encode_props(<<acc::bytes, type_int::16, length::16, body::bytes>>, table, rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decode_instructions(acc, ""), do: Enum.reverse(acc)
|
||||||
|
defp decode_instructions(acc, <<0xffff::16, _::16, exp_id::32, rest::bytes>>) do
|
||||||
|
decode_instructions([Openflow.Instruction.Experimenter.new(exp_id)|acc], rest)
|
||||||
|
end
|
||||||
|
defp decode_instructions(acc, <<type_int::16, _::16, rest::bytes>>) do
|
||||||
|
instruction = Openflow.Enums.to_atom(type_int, :instruction_type)
|
||||||
|
decode_instructions([instruction|acc], rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_instructions(acc, []), do: acc
|
||||||
|
defp encode_instructions(acc, [%Openflow.Instruction.Experimenter{exp_id: exp_id}|rest]) do
|
||||||
|
encode_instructions(<<acc::bytes, 0xffff::16, 8::16, exp_id::32>>, rest)
|
||||||
|
end
|
||||||
|
defp encode_instructions(acc, [type|rest]) do
|
||||||
|
type_int = Openflow.Enums.to_int(type, :instruction_type)
|
||||||
|
encode_instructions(<<acc::bytes, type_int::16, 4::16>>, rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decode_actions(acc, ""), do: Enum.reverse(acc)
|
||||||
|
defp decode_actions(acc, <<0xffff::16, _::16, exp_id::32, rest::bytes>>) do
|
||||||
|
decode_actions([Openflow.Action.Experimenter.new(exp_id)|acc], rest)
|
||||||
|
end
|
||||||
|
defp decode_actions(acc, <<type_int::16, _::16, rest::bytes>>) do
|
||||||
|
action = Openflow.Enums.to_atom(type_int, :action_type)
|
||||||
|
decode_actions([action|acc], rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_actions(acc, []), do: acc
|
||||||
|
defp encode_actions(acc, [%Openflow.Action.Experimenter{exp_id: exp_id}|rest]) do
|
||||||
|
encode_actions(<<acc::bytes, 0xffff::16, 8::16, exp_id::32>>, rest)
|
||||||
|
end
|
||||||
|
defp encode_actions(acc, [type|rest]) do
|
||||||
|
type_int = Openflow.Enums.to_int(type, :action_type)
|
||||||
|
encode_actions(<<acc::bytes, type_int::16, 4::16>>, rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decode_matches(acc, ""), do: Enum.reverse(acc)
|
||||||
|
defp decode_matches(acc, binary) do
|
||||||
|
length = Openflow.Match.header_size(binary)
|
||||||
|
<<header_bin::size(length)-bytes, rest::bytes>> = binary
|
||||||
|
field = Openflow.Match.codec_header(header_bin)
|
||||||
|
decode_matches([field|acc], rest)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_matches(acc, []), do: acc
|
||||||
|
defp encode_matches(acc, [field|rest]) do
|
||||||
|
header_bin = Openflow.Match.codec_header(field)
|
||||||
|
encode_matches(<<acc::bytes, header_bin::bytes>>, rest)
|
||||||
|
end
|
||||||
|
end
|
||||||
41
lib/openflow/multipart/table_features/reply.ex
Normal file
41
lib/openflow/multipart/table_features/reply.ex
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
defmodule Openflow.Multipart.TableFeatures.Reply do
|
||||||
|
defstruct(
|
||||||
|
version: 4,
|
||||||
|
xid: 0,
|
||||||
|
datapath_id: nil, # virtual field
|
||||||
|
aux_id: nil,
|
||||||
|
flags: [],
|
||||||
|
tables: []
|
||||||
|
)
|
||||||
|
|
||||||
|
alias __MODULE__
|
||||||
|
alias Openflow.Multipart.TableFeatures.Body
|
||||||
|
|
||||||
|
def ofp_type, do: 18
|
||||||
|
|
||||||
|
def new(tables \\ []) do
|
||||||
|
%Reply{tables: tables}
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(<<tables_bin::bytes>>) do
|
||||||
|
tables = Body.read(tables_bin)
|
||||||
|
%Reply{tables: tables}
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_binary(msg) do
|
||||||
|
header_bin = Openflow.Multipart.Reply.header(msg)
|
||||||
|
%Reply{tables: tables} = msg
|
||||||
|
tables_bin = Openflow.Multipart.TableFeatures.Body.to_binary(tables)
|
||||||
|
<<header_bin::bytes, tables_bin::bytes>>
|
||||||
|
end
|
||||||
|
|
||||||
|
def append_body(%Reply{tables: tables} = message, %Reply{flags: [:more], tables: continue}) do
|
||||||
|
%{message|tables: [continue|tables]}
|
||||||
|
end
|
||||||
|
def append_body(%Reply{tables: tables} = message, %Reply{flags: [], tables: continue}) do
|
||||||
|
new_tables = [continue|tables]
|
||||||
|
|> Enum.reverse
|
||||||
|
|> List.flatten
|
||||||
|
%{message|tables: new_tables}
|
||||||
|
end
|
||||||
|
end
|
||||||
41
lib/openflow/multipart/table_features/request.ex
Normal file
41
lib/openflow/multipart/table_features/request.ex
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
defmodule Openflow.Multipart.TableFeatures.Request do
|
||||||
|
defstruct(
|
||||||
|
version: 4,
|
||||||
|
xid: 0,
|
||||||
|
datapath_id: nil, # virtual field
|
||||||
|
aux_id: nil,
|
||||||
|
flags: [],
|
||||||
|
tables: []
|
||||||
|
)
|
||||||
|
|
||||||
|
alias __MODULE__
|
||||||
|
alias Openflow.Multipart.TableFeatures.Body
|
||||||
|
|
||||||
|
def ofp_type, do: 18
|
||||||
|
|
||||||
|
def new(tables \\ []) do
|
||||||
|
%Request{tables: tables}
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(<<tables_bin::bytes>>) do
|
||||||
|
tables = Body.read(tables_bin)
|
||||||
|
%Request{tables: tables}
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_binary(msg) do
|
||||||
|
header_bin = Openflow.Multipart.Request.header(msg)
|
||||||
|
%Request{tables: tables} = msg
|
||||||
|
tables_bin = Openflow.Multipart.TableFeatures.Body.to_binary(tables)
|
||||||
|
<<header_bin::bytes, tables_bin::bytes>>
|
||||||
|
end
|
||||||
|
|
||||||
|
def append_body(%Request{tables: tables} = message, %Request{flags: [:more], tables: continue}) do
|
||||||
|
%{message|tables: [continue|tables]}
|
||||||
|
end
|
||||||
|
def append_body(%Request{tables: tables} = message, %Request{flags: [], tables: continue}) do
|
||||||
|
new_tables = [continue|tables]
|
||||||
|
|> Enum.reverse
|
||||||
|
|> List.flatten
|
||||||
|
%{message|tables: new_tables}
|
||||||
|
end
|
||||||
|
end
|
||||||
64
test/flay.ex
64
test/flay.ex
|
|
@ -9,7 +9,8 @@ defmodule Flay do
|
||||||
datapath_id: nil,
|
datapath_id: nil,
|
||||||
tester_pid: nil,
|
tester_pid: nil,
|
||||||
conn_ref: nil,
|
conn_ref: nil,
|
||||||
reply_to: nil
|
reply_to: nil,
|
||||||
|
default_profile: nil,
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -19,6 +20,8 @@ defmodule Flay do
|
||||||
|
|
||||||
def init(args) do
|
def init(args) do
|
||||||
state = init_controller(args)
|
state = init_controller(args)
|
||||||
|
TableFeatures.Request.new
|
||||||
|
|> send_message(state.datapath_id)
|
||||||
init_bridge(state.datapath_id)
|
init_bridge(state.datapath_id)
|
||||||
{:ok, state}
|
{:ok, state}
|
||||||
end
|
end
|
||||||
|
|
@ -48,6 +51,10 @@ defmodule Flay do
|
||||||
send_flow_mod_delete(state.datapath_id, match: Match.new)
|
send_flow_mod_delete(state.datapath_id, match: Match.new)
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
def handle_cast(:restore_flow_profile, state) do
|
||||||
|
send_message(state.default_profile, state.datapath_id)
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_info(%ErrorMsg{} = error, state) do
|
def handle_info(%ErrorMsg{} = error, state) do
|
||||||
send(state.tester_pid, error)
|
send(state.tester_pid, error)
|
||||||
|
|
@ -57,6 +64,9 @@ defmodule Flay do
|
||||||
send(state.tester_pid, pktin)
|
send(state.tester_pid, pktin)
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
def handle_info(%TableFeatures.Reply{} = table, state) do
|
||||||
|
{:noreply, %{state|default_profile: table}}
|
||||||
|
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}}
|
||||||
|
|
@ -70,8 +80,8 @@ defmodule Flay do
|
||||||
{:noreply, %{state|reply_to: nil}}
|
{:noreply, %{state|reply_to: nil}}
|
||||||
end
|
end
|
||||||
# `Catch all` function is required.
|
# `Catch all` function is required.
|
||||||
def handle_info(_info, state) do
|
def handle_info(info, state) do
|
||||||
# :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}")
|
:ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}")
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -119,6 +129,54 @@ defmodule Flay do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp init_bridge(datapath_id) do
|
defp init_bridge(datapath_id) do
|
||||||
|
tables = [
|
||||||
|
TableFeatures.Body.new(
|
||||||
|
table_id: 0,
|
||||||
|
max_entries: 2000,
|
||||||
|
instructions: [
|
||||||
|
Openflow.Instruction.ApplyActions,
|
||||||
|
Openflow.Instruction.GotoTable
|
||||||
|
],
|
||||||
|
next_tables: [1],
|
||||||
|
apply_actions: [
|
||||||
|
Openflow.Action.Output,
|
||||||
|
Openflow.Action.PushVlan,
|
||||||
|
Openflow.Action.PopVlan,
|
||||||
|
Openflow.Action.SetField
|
||||||
|
],
|
||||||
|
match: [
|
||||||
|
:in_port, :eth_src, :eth_dst, :eth_type, :vlan_vid,
|
||||||
|
:ip_proto, :ipv4_src, :ipv4_dst, :tcp_dst,:udp_dst
|
||||||
|
],
|
||||||
|
apply_setfield: [
|
||||||
|
:eth_src, :eth_dst, :vlan_vid
|
||||||
|
]
|
||||||
|
),
|
||||||
|
TableFeatures.Body.new(
|
||||||
|
table_id: 0,
|
||||||
|
max_entries: 2000,
|
||||||
|
instructions: [
|
||||||
|
Openflow.Instruction.ApplyActions
|
||||||
|
],
|
||||||
|
next_tables: [],
|
||||||
|
apply_actions: [
|
||||||
|
Openflow.Action.Output,
|
||||||
|
Openflow.Action.PushVlan,
|
||||||
|
Openflow.Action.PopVlan,
|
||||||
|
Openflow.Action.SetField
|
||||||
|
],
|
||||||
|
match: [
|
||||||
|
:in_port, :eth_src, :eth_dst, :eth_type, :vlan_vid,
|
||||||
|
:ip_proto, :ipv4_src, :ipv4_dst, :tcp_dst,:udp_dst
|
||||||
|
],
|
||||||
|
apply_setfield: [
|
||||||
|
:eth_src, :eth_dst, :vlan_vid, :ipv4_src, :ipv4_dst,
|
||||||
|
:arp_spa, :arp_tpa, :arp_tha
|
||||||
|
]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
TableFeatures.Request.new(tables)
|
||||||
|
|> send_message(datapath_id)
|
||||||
send_flow_mod_delete(datapath_id, table_id: :all)
|
send_flow_mod_delete(datapath_id, table_id: :all)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ defmodule FlogTest do
|
||||||
Code.load_file("test/pf.ex")
|
Code.load_file("test/pf.ex")
|
||||||
|
|
||||||
# GIVEN
|
# GIVEN
|
||||||
setup do
|
setup_all do
|
||||||
setup_applications()
|
setup_applications()
|
||||||
wait_for_connected()
|
wait_for_connected()
|
||||||
ports = get_ports_desc()
|
ports = get_ports_desc()
|
||||||
|
|
@ -53,8 +53,7 @@ defmodule FlogTest do
|
||||||
]
|
]
|
||||||
|
|
||||||
on_exit fn ->
|
on_exit fn ->
|
||||||
print_flows()
|
GenServer.cast(Flay, :restore_flow_profile)
|
||||||
GenServer.cast(Flay, :flow_del)
|
|
||||||
end
|
end
|
||||||
{:ok, options}
|
{:ok, options}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue