diff --git a/lib/openflow/multipart/aggregate/request.ex b/lib/openflow/multipart/aggregate/request.ex index c211684..089d7da 100644 --- a/lib/openflow/multipart/aggregate/request.ex +++ b/lib/openflow/multipart/aggregate/request.ex @@ -33,9 +33,6 @@ defmodule Openflow.Multipart.Aggregate.Request do def ofp_type, do: 18 @spec new( - version: 4, - datapath_id: String.t(), - aux_id: 0..0xFF | nil, xid: 0..0xFFFFFFFF, table_id: 0..0xFF | :all | :max, out_port: Openflow.Port.no(), diff --git a/lib/openflow/multipart/flow/reply.ex b/lib/openflow/multipart/flow/reply.ex index 135a222..0618e7a 100644 --- a/lib/openflow/multipart/flow/reply.ex +++ b/lib/openflow/multipart/flow/reply.ex @@ -11,12 +11,19 @@ defmodule Openflow.Multipart.Flow.Reply do alias __MODULE__ - def ofp_type, do: 18 + @type t :: %Reply{ + version: 4, + datapath_id: String.t() | nil, + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF, + flags: [:more], + flows: [Openflow.Multipart.FlowStats.t()] + } - def new(flows \\ []) do - %Reply{flows: flows} - end + @spec ofp_type() :: 19 + def ofp_type, do: 19 + @spec read(<<_::16, _::_*384>>) :: t() def read(<>) do flows = Openflow.Multipart.FlowStats.read(flows_bin) %Reply{flows: flows} @@ -54,27 +61,53 @@ defmodule Openflow.Multipart.FlowStats do alias __MODULE__ - def read(binary) do - do_read([], binary) - end + @type t :: %FlowStats{ + table_id: 0..0xFF, + duration_sec: 0..0xFFFFFFFF, + duration_nsec: 0..0xFFFFFFFF, + priority: 0..0xFFFF, + idle_timeout: 0..0xFFFF, + hard_timeout: 0..0xFFFF, + flags: [:send_flow_rem | :delete_learned | :write_result], + cookie: 0..0xFFFFFFFFFFFFFFFF, + packet_count: 0..0xFFFFFFFFFFFFFFFF, + byte_count: 0..0xFFFFFFFFFFFFFFFF, + match: Openflow.Match.new(), + instructions: [Openflow.Instruction.instruction()] + } + + @spec read(<<_::_*8>>) :: [t()] + def read(binary), do: do_read([], binary) # private functions - defp do_read(acc, ""), do: Enum.reverse(acc) + @spec do_read(acc :: [t()], binary()) :: [t()] + defp do_read(acc, ""), do: acc + @spec do_read(acc :: [t()], <<_::16, _::_*8>>) :: [t()] defp do_read(acc, <> = binary) do <> = binary - do_read([codec(flow_stats_bin) | acc], rest) + do_read(acc ++ [codec(flow_stats_bin)], rest) end - defp codec( - <<_length::16, table_id_int::8, 0::8, duration_sec::32, duration_nsec::32, priority::16, - idle::16, hard::16, flags_int::16, _::size(4)-unit(8), cookie::64, packet_count::64, - byte_count::64, tail::bytes>> - ) do - {match, instructions_bin} = Openflow.Match.read(tail) - table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + defp codec(<< + _length::16, + table_id::8, + 0::8, + duration_sec::32, + duration_nsec::32, + priority::16, + idle::16, + hard::16, + flags_int::16, + _::size(4)-unit(8), + cookie::64, + packet_count::64, + byte_count::64, + tail::bytes + >>) do flags = Openflow.Enums.int_to_flags(flags_int, :flow_mod_flags) + {match, instructions_bin} = Openflow.Match.read(tail) instructions = Openflow.Instruction.read(instructions_bin) %FlowStats{ diff --git a/lib/openflow/multipart/flow/request.ex b/lib/openflow/multipart/flow/request.ex index c3b37b6..36fc004 100644 --- a/lib/openflow/multipart/flow/request.ex +++ b/lib/openflow/multipart/flow/request.ex @@ -4,58 +4,82 @@ defmodule Openflow.Multipart.Flow.Request do xid: 0, # virtual field datapath_id: nil, + aux_id: nil, flags: [], table_id: :all, out_port: :any, out_group: :any, cookie: 0, cookie_mask: 0, - match: [] + match: Openflow.Match.new() ) alias __MODULE__ + @type t :: %Request{ + version: 4, + datapath_id: String.t(), + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF, + table_id: 0..0xFF | :all | :max, + out_port: Openflow.Port.no(), + out_group: Openflow.GroupMod.id(), + cookie: 0..0xFFFFFFFFFFFFFFFF, + cookie_mask: 0..0xFFFFFFFFFFFFFFFF, + match: %Openflow.Match{fields: [map()], type: :oxm} + } + + @spec ofp_type() :: 18 def ofp_type, do: 18 + @spec new( + xid: 0..0xFFFFFFFF, + table_id: 0..0xFF | :all | :max, + out_port: Openflow.Port.no(), + out_group: Openflow.GroupMod.id(), + cookie: 0..0xFFFFFFFFFFFFFFFF, + cookie_mask: 0..0xFFFFFFFFFFFFFFFF, + match: %Openflow.Match{fields: [map()], type: :oxm} + ) :: t() def new(options \\ []) do - xid = Keyword.get(options, :xid, 0) - table_id = Keyword.get(options, :table_id, :all) - out_port = Keyword.get(options, :out_port, :any) - out_group = Keyword.get(options, :out_group, :any) - cookie = Keyword.get(options, :cookie, 0) - cookie_mask = Keyword.get(options, :cookie, 0) - match = Keyword.get(options, :match, Openflow.Match.new()) + %Request{ + xid: options[:xid] || 0, + table_id: options[:table_id] || :all, + out_port: options[:out_port] || :any, + out_group: options[:out_group] || :any, + cookie: options[:cookie] || 0, + cookie_mask: options[:cookie_mask] || 0, + match: options[:match] || Openflow.Match.new() + } + end + + @spec read(<<_::256, _::_*8>>) :: t() + def read(<< + table_id_int::8, + _::size(3)-unit(8), + out_port_int::32, + out_group_int::32, + _::size(4)-unit(8), + cookie::64, + cookie_mask::64, + match_bin::bytes + >>) do + match = + match_bin + |> Openflow.Match.read() + |> Kernel.elem(0) %Request{ - xid: xid, - table_id: table_id, - out_port: out_port, - out_group: out_group, - cookie: cookie, - cookie_mask: cookie_mask, - match: match - } - end - - def read( - <> - ) do - table_id = Openflow.Utils.get_enum(table_id_int, :table_id) - out_port = Openflow.Utils.get_enum(out_port_int, :openflow13_port_no) - out_group = Openflow.Utils.get_enum(out_group_int, :group_id) - {match, _rest} = Openflow.Match.read(match_bin) - - %Request{ - table_id: table_id, - out_port: out_port, - out_group: out_group, + table_id: Openflow.Utils.get_enum(table_id_int, :table_id), + out_port: Openflow.Utils.get_enum(out_port_int, :openflow13_port_no), + out_group: Openflow.Utils.get_enum(out_group_int, :group_id), cookie: cookie, cookie_mask: cookie_mask, match: match } end + @spec to_binary(t()) :: <<_::256, _::_*8>> def to_binary( %Request{ table_id: table_id, @@ -66,16 +90,16 @@ defmodule Openflow.Multipart.Flow.Request do match: match } = msg ) do - table_id_int = Openflow.Utils.get_enum(table_id, :table_id) - out_port_int = Openflow.Utils.get_enum(out_port, :openflow13_port_no) - out_group_int = Openflow.Utils.get_enum(out_group, :group_id) - match_bin = Openflow.Match.to_binary(match) - - body_bin = - <> - - header_bin = Openflow.Multipart.Request.header(msg) - <> + << + Openflow.Multipart.Request.header(msg)::bytes, + Openflow.Utils.get_enum(table_id, :table_id)::8, + 0::size(3)-unit(8), + Openflow.Utils.get_enum(out_port, :openflow13_port_no)::32, + Openflow.Utils.get_enum(out_group, :group_id)::32, + 0::size(4)-unit(8), + cookie::64, + cookie_mask::64, + Openflow.Match.to_binary(match)::bytes + >> end end diff --git a/test/lib/openflow/ofp_flow_stats_test.exs b/test/lib/openflow/ofp_flow_stats_test.exs new file mode 100644 index 0000000..0eecc19 --- /dev/null +++ b/test/lib/openflow/ofp_flow_stats_test.exs @@ -0,0 +1,141 @@ +defmodule OfpFlowStatsTest do + use ExUnit.Case + + describe "Openflow.Multipart.Flow.Request" do + test "with default values" do + flow_stats = + %Openflow.Multipart.Flow.Request{} + |> Map.to_list() + |> Openflow.Multipart.Flow.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert flow_stats.xid == 0 + assert flow_stats.table_id == :all + assert flow_stats.out_port == :any + assert flow_stats.out_group == :any + assert flow_stats.cookie == 0 + assert flow_stats.cookie_mask == 0 + assert flow_stats.match == [] + end + end + + describe "Openflow.Multipart.Flow.Reply" do + test "with test packet_data" do + flow_stats = + "test/packet_data/4-12-ofp_flow_stats_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.Flow.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + flows: [ + %Openflow.Multipart.FlowStats{ + byte_count: 0, + cookie: 0, + duration_nsec: 115_277_000, + duration_sec: 358, + flags: [], + hard_timeout: 0, + idle_timeout: 0, + instructions: [], + match: [], + packet_count: 0, + priority: 65535, + table_id: 0 + }, + %Openflow.Multipart.FlowStats{ + byte_count: 0, + cookie: 0, + duration_nsec: 115_055_000, + duration_sec: 358, + flags: [], + hard_timeout: 0, + idle_timeout: 0, + instructions: [ + %Openflow.Instruction.ApplyActions{ + actions: [%Openflow.Action.Output{max_len: 0, port_number: :normal}] + } + ], + match: [eth_type: 2054], + packet_count: 0, + priority: 65534, + table_id: 0 + }, + %Openflow.Multipart.FlowStats{ + byte_count: 238, + cookie: 0, + duration_nsec: 511_582_000, + duration_sec: 316_220, + flags: [], + hard_timeout: 0, + idle_timeout: 0, + instructions: [%Openflow.Instruction.GotoTable{table_id: 1}], + match: [in_port: 6, eth_src: "f20ba47df8ea"], + packet_count: 3, + priority: 123, + table_id: 0 + }, + %Openflow.Multipart.FlowStats{ + byte_count: 98, + cookie: 0, + duration_nsec: 980_901_000, + duration_sec: 313_499, + flags: [], + hard_timeout: 0, + idle_timeout: 0, + instructions: [ + %Openflow.Instruction.WriteActions{ + actions: [ + %Openflow.Action.SetField{field: [vlan_vid: 258]}, + %Openflow.Action.CopyTtlOut{}, + %Openflow.Action.CopyTtlIn{}, + %Openflow.Action.CopyTtlIn{}, + %Openflow.Action.PopPbb{}, + %Openflow.Action.PushPbb{ethertype: 4660}, + %Openflow.Action.PopMpls{ethertype: 39030}, + %Openflow.Action.PushMpls{ethertype: 34887}, + %Openflow.Action.PopVlan{}, + %Openflow.Action.PushVlan{ethertype: 33024}, + %Openflow.Action.DecMplsTtl{}, + %Openflow.Action.SetMplsTtl{ttl: 10}, + %Openflow.Action.DecNwTtl{}, + %Openflow.Action.SetNwTtl{ttl: 10}, + %Openflow.Action.SetQueue{id: 3}, + %Openflow.Action.Group{id: 99}, + %Openflow.Action.Output{max_len: :no_buffer, port_number: 6}, + %Openflow.Action.Experimenter{data: "exp_data", exp_id: 98_765_432}, + %Openflow.Action.Experimenter{data: "exp_data", exp_id: 8992} + ] + }, + %Openflow.Instruction.ApplyActions{ + actions: [ + %Openflow.Action.SetField{field: [eth_src: "010203040506"]}, + %Openflow.Action.SetField{field: [onf_pbb_uca: 1]} + ] + }, + %Openflow.Instruction.WriteActions{ + actions: [ + %Openflow.Action.Output{ + max_len: :no_buffer, + port_number: :controller + } + ] + } + ], + match: [], + packet_count: 1, + priority: 0, + table_id: 0 + } + ], + version: 4, + xid: 0 + } = flow_stats + end + end +end