diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12179ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where 3rd-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez diff --git a/README.md b/README.md new file mode 100644 index 0000000..633bbbb --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Tres + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `tres` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:tres, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at [https://hexdocs.pm/tres](https://hexdocs.pm/tres). + diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..93b9ad9 --- /dev/null +++ b/config/config.exs @@ -0,0 +1,16 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +config :tres, + callback_module: Tres.ExampleHandler, + max_connections: 10, + num_acceptors: 10, + protocol: :tcp, + port: 6653 + +config :logger, + level: :info, + format: "$date $time [$level] $metadata$message\n", + metadata: [:application], + handle_otp_reports: true diff --git a/lib/openflow.ex b/lib/openflow.ex new file mode 100644 index 0000000..a5ca2bc --- /dev/null +++ b/lib/openflow.ex @@ -0,0 +1,50 @@ +defmodule Openflow do + @moduledoc "OpenFlow Protocol format codec" + + @ofp_header_size 8 + + @spec read(binary()) :: {:ok, map()} | {:error, :binary_too_small} + def read(binary) + when byte_size(binary) < @ofp_header_size do + {:error, :binary_too_small} + end + def read(<<_v::8, _t::8, len::16, _x::32, _r::bytes>> = binary) + when byte_size(binary) < len do + {:error, :binary_too_small} + end + def read(<>) do + body_len = len - @ofp_header_size + <> = binary2 + + result = type + |> Openflow.Enums.to_atom(:openflow_codec) + |> do_read(body_bin) + + case result do + {:ok, struct} -> {:ok, %{struct|version: ver, xid: xid}, rest} + {:error, reason} -> {:error, reason} + end + end + + def to_binary(%{__struct__: encoder, version: version, xid: xid} = msg) do + case encoder.to_binary(msg) do + body_bin when is_binary(body_bin) -> + length = @ofp_header_size + byte_size(body_bin) + <> + {:error, reason} -> + {:error, reason} + end + end + + # private functions + + defp do_read({:error, reason}, _) do + {:error, reason} + end + defp do_read(decoder, body_bin) do + case decoder.read(body_bin) do + {:error, reason} -> {:error, reason} + result when is_map(result) -> {:ok, result} + end + end +end diff --git a/lib/openflow/.#nx_packet_in2.ex b/lib/openflow/.#nx_packet_in2.ex new file mode 120000 index 0000000..b4344b8 --- /dev/null +++ b/lib/openflow/.#nx_packet_in2.ex @@ -0,0 +1 @@ +shun159@shun159.5674:1510580208 \ No newline at end of file diff --git a/lib/openflow/action.ex b/lib/openflow/action.ex new file mode 100644 index 0000000..2bccd6d --- /dev/null +++ b/lib/openflow/action.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Action do + + def read(action_bin) do + do_read([], action_bin) + end + + def to_binary(actions) when is_list(actions) do + to_binary(<<>>, actions) + end + def to_binary(action) do + to_binary([action]) + end + + # private functions + + defp do_read(acc, <<>>), do: Enum.reverse(acc) + defp do_read(acc, <<0::32, _::bytes>>), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + codec = Openflow.Enums.to_atom(type, :action_type) + do_read([codec.read(action_bin)|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [action|rest]) do + codec = action.__struct__ + to_binary(<>, rest) + end +end diff --git a/lib/openflow/actions/copy_ttl_in.ex b/lib/openflow/actions/copy_ttl_in.ex new file mode 100644 index 0000000..83347b4 --- /dev/null +++ b/lib/openflow/actions/copy_ttl_in.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.CopyTtlIn do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 12 + + def new do + %CopyTtlIn{} + end + + def to_binary(%CopyTtlIn{}) do + <<12::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<12::16, 8::16, _::size(4)-unit(8)>>) do + %CopyTtlIn{} + end +end diff --git a/lib/openflow/actions/copy_ttl_out.ex b/lib/openflow/actions/copy_ttl_out.ex new file mode 100644 index 0000000..759c0ad --- /dev/null +++ b/lib/openflow/actions/copy_ttl_out.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.CopyTtlOut do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 11 + + def new do + %CopyTtlOut{} + end + + def to_binary(%CopyTtlOut{}) do + <<11::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<11::16, 8::16, _::size(4)-unit(8)>>) do + %CopyTtlOut{} + end +end diff --git a/lib/openflow/actions/dec_mpls_ttl.ex b/lib/openflow/actions/dec_mpls_ttl.ex new file mode 100644 index 0000000..2931954 --- /dev/null +++ b/lib/openflow/actions/dec_mpls_ttl.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.DecMplsTtl do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 16 + + def new do + %DecMplsTtl{} + end + + def to_binary(%DecMplsTtl{}) do + <<16::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<16::16, 8::16, _::size(4)-unit(8)>>) do + %DecMplsTtl{} + end +end diff --git a/lib/openflow/actions/dec_nw_ttl.ex b/lib/openflow/actions/dec_nw_ttl.ex new file mode 100644 index 0000000..6d23b5d --- /dev/null +++ b/lib/openflow/actions/dec_nw_ttl.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.DecNwTtl do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 24 + + def new do + %DecNwTtl{} + end + + def to_binary(%DecNwTtl{}) do + <<24::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<24::16, 8::16, _::size(4)-unit(8)>>) do + %DecNwTtl{} + end +end diff --git a/lib/openflow/actions/experimenter.ex b/lib/openflow/actions/experimenter.ex new file mode 100644 index 0000000..8390026 --- /dev/null +++ b/lib/openflow/actions/experimenter.ex @@ -0,0 +1,32 @@ +defmodule Openflow.Action.Experimenter do + defstruct(exp_id: 0, data: "") + + alias __MODULE__ + + @experimter_size 8 + + def ofpat, do: 0xffff + + def new(exp_id, data \\ "") do + %Experimenter{exp_id: exp_id, data: data} + end + + def to_binary(%Experimenter{exp_id: exp_id, data: data}) do + length = @experimter_size + byte_size(data) + <<0xffff::16, length::16, exp_id::32, data::bytes>> + end + + def read(<<0xffff::16, _length::16, exp_id::32, exp_type::16, data::bytes>>) do + case Openflow.Utils.get_enum(exp_id, :action_vendor) do + vendor_id when is_integer(vendor_id) -> + %Experimenter{exp_id: exp_id, data: <>} + vendor when is_atom(vendor) -> + case Openflow.Utils.get_enum(exp_type, vendor) do + codec when is_atom(codec) -> + codec.read(<>) + exp_type when is_integer(exp_type) -> + %Experimenter{exp_id: exp_id, data: <>} + end + end + end +end diff --git a/lib/openflow/actions/group.ex b/lib/openflow/actions/group.ex new file mode 100644 index 0000000..ff2d756 --- /dev/null +++ b/lib/openflow/actions/group.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.Group do + defstruct(id: 0) + + alias __MODULE__ + + def ofpat, do: 22 + + def new(id) do + %Group{id: id} + end + + def to_binary(%Group{id: id}) do + <<22::16, 8::16, id::32>> + end + + def read(<<22::16, 8::16, id::32>>) do + %Group{id: id} + end +end diff --git a/lib/openflow/actions/nx_bundle.ex b/lib/openflow/actions/nx_bundle.ex new file mode 100644 index 0000000..6a65bc4 --- /dev/null +++ b/lib/openflow/actions/nx_bundle.ex @@ -0,0 +1,80 @@ +defmodule Openflow.Action.NxBundle do + defstruct( + algorithm: :active_backup, + hash_field: :eth_src, + basis: 0, + slave_type: :nx_in_port, + n_slaves: 0, + slaves: [] + ) + + @experimenter 0x00002320 + @nxast 12 + + alias __MODULE__ + + def new(options) do + hash_field = Keyword.get(options, :hash_field, :eth_src) + basis = Keyword.get(options, :basis, 0) + alg = Keyword.get(options, :algorithm, :active_backup) + slaves = Keyword.get(options, :slaves, []) + %NxBundle{algorithm: alg, + hash_field: hash_field, + basis: basis, + n_slaves: length(slaves), + slaves: slaves} + end + + def to_binary(%NxBundle{algorithm: alg, + hash_field: hash_field, + basis: basis, + slave_type: slave_type, + n_slaves: n_slaves, + slaves: slaves}) do + hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields) + alg_int = Openflow.Enums.to_int(alg, :nx_bd_algorithm) + slave_type_bin = Openflow.Match.codec_header(slave_type) + slaves_bin = codec_slaves(slaves) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + slave_len = n_slaves * 2 + <> = rest + alg = Openflow.Enums.to_atom(alg_int, :nx_bd_algorithm) + hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields) + slave_type = Openflow.Match.codec_header(slave_type_bin) + slaves = codec_slaves(slaves_bin) + n_slaves = length(slaves) + %NxBundle{algorithm: alg, + hash_field: hash_field, + basis: basis, + slave_type: slave_type, + n_slaves: n_slaves, + slaves: slaves} + end + + # private functions + + defp codec_slaves(slaves) when is_list(slaves) do + slaves1 = for slave <- slaves do + slave_int = Openflow.Utils.get_enum(slave, :openflow10_port_no) + <> + end + Enum.join(slaves1, "") + end + defp codec_slaves(slaves) when is_binary(slaves) do + for <> do + Openflow.Utils.get_enum(slave_int, :openflow10_port_no) + end + end +end diff --git a/lib/openflow/actions/nx_bundle_load.ex b/lib/openflow/actions/nx_bundle_load.ex new file mode 100644 index 0000000..a76d9f4 --- /dev/null +++ b/lib/openflow/actions/nx_bundle_load.ex @@ -0,0 +1,102 @@ +defmodule Openflow.Action.NxBundleLoad do + import Bitwise + + defstruct( + algorithm: :active_backup, + hash_field: :eth_src, + basis: 0, + slave_type: :nx_in_port, + n_slaves: 0, + slaves: [], + offset: 0, + n_bits: 0, + dst_field: nil + ) + + @experimenter 0x00002320 + @nxast 13 + + alias __MODULE__ + + def new(options) do + hash_field = Keyword.get(options, :hash_field, :eth_src) + basis = Keyword.get(options, :basis, 0) + alg = Keyword.get(options, :algorithm, :active_backup) + slaves = Keyword.get(options, :slaves, []) + dst_field = Keyword.get(options, :dst_field) + default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + %NxBundleLoad{algorithm: alg, + hash_field: hash_field, + basis: basis, + n_slaves: length(slaves), + slaves: slaves, + offset: ofs, + n_bits: n_bits, + dst_field: dst_field} + end + + def to_binary(%NxBundleLoad{algorithm: alg, + hash_field: hash_field, + basis: basis, + slave_type: slave_type, + n_slaves: n_slaves, + slaves: slaves, + offset: ofs, + n_bits: n_bits, + dst_field: dst_field}) do + hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields) + alg_int = Openflow.Enums.to_int(alg, :nx_bd_algorithm) + slave_type_bin = Openflow.Match.codec_header(slave_type) + slaves_bin = codec_slaves(slaves) + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + dst_field_bin = Openflow.Match.codec_header(dst_field) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + slave_len = n_slaves * 2 + <> = rest + alg = Openflow.Enums.to_atom(alg_int, :nx_bd_algorithm) + hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields) + slave_type = Openflow.Match.codec_header(slave_type_bin) + slaves = codec_slaves(slaves_bin) + n_slaves = length(slaves) + dst_field = Openflow.Match.codec_header(dst_field_bin) + %NxBundleLoad{algorithm: alg, + hash_field: hash_field, + basis: basis, + slave_type: slave_type, + n_slaves: n_slaves, + slaves: slaves, + offset: ofs, + n_bits: n_bits + 1, + dst_field: dst_field} + end + + # private functions + + defp codec_slaves(slaves) when is_list(slaves) do + slaves1 = for slave <- slaves do + slave_int = Openflow.Utils.get_enum(slave, :openflow10_port_no) + <> + end + Enum.join(slaves1, "") + end + defp codec_slaves(slaves) when is_binary(slaves) do + for <> do + Openflow.Utils.get_enum(slave_int, :openflow10_port_no) + end + end +end diff --git a/lib/openflow/actions/nx_clone.ex b/lib/openflow/actions/nx_clone.ex new file mode 100644 index 0000000..afa8618 --- /dev/null +++ b/lib/openflow/actions/nx_clone.ex @@ -0,0 +1,28 @@ +defmodule Openflow.Action.NxClone do + defstruct( + actions: [] + ) + + @experimenter 0x00002320 + @nxast 42 + + alias __MODULE__ + + def new(actions \\ []) do + %NxClone{actions: actions} + end + + def to_binary(%NxClone{actions: actions}) do + actions_bin = Openflow.Action.to_binary(actions) + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>>) do + actions = Openflow.Action.read(actions_bin) + %NxClone{actions: actions} + end +end diff --git a/lib/openflow/actions/nx_conjunction.ex b/lib/openflow/actions/nx_conjunction.ex new file mode 100644 index 0000000..3b90b4d --- /dev/null +++ b/lib/openflow/actions/nx_conjunction.ex @@ -0,0 +1,28 @@ +defmodule Openflow.Action.NxConjunction do + defstruct( + clause: 0, + n_clauses: 0, + id: 0 + ) + + @experimenter 0x00002320 + @nxast 34 + + alias __MODULE__ + + def new(options) do + clause = Keyword.get(options, :clause, 0) + n_clauses = Keyword.get(options, :n_clauses, 0) + id = Keyword.get(options, :id, 0) + %NxConjunction{clause: clause, n_clauses: n_clauses, id: id} + end + + def to_binary(%NxConjunction{clause: clause, n_clauses: n_clauses, id: id}) do + exp_body = <<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>>) do + %NxConjunction{clause: clause, n_clauses: n_clauses, id: id} + end +end diff --git a/lib/openflow/actions/nx_conntrack.ex b/lib/openflow/actions/nx_conntrack.ex new file mode 100644 index 0000000..76b033f --- /dev/null +++ b/lib/openflow/actions/nx_conntrack.ex @@ -0,0 +1,88 @@ +defmodule Openflow.Action.NxConntrack do + import Bitwise + + defstruct( + flags: [], + zone_src: nil, + zone_imm: 0, + zone_offset: nil, + zone_n_bits: nil, + recirc_table: 255, + alg: 0, + exec: [] + ) + + @experimenter 0x00002320 + @nxast 35 + + alias __MODULE__ + + def new(options \\ []) do + flags = Keyword.get(options, :flags, []) + zone_src = Keyword.get(options, :zone_src) + zone_ofs = Keyword.get(options, :zone_offset) + zone_n_bits = Keyword.get(options, :zone_n_bits) + zone_imm = Keyword.get(options, :zone_imm, 0) + recirc_table = Keyword.get(options, :recirc_table, 255) + alg = Keyword.get(options, :alg, 0) + exec = Keyword.get(options, :exec, []) + %NxConntrack{ + flags: flags, + zone_src: zone_src, + zone_imm: zone_imm, + zone_offset: zone_ofs, + zone_n_bits: zone_n_bits, + recirc_table: recirc_table, + alg: alg, + exec: exec + } + end + + def to_binary(%NxConntrack{ + flags: flags, + zone_src: zone_src, + zone_offset: zone_ofs, + zone_n_bits: zone_n_bits, + zone_imm: zone_imm, + recirc_table: recirc_table, + alg: alg, + exec: exec}) do + flags_int = Openflow.Enums.flags_to_int(flags, :nx_conntrack_flags) + {src_bin, ofs_nbits} = if not (is_nil(zone_src)) do + zone_src_bin = Openflow.Match.codec_header(zone_src) + {zone_src_bin, (zone_ofs <<< 6) ||| (zone_n_bits - 1)} + else + {<<0::32>>, zone_imm} + end + exec_bin = Openflow.Action.to_binary(exec) + exp_body = <<@experimenter::32, @nxast::16, flags_int::16, + src_bin::bytes, ofs_nbits::16, recirc_table::8, + 0::size(3)-unit(8), alg::16, exec_bin::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, flags_int::16, + src_bin::4-bytes, ofs_nbits::16-bits, recirc_table::8, + _::size(3)-unit(8), alg::16, exec_bin::bytes>>) do + flags = Openflow.Enums.int_to_flags(flags_int, :nx_conntrack_flags) + exec = Openflow.Action.read(exec_bin) + ct = %NxConntrack{ + flags: flags, + recirc_table: recirc_table, + alg: alg, + exec: exec + } + case src_bin do + <<0::32>> -> + <> = ofs_nbits + %{ct|zone_imm: zone_imm} + binary when is_binary(binary) -> + zone_src = Openflow.Match.codec_header(binary) + <> = ofs_nbits + %{ct|zone_src: zone_src, zone_offset: ofs, zone_n_bits: n_bits + 1} + end + end +end diff --git a/lib/openflow/actions/nx_controller.ex b/lib/openflow/actions/nx_controller.ex new file mode 100644 index 0000000..8128f40 --- /dev/null +++ b/lib/openflow/actions/nx_controller.ex @@ -0,0 +1,32 @@ +defmodule Openflow.Action.NxController do + defstruct( + max_len: :no_buffer, + id: 0, + reason: :action + ) + + @experimenter 0x00002320 + @nxast 20 + + alias __MODULE__ + + def new(options) do + max_len = Keyword.get(options, :max_len, :no_buffer) + controller_id = Keyword.get(options, :id, 0) + reason = Keyword.get(options, :reason, :action) + %NxController{max_len: max_len, id: controller_id, reason: reason} + end + + def to_binary(%NxController{max_len: max_len, id: controller_id, reason: reason}) do + max_len_int = Openflow.Utils.get_enum(max_len, :controller_max_len) + reason_int = Openflow.Enums.to_int(reason, :packet_in_reason) + exp_body = <<@experimenter::32, @nxast::16, max_len_int::16, controller_id::16, reason_int::8, 0::8>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, max_len_int::16, controller_id::16, reason_int::8, _::bytes>>) do + max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len) + reason = Openflow.Enums.to_atom(reason_int, :packet_in_reason) + %NxController{max_len: max_len, id: controller_id, reason: reason} + end +end diff --git a/lib/openflow/actions/nx_controller2.ex b/lib/openflow/actions/nx_controller2.ex new file mode 100644 index 0000000..6e1637c --- /dev/null +++ b/lib/openflow/actions/nx_controller2.ex @@ -0,0 +1,119 @@ +defmodule Openflow.Action.NxController2 do + defstruct( + max_len: :no_buffer, + id: 0, + reason: :action, + userdata: "", + pause: false + ) + + @experimenter 0x00002320 + @nxast 37 + + @prop_header_size 4 + + @prop_max_len 0 + @prop_ctl_id 1 + @prop_reason 2 + @prop_userdata 3 + @prop_pause 4 + + alias __MODULE__ + + def new(options) do + max_len = Keyword.get(options, :max_len, :no_buffer) + controller_id = Keyword.get(options, :id, 0) + reason = Keyword.get(options, :reason, :action) + userdata = Keyword.get(options, :userdata) + pause = Keyword.get(options, :pause, false) + %NxController2{max_len: max_len, + id: controller_id, + reason: reason, + userdata: userdata, + pause: pause} + end + + def to_binary(%NxController2{} = ctl) do + ext_header = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>> + prop_keys = get_prop_key(ctl) + props_bin = encode_prop("", prop_keys, ctl) + exp_body = <> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), body::bytes>>) do + %NxController2{} + |> decode_prop(body) + end + + # private functions + + defp get_prop_key(ctl) do + ctl + |> Map.from_struct + |> Enum.map(fn({k, v}) -> if(not is_nil(v), do: k, else: nil) end) + |> Enum.filter(fn(v) -> not is_nil(v) end) + end + + defp encode_prop(acc, [], _ctl), do: acc + defp encode_prop(acc, [prop|rest], ctl) do + value = Map.get(ctl, prop) + prop_bin = cond do + prop == :max_len and (value != :no_buffer or value < 0xffff) -> + padding_length = 2 + prop_length = @prop_header_size + 2 + padding_length + max_len_int = Openflow.Utils.get_enum(value, :controller_max_len) + <<@prop_max_len::16, prop_length::16, max_len_int::16, 0::size(padding_length)-unit(8)>> + prop == :id -> + padding_length = 2 + prop_length = @prop_header_size + 2 + padding_length + <<@prop_ctl_id::16, prop_length::16, value::16, 0::size(padding_length)-unit(8)>> + prop == :reason and value != :action -> + padding_length = 3 + prop_length = @prop_header_size + 1 + padding_length + reason_int = Openflow.Utils.get_enum(value, :packet_in_reason) + <<@prop_reason::16, prop_length::16, reason_int::8, 0::size(padding_length)-unit(8)>> + prop == :userdata and byte_size(value) > 0 -> + prop_length = @prop_header_size + byte_size(value) + padding_length = Openflow.Utils.padding(prop_length, 8) + <<@prop_userdata::16, prop_length::16, value::bytes, 0::size(padding_length)-unit(8)>> + prop == :pause and value == true -> + padding_length = 4 + prop_length = @prop_header_size + padding_length + <<@prop_pause::16, prop_length::16, 0::size(padding_length)-unit(8)>> + true -> + "" + end + encode_prop(<>, rest, ctl) + end + + defp decode_prop(ctl, ""), do: ctl + defp decode_prop(ctl, <> = bin) do + prop_type = Openflow.Enums.to_atom(prop_type_int, :nx_action_controller2_prop_type) + case prop_type do + :max_len -> + <<@prop_max_len::16, _prop_length::16, max_len_int::16, _::size(2)-unit(8), rest::bytes>> = bin + max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len) + decode_prop(struct(ctl, %{max_len: max_len}), rest) + :controller_id -> + <<@prop_ctl_id::16, _prop_length::16, controller_id::16, _::size(2)-unit(8), rest::bytes>> = bin + decode_prop(struct(ctl, %{controller_id: controller_id}), rest) + :reason -> + <<@prop_reason::16, _prop_length::16, reason_int::8, _::size(3)-unit(8), rest::bytes>> = bin + reason = Openflow.Utils.get_enum(reason_int, :packet_in_reason) + decode_prop(struct(ctl, %{reason: reason}), rest) + :userdata -> + <<@prop_userdata::16, prop_length::16, remains::bytes>> = bin + userdata_len = prop_length - 4 + padding_length = Openflow.Utils.padding(prop_length, 8) + <> = remains + decode_prop(struct(ctl, %{userdata: userdata}), rest) + :pause -> + <<@prop_pause::16, _::16, 0::size(4)-unit(8), rest::bytes>> = bin + decode_prop(struct(ctl, %{pause: true}), rest) + end + end +end diff --git a/lib/openflow/actions/nx_ct_clear.ex b/lib/openflow/actions/nx_ct_clear.ex new file mode 100644 index 0000000..32f0810 --- /dev/null +++ b/lib/openflow/actions/nx_ct_clear.ex @@ -0,0 +1,21 @@ +defmodule Openflow.Action.NxCtClear do + defstruct([]) + + @experimenter 0x00002320 + @nxast 43 + + alias __MODULE__ + + def new do + %NxCtClear{} + end + + def to_binary(%NxCtClear{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::16, 0::size(4)-unit(8)>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do + %NxCtClear{} + end +end diff --git a/lib/openflow/actions/nx_dec_mpls_ttl.ex b/lib/openflow/actions/nx_dec_mpls_ttl.ex new file mode 100644 index 0000000..8b2c0f1 --- /dev/null +++ b/lib/openflow/actions/nx_dec_mpls_ttl.ex @@ -0,0 +1,21 @@ +defmodule Openflow.Action.NxDecMplsTtl do + defstruct([]) + + @experimenter 0x00002320 + @nxast 26 + + alias __MODULE__ + + def new do + %NxDecMplsTtl{} + end + + def to_binary(%NxDecMplsTtl{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8)>>) do + %NxDecMplsTtl{} + end +end diff --git a/lib/openflow/actions/nx_dec_ttl.ex b/lib/openflow/actions/nx_dec_ttl.ex new file mode 100644 index 0000000..7d949b8 --- /dev/null +++ b/lib/openflow/actions/nx_dec_ttl.ex @@ -0,0 +1,21 @@ +defmodule Openflow.Action.NxDecTtl do + defstruct([]) + + @experimenter 0x00002320 + @nxast 18 + + alias __MODULE__ + + def new do + %NxDecTtl{} + end + + def to_binary(%NxDecTtl{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do + %NxDecTtl{} + end +end diff --git a/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex b/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex new file mode 100644 index 0000000..6704e75 --- /dev/null +++ b/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex @@ -0,0 +1,31 @@ +defmodule Openflow.Action.NxDecTtlCntIds do + defstruct(ids: []) + + @experimenter 0x00002320 + @nxast 21 + + alias __MODULE__ + + def new(ids) do + %NxDecTtlCntIds{ids: ids} + end + + def to_binary(%NxDecTtlCntIds{ids: ids}) do + n_controllers = length(ids) + ids_bin = Enum.join((for id <- ids, do: <>), "") + padding = Openflow.Utils.padding(n_controllers, 8) + exp_body = <<@experimenter::32, @nxast::16, n_controllers::16, + 0::size(4)-unit(8), ids_bin::bytes, 0::size(padding)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, n_controllers::16, body::bitstring>>) do + n_controllers_len = n_controllers * 16 + <<0::size(4)-unit(8), ids_bin::size(n_controllers_len)-bits, _::bitstring>> = body + ids = for <>, do: id + %NxDecTtlCntIds{ids: ids} + end +end diff --git a/lib/openflow/actions/nx_exit.ex b/lib/openflow/actions/nx_exit.ex new file mode 100644 index 0000000..842dd77 --- /dev/null +++ b/lib/openflow/actions/nx_exit.ex @@ -0,0 +1,21 @@ +defmodule Openflow.Action.NxExit do + defstruct([]) + + @experimenter 0x00002320 + @nxast 17 + + alias __MODULE__ + + def new do + %NxExit{} + end + + def to_binary(%NxExit{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::48>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, 0::48>>) do + %NxExit{} + end +end diff --git a/lib/openflow/actions/nx_fin_timeout.ex b/lib/openflow/actions/nx_fin_timeout.ex new file mode 100644 index 0000000..00a14d4 --- /dev/null +++ b/lib/openflow/actions/nx_fin_timeout.ex @@ -0,0 +1,26 @@ +defmodule Openflow.Action.NxFinTimeout do + defstruct( + idle_timeout: 0, + hard_timeout: 0 + ) + + @experimenter 0x00002320 + @nxast 19 + + alias __MODULE__ + + def new(options) do + fin_idle = Keyword.get(options, :idle_timeout, 0) + fin_hard = Keyword.get(options, :hard_timeout, 0) + %NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard} + end + + def to_binary(%NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard}) do + exp_body = <<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16>> + <<0xffff::16, 16::16, exp_body::bytes, 0::size(2)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16, _::size(2)-unit(8)>>) do + %NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard} + end +end diff --git a/lib/openflow/actions/nx_flow_spec.ex b/lib/openflow/actions/nx_flow_spec.ex new file mode 100644 index 0000000..62a56b3 --- /dev/null +++ b/lib/openflow/actions/nx_flow_spec.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Action.NxFlowSpec do + + def read(flow_spec_bin) do + do_read([], flow_spec_bin) + end + + def to_binary(flow_specs) when is_list(flow_specs) do + to_binary(<<>>, flow_specs) + end + def to_binary(flow_spec) do + to_binary([flow_spec]) + end + + # private functions + + defp do_read(acc, <<>>), do: Enum.reverse(acc) + defp do_read(acc, <<0::16, _::bitstring>>), do: Enum.reverse(acc) + defp do_read(acc, <<_::2, _::1, type::2, _::bitstring>> = binary) do + codec = Openflow.Enums.to_atom(type, :nx_flow_spec_type) + {flow_spec, rest} = codec.read(binary) + do_read([flow_spec|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [flow_spec|rest]) do + codec = flow_spec.__struct__ + to_binary(<>, rest) + end +end diff --git a/lib/openflow/actions/nx_flow_spec_load.ex b/lib/openflow/actions/nx_flow_spec_load.ex new file mode 100644 index 0000000..120a90d --- /dev/null +++ b/lib/openflow/actions/nx_flow_spec_load.ex @@ -0,0 +1,83 @@ +defmodule Openflow.Action.NxFlowSpecLoad do + defstruct( + src: nil, + dst: nil, + n_bits: 0, + src_offset: 0, + dst_offset: 0 + ) + + @learn_src_field 0 + @learn_src_immediate 1 + @learn_dst 1 + + alias __MODULE__ + + def new(options) do + src = Keyword.get(options, :src) + dst = Keyword.get(options, :dst) + src_ofs = Keyword.get(options, :src_offset, 0) + dst_ofs = Keyword.get(options, :dst_offset, 0) + default_n_bits = Openflow.Match.Field.n_bits_of(dst) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + %NxFlowSpecLoad{src: src, + dst: dst, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs} + end + + def to_binary(%NxFlowSpecLoad{} = fsm) do + %NxFlowSpecLoad{ + dst: dst_field, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs + } = fsm + {src_code, src_bin} = codec_src(fsm) + dst_bin = Openflow.Match.codec_header(dst_field) + case src_code do + @learn_src_immediate -> + <<0::2, src_code::1, @learn_dst::2, n_bits::11, + src_bin::bytes, dst_bin::4-bytes, dst_ofs::16>> + @learn_src_field -> + <<0::2, src_code::1, @learn_dst::2, n_bits::11, + src_bin::4-bytes, src_ofs::16, dst_bin::4-bytes, dst_ofs::16>> + end + end + + def read(<<_::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, + src_ofs::16, dst_bin::4-bytes, dst_ofs::16, rest::bitstring>>) do + src = Openflow.Match.codec_header(src_bin) + dst = Openflow.Match.codec_header(dst_bin) + flow_spec = %NxFlowSpecLoad{src: src, + dst: dst, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs} + {flow_spec, rest} + end + def read(<<_::2, @learn_src_immediate::1, @learn_dst::2, n_bits::11, binary::bitstring>>) do + rounded_up_len = Openflow.Utils.pad_length(n_bits, 8) + rounded_up_nbits = n_bits + rounded_up_len + <> = binary + dst = Openflow.Match.codec_header(dst_bin) + src = Openflow.Match.Field.codec(src_bin, dst) + flow_spec = %NxFlowSpecLoad{src: src, + dst: dst, + n_bits: n_bits, + dst_offset: dst_ofs} + {flow_spec, rest} + end + + # private functions + + defp codec_src(%NxFlowSpecLoad{src: src_field}) when is_atom(src_field) do + src_bin = Openflow.Match.codec_header(src_field) + {@learn_src_field, src_bin} + end + defp codec_src(%NxFlowSpecLoad{src: src, dst: dst_field}) do + src_bin = Openflow.Match.Field.codec(src, dst_field) + {@learn_src_immediate, src_bin} + end +end diff --git a/lib/openflow/actions/nx_flow_spec_match.ex b/lib/openflow/actions/nx_flow_spec_match.ex new file mode 100644 index 0000000..8312fb4 --- /dev/null +++ b/lib/openflow/actions/nx_flow_spec_match.ex @@ -0,0 +1,83 @@ +defmodule Openflow.Action.NxFlowSpecMatch do + defstruct( + src: nil, + dst: nil, + n_bits: 0, + src_offset: 0, + dst_offset: 0 + ) + + @learn_src_field 0 + @learn_src_immediate 1 + @learn_dst 0 + + alias __MODULE__ + + def new(options) do + src = Keyword.get(options, :src) + dst = Keyword.get(options, :dst) + default_n_bits = Openflow.Match.Field.n_bits_of(dst) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + src_ofs = Keyword.get(options, :src_offset, 0) + dst_ofs = Keyword.get(options, :dst_offset, 0) + %NxFlowSpecMatch{src: src, + dst: dst, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs} + end + + def to_binary(%NxFlowSpecMatch{} = fsm) do + %NxFlowSpecMatch{ + dst: dst_field, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs + } = fsm + {src_code, src_bin} = codec_src(fsm) + dst_bin = Openflow.Match.codec_header(dst_field) + case src_code do + @learn_src_immediate -> + <<0::2, src_code::1, @learn_dst::2, n_bits::11, + src_bin::bytes, dst_bin::4-bytes, dst_ofs::16>> + @learn_src_field -> + <<0::2, src_code::1, @learn_dst::2, n_bits::11, + src_bin::bytes, src_ofs::16, dst_bin::4-bytes, dst_ofs::16>> + end + end + + def read(<<_::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, + src_ofs::16, dst_bin::4-bytes, dst_ofs::16, rest::bitstring>>) do + src = Openflow.Match.codec_header(src_bin) + dst = Openflow.Match.codec_header(dst_bin) + flow_spec = %NxFlowSpecMatch{src: src, + dst: dst, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs} + {flow_spec, rest} + end + def read(<<_::2, @learn_src_immediate::1, @learn_dst::2, n_bits::11, binary::bitstring>>) do + rounded_up_len = Openflow.Utils.pad_length(n_bits, 8) + rounded_up_nbits = n_bits + rounded_up_len + <> = binary + dst = Openflow.Match.codec_header(dst_bin) + src = Openflow.Match.Field.codec(src_bin, dst) + flow_spec = %NxFlowSpecMatch{src: src, + dst: dst, + n_bits: n_bits, + dst_offset: dst_ofs} + {flow_spec, rest} + end + + # private functions + + defp codec_src(%NxFlowSpecMatch{src: src_field}) when is_atom(src_field) do + src_bin = Openflow.Match.codec_header(src_field) + {@learn_src_field, src_bin} + end + defp codec_src(%NxFlowSpecMatch{src: src, dst: dst_field}) do + src_bin = Openflow.Match.Field.codec(src, dst_field) + {@learn_src_immediate, src_bin} + end +end diff --git a/lib/openflow/actions/nx_flow_spec_output.ex b/lib/openflow/actions/nx_flow_spec_output.ex new file mode 100644 index 0000000..ae0b18f --- /dev/null +++ b/lib/openflow/actions/nx_flow_spec_output.ex @@ -0,0 +1,38 @@ +defmodule Openflow.Action.NxFlowSpecOutput do + defstruct( + n_bits: 0, + src: nil, + src_offset: 0 + ) + + @learn_src_field 0 + @learn_dst 2 + + alias __MODULE__ + + def new(options) do + src = Keyword.get(options, :src) + src_ofs = Keyword.get(options, :src_offset, 0) + default_n_bits = Openflow.Match.Field.n_bits_of(src) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + %NxFlowSpecOutput{n_bits: n_bits, + src: src, + src_offset: src_ofs} + end + + def to_binary(%NxFlowSpecOutput{n_bits: n_bits, + src: src, + src_offset: src_ofs}) do + src_bin = Openflow.Match.codec_header(src) + <<0::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16>> + end + + def read(<<0::2, @learn_src_field::1, @learn_dst::2, + n_bits::11, src_bin::4-bytes, src_ofs::16, rest::bitstring>>) do + src = Openflow.Match.codec_header(src_bin) + flow_spec = %NxFlowSpecOutput{n_bits: n_bits, + src: src, + src_offset: src_ofs} + {flow_spec, rest} + end +end diff --git a/lib/openflow/actions/nx_learn.ex b/lib/openflow/actions/nx_learn.ex new file mode 100644 index 0000000..df6932f --- /dev/null +++ b/lib/openflow/actions/nx_learn.ex @@ -0,0 +1,78 @@ +defmodule Openflow.Action.NxLearn do + defstruct( + idle_timeout: 0, + hard_timeout: 0, + priority: 0, + cookie: 0, + flags: [], + table_id: 0, + fin_idle_timeout: 0, + fin_hard_timeout: 0, + flow_specs: [] + ) + + @experimenter 0x00002320 + @nxast 16 + + alias __MODULE__ + + def new(options) do + idle = Keyword.get(options, :idle_timeout, 0) + hard = Keyword.get(options, :hard_timeout, 0) + prio = Keyword.get(options, :priority, 0) + cookie = Keyword.get(options, :cookie, 0) + flags = Keyword.get(options, :flags, []) + table_id = Keyword.get(options, :table_id, 0) + fin_idle = Keyword.get(options, :fin_idle_timeout, 0) + fin_hard = Keyword.get(options, :fin_hard_timeout, 0) + flow_specs = Keyword.get(options, :flow_specs, []) + %NxLearn{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + flow_specs: flow_specs} + end + + def to_binary(%NxLearn{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + flow_specs: flow_specs}) do + flags_int = Openflow.Enums.flags_to_int(flags, :nx_learn_flag) + flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(flow_specs) + exp_body = <<@experimenter::32, @nxast::16, idle::16, hard::16, + prio::16, cookie::64, flags_int::16, table_id::8, + 0::size(1)-unit(8), fin_idle::16, fin_hard::16, + flow_specs_bin::bitstring>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bitstring>>) do + <> = body + flags = Openflow.Enums.int_to_flags(flags_int, :nx_learn_flag) + flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin) + %NxLearn{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + flow_specs: flow_specs} + end +end diff --git a/lib/openflow/actions/nx_learn2.ex b/lib/openflow/actions/nx_learn2.ex new file mode 100644 index 0000000..4a47ef5 --- /dev/null +++ b/lib/openflow/actions/nx_learn2.ex @@ -0,0 +1,106 @@ +defmodule Openflow.Action.NxLearn2 do + defstruct( + idle_timeout: 0, + hard_timeout: 0, + priority: 0, + cookie: 0, + flags: [], + table_id: 0, + fin_idle_timeout: 0, + fin_hard_timeout: 0, + limit: 0, + result_dst_offset: 0, + result_dst: nil, + flow_specs: [] + ) + + @experimenter 0x00002320 + @nxast 45 + + alias __MODULE__ + + def new(options) do + idle = Keyword.get(options, :idle_timeout, 0) + hard = Keyword.get(options, :hard_timeout, 0) + prio = Keyword.get(options, :priority, 0) + cookie = Keyword.get(options, :cookie, 0) + flags = Keyword.get(options, :flags, []) + table_id = Keyword.get(options, :table_id, 0) + fin_idle = Keyword.get(options, :fin_idle_timeout, 0) + fin_hard = Keyword.get(options, :fin_hard_timeout, 0) + flow_specs = Keyword.get(options, :flow_specs, []) + limit = Keyword.get(options, :limit, 0) + result_dst_offset = Keyword.get(options, :result_dst_offset, 0) + result_dst = Keyword.get(options, :result_dst) + %NxLearn2{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + limit: limit, + result_dst_offset: result_dst_offset, + result_dst: result_dst, + flow_specs: flow_specs} + end + + def to_binary(%NxLearn2{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + limit: limit, + result_dst_offset: result_dst_ofs, + result_dst: result_dst, + flow_specs: flow_specs}) do + flags_int = Openflow.Enums.flags_to_int(flags, :nx_learn_flag) + result_dst_bin = if :write_result in flags do + Openflow.Match.codec_header(result_dst) + else + "" + end + flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(flow_specs) + exp_body = <<@experimenter::32, @nxast::16, idle::16, hard::16, + prio::16, cookie::64, flags_int::16, table_id::8, + 0::size(1)-unit(8), fin_idle::16, fin_hard::16, + limit::32, result_dst_ofs::16, 0::size(2)-unit(8), + result_dst_bin::bytes, flow_specs_bin::bitstring>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + flags = Openflow.Enums.int_to_flags(flags_int, :nx_learn_flag) + learn = %NxLearn2{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + limit: limit, + result_dst_offset: result_dst_ofs} + if :write_result in flags do + header_size = Openflow.Match.header_size(rest) + <> = rest + result_dst = Openflow.Match.codec_header(result_dst_bin) + flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin) + struct(learn, %{result_dst: result_dst, flow_specs: flow_specs}) + else + <> = rest + flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin) + struct(learn, %{flow_specs: flow_specs}) + end + end +end diff --git a/lib/openflow/actions/nx_multipath.ex b/lib/openflow/actions/nx_multipath.ex new file mode 100644 index 0000000..86c45c9 --- /dev/null +++ b/lib/openflow/actions/nx_multipath.ex @@ -0,0 +1,78 @@ +defmodule Openflow.Action.NxMultipath do + import Bitwise + + defstruct( + hash_field: :eth_src, + basis: 0, + algorithm: :modulo_n, + max_link: 0, + argument: 0, + offset: 0, + n_bits: 0, + dst_field: nil + ) + + @experimenter 0x00002320 + @nxast 10 + + alias __MODULE__ + + def new(options) do + hash_field = Keyword.get(options, :hash_field, :eth_src) + basis = Keyword.get(options, :basis, 0) + alg = Keyword.get(options, :algorithm, :modulo_n) + max_link = Keyword.get(options, :max_link, 0) + arg = Keyword.get(options, :argument, 0) + dst_field = Keyword.get(options, :dst_field) + default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + %NxMultipath{hash_field: hash_field, + basis: basis, + algorithm: alg, + max_link: max_link, + offset: ofs, + n_bits: n_bits, + argument: arg, + dst_field: dst_field} + end + + def to_binary(%NxMultipath{hash_field: hash_field, + basis: basis, + algorithm: alg, + max_link: max_link, + argument: arg, + offset: ofs, + n_bits: n_bits, + dst_field: dst_field}) do + hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields) + alg_int = Openflow.Enums.to_int(alg, :nx_mp_algorithm) + dst_field_bin = Openflow.Match.codec_header(dst_field) + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields) + alg = Openflow.Enums.to_atom(alg_int, :nx_mp_algorithm) + dst_field = Openflow.Match.codec_header(dst_field_bin) + %NxMultipath{hash_field: hash_field, + basis: basis, + algorithm: alg, + max_link: max_link, + argument: arg, + offset: ofs, + n_bits: n_bits + 1, + dst_field: dst_field} + end +end diff --git a/lib/openflow/actions/nx_nat.ex b/lib/openflow/actions/nx_nat.ex new file mode 100644 index 0000000..a8af26d --- /dev/null +++ b/lib/openflow/actions/nx_nat.ex @@ -0,0 +1,121 @@ +defmodule Openflow.Action.NxNat do + defstruct( + flags: [], + ipv4_min: nil, + ipv4_max: nil, + ipv6_min: nil, + ipv6_max: nil, + proto_min: nil, + proto_max: nil + ) + + @experimenter 0x00002320 + @nxast 36 + + alias __MODULE__ + + def new(options \\ []) do + flags = Keyword.get(options, :flags, []) + ipv4_min = Keyword.get(options, :ipv4_min) + ipv4_max = Keyword.get(options, :ipv4_max) + ipv6_min = Keyword.get(options, :ipv6_min) + ipv6_max = Keyword.get(options, :ipv6_max) + proto_min = Keyword.get(options, :proto_min) + proto_max = Keyword.get(options, :proto_max) + %NxNat{flags: flags, + ipv4_min: ipv4_min, + ipv4_max: ipv4_max, + ipv6_min: ipv6_min, + ipv6_max: ipv6_max, + proto_min: proto_min, + proto_max: proto_max} + end + + def to_binary(%NxNat{flags: flags} = nat) do + flags_int = Openflow.Enums.flags_to_int(flags, :nx_nat_flags) + range_flags = + nat + |> get_ranges + |> Openflow.Enums.flags_to_int(:nx_nat_range) + |> Openflow.Enums.int_to_flags(:nx_nat_range) + ranges_bin = encode_ranges("", range_flags, nat) + range_flags_int = Openflow.Enums.flags_to_int(range_flags, :nx_nat_range) + exp_body = <<@experimenter::32, @nxast::16, 0::size(2)-unit(8), + flags_int::16, range_flags_int::16, ranges_bin::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <<0::size(2)-unit(8), flags_int::16, range_flags_int::16, ranges_bin::bytes>> = body + flags = Openflow.Enums.int_to_flags(flags_int, :nx_nat_flags) + range_flags = Openflow.Enums.int_to_flags(range_flags_int, :nx_nat_range) + decode_ranges(%NxNat{flags: flags}, range_flags, ranges_bin) + end + + # private functions + + defp get_ranges(nat) do + nat + |> Map.from_struct + |> Map.delete(:flags) + |> Enum.map(fn({k, v}) -> if(not is_nil(v), do: k, else: nil) end) + |> Enum.filter(fn(v) -> not is_nil(v) end) + end + + defp encode_ranges(acc, [], _nat), do: acc + defp encode_ranges(acc, [range|rest], nat) do + cond do + range == :ipv4_min or range == :ipv4_max -> + case Map.get(nat, range) do + {a1, a2, a3, a4} -> + encode_ranges(<>, rest, nat) + "" -> + encode_ranges(<>, rest, nat) + end + range == :ipv6_min or range == :ipv6_max -> + case Map.get(nat, range) do + {a1, a2, a3, a4, a5, a6, a7, a8} -> + encode_ranges(<>, rest, nat) + "" -> + encode_ranges(<>, rest, nat) + end + range == :proto_min or range == :proto_max -> + case Map.get(nat, range) do + proto when is_integer(proto) and proto in (1..0xffff) -> + encode_ranges(<>, rest, nat) + _ -> + encode_ranges(<>, rest, nat) + end + end + end + + defp decode_ranges(nat, [], _), do: nat + defp decode_ranges(nat, [range|ranges], bin) do + cond do + range == :ipv4_min or range == :ipv4_max -> + case bin do + <> -> + decode_ranges(struct(nat, %{range => {a1, a2, a3, a4}}), ranges, rest) + rest -> + decode_ranges(struct(nat, %{range => ""}), ranges, rest) + end + range == :ipv6_min or range == :ipv6_max -> + case bin do + <> -> + decode_ranges(struct(nat, %{range => {a1, a2, a3, a4, a5, a6, a7, a8}}), ranges, rest) + rest -> + decode_ranges(struct(nat, %{range => ""}), ranges, rest) + end + range == :proto_min or range == :proto_max -> + case bin do + <> when proto in (1..0xffff) -> + decode_ranges(struct(nat, %{range => proto}), ranges, rest) + rest -> + decode_ranges(struct(nat, %{range => ""}), ranges, rest) + end + end + end +end diff --git a/lib/openflow/actions/nx_note.ex b/lib/openflow/actions/nx_note.ex new file mode 100644 index 0000000..2e281fe --- /dev/null +++ b/lib/openflow/actions/nx_note.ex @@ -0,0 +1,26 @@ +defmodule Openflow.Action.NxNote do + defstruct(note: "") + + @experimenter 0x00002320 + @nxast 8 + + alias __MODULE__ + + def new(note) do + %NxNote{note: note} + end + + def to_binary(%NxNote{note: note}) do + padding = Openflow.Utils.padding(byte_size(note) + 2, 8) + exp_body = <<@experimenter::32, @nxast::16, note::bytes, 0::size(padding)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, note_bin::bytes>>) do + note = Openflow.Utils.decode_string(note_bin) + %NxNote{note: note} + end +end diff --git a/lib/openflow/actions/nx_output_reg.ex b/lib/openflow/actions/nx_output_reg.ex new file mode 100644 index 0000000..f94c130 --- /dev/null +++ b/lib/openflow/actions/nx_output_reg.ex @@ -0,0 +1,52 @@ +defmodule Openflow.Action.NxOutputReg do + import Bitwise + + defstruct( + n_bits: 0, + offset: 0, + src_field: nil, + max_len: :no_buffer + ) + + @experimenter 0x00002320 + @nxast 15 + + alias __MODULE__ + + def new(options) do + src_field = Keyword.get(options, :src_field) + default_n_bits = Openflow.Match.Field.n_bits_of(src_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + max_len = Keyword.get(options, :max_len, :no_buffer) + %NxOutputReg{n_bits: n_bits, + offset: ofs, + src_field: src_field, + max_len: max_len} + end + + def to_binary(%NxOutputReg{n_bits: n_bits, + offset: ofs, + src_field: src_field, + max_len: max_len}) do + src_field_bin = Openflow.Match.codec_header(src_field) + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + src_field = Openflow.Match.codec_header(src_field_bin) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + %NxOutputReg{n_bits: n_bits + 1, + offset: ofs, + src_field: src_field, + max_len: max_len} + end +end diff --git a/lib/openflow/actions/nx_output_reg2.ex b/lib/openflow/actions/nx_output_reg2.ex new file mode 100644 index 0000000..d5c88c7 --- /dev/null +++ b/lib/openflow/actions/nx_output_reg2.ex @@ -0,0 +1,51 @@ +defmodule Openflow.Action.NxOutputReg2 do + import Bitwise + + defstruct( + n_bits: 0, + offset: 0, + src_field: nil, + max_len: :no_buffer + ) + + @experimenter 0x00002320 + @nxast 32 + + alias __MODULE__ + + def new(options) do + src_field = Keyword.get(options, :src_field) + default_n_bits = Openflow.Match.Field.n_bits_of(src_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + max_len = Keyword.get(options, :max_len, :no_buffer) + %NxOutputReg2{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len} + end + + def to_binary(%NxOutputReg2{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}) do + src_field_bin = Openflow.Match.codec_header(src_field) + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + padding = Openflow.Utils.padding(byte_size(src_field_bin), 10) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + exp_size = match_header_size(oxm_header) - 4 + <> = rest + src_field = Openflow.Match.codec_header(<>) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + %NxOutputReg2{n_bits: n_bits + 1, offset: ofs, src_field: src_field, max_len: max_len} + end + + # private functions + + defp match_header_size(<<0xffff::16, _::bytes>>), do: 8 + defp match_header_size(<<_::16, _::bytes>>), do: 4 +end diff --git a/lib/openflow/actions/nx_output_trunc.ex b/lib/openflow/actions/nx_output_trunc.ex new file mode 100644 index 0000000..d8289b2 --- /dev/null +++ b/lib/openflow/actions/nx_output_trunc.ex @@ -0,0 +1,31 @@ +defmodule Openflow.Action.NxOutputTrunc do + defstruct( + port_number: 0, + max_len: :no_buffer + ) + + @experimenter 0x00002320 + @nxast 39 + + alias __MODULE__ + + def new(options) do + port_no = Keyword.get(options, :port_number) + max_len = Keyword.get(options, :max_len) + %NxOutputTrunc{port_number: port_no, max_len: max_len} + end + + def to_binary(%NxOutputTrunc{port_number: port_no, max_len: max_len}) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow10_port_no) + exp_body = <<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow10_port_no) + %NxOutputTrunc{port_number: port_no, max_len: max_len} + end +end diff --git a/lib/openflow/actions/nx_pop_mpls.ex b/lib/openflow/actions/nx_pop_mpls.ex new file mode 100644 index 0000000..7aef5b0 --- /dev/null +++ b/lib/openflow/actions/nx_pop_mpls.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxPopMpls do + defstruct(ethertype: 0x8847) + + @experimenter 0x00002320 + @nxast 24 + + alias __MODULE__ + + def new(ethertype \\ 0x8847) do + %NxPopMpls{ethertype: ethertype} + end + + def to_binary(%NxPopMpls{ethertype: ethertype}) do + exp_body = <<@experimenter::32, @nxast::16, ethertype::16, 0::size(4)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ethertype::16, _::size(4)-unit(8)>>) do + %NxPopMpls{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/nx_pop_queue.ex b/lib/openflow/actions/nx_pop_queue.ex new file mode 100644 index 0000000..bda8339 --- /dev/null +++ b/lib/openflow/actions/nx_pop_queue.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxPopQueue do + defstruct([]) + + @experimenter 0x00002320 + @nxast 5 + + alias __MODULE__ + + def new do + %NxPopQueue{} + end + + def to_binary(%NxPopQueue{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::48>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>) do + %NxPopQueue{} + end +end diff --git a/lib/openflow/actions/nx_push_mpls.ex b/lib/openflow/actions/nx_push_mpls.ex new file mode 100644 index 0000000..241d6b3 --- /dev/null +++ b/lib/openflow/actions/nx_push_mpls.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxPushMpls do + defstruct(ethertype: 0x8847) + + @experimenter 0x00002320 + @nxast 23 + + alias __MODULE__ + + def new(ethertype \\ 0x8847) do + %NxPushMpls{ethertype: ethertype} + end + + def to_binary(%NxPushMpls{ethertype: ethertype}) do + exp_body = <<@experimenter::32, @nxast::16, ethertype::16, 0::size(4)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ethertype::16, _::size(4)-unit(8)>>) do + %NxPushMpls{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/nx_reg_load.ex b/lib/openflow/actions/nx_reg_load.ex new file mode 100644 index 0000000..ae5a6ac --- /dev/null +++ b/lib/openflow/actions/nx_reg_load.ex @@ -0,0 +1,54 @@ +defmodule Openflow.Action.NxRegLoad do + import Bitwise + + defstruct( + n_bits: 0, + offset: 0, + dst_field: nil, + value: nil + ) + + @experimenter 0x00002320 + @nxast 7 + + alias __MODULE__ + + def new(options) do + dst_field = Keyword.get(options, :dst_field) + default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + value = Keyword.get(options, :value) + %NxRegLoad{n_bits: n_bits, + offset: ofs, + dst_field: dst_field, + value: value} + end + + def to_binary(%NxRegLoad{n_bits: n_bits, + offset: ofs, + dst_field: dst_field, + value: value}) do + dst_field_bin = Openflow.Match.codec_header(dst_field) + value_bin0 = Openflow.Match.Field.codec(value, dst_field) + tmp_value = :binary.decode_unsigned(value_bin0, :big) + value_bin = <> + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + dst_field = Openflow.Match.codec_header(dst_field_bin) + value = Openflow.Match.Field.codec(value_bin, dst_field) + %NxRegLoad{n_bits: n_bits + 1, + offset: ofs, + dst_field: dst_field, + value: value} + end +end diff --git a/lib/openflow/actions/nx_reg_load2.ex b/lib/openflow/actions/nx_reg_load2.ex new file mode 100644 index 0000000..00d4cf6 --- /dev/null +++ b/lib/openflow/actions/nx_reg_load2.ex @@ -0,0 +1,40 @@ +defmodule Openflow.Action.NxRegLoad2 do + defstruct( + dst_field: nil, + value: nil + ) + + @experimenter 0x00002320 + @nxast 33 + + alias __MODULE__ + + def new(options) do + dst_field = Keyword.get(options, :dst_field) + value = Keyword.get(options, :value) + %NxRegLoad2{dst_field: dst_field, value: value} + end + + def to_binary(%NxRegLoad2{dst_field: dst_field, value: value}) do + match_bin = + [{dst_field, value}] + |> Openflow.Match.new + |> Openflow.Match.to_binary + <<1::16, _length::16, padded_field::bytes>> = match_bin + patial_len = 4 + 4 + 2 + 6 + byte_size(padded_field) + padding = Openflow.Utils.padding(patial_len, 8) + exp_body = <<@experimenter::32, @nxast::16, 0::48, padded_field::bytes, 0::size(padding)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, _::48, match_field_bin::bytes>>) do + <<_class::16, _field::7, _hm::1, flen::8, _rest::bytes>> = match_field_bin + match_len = 4 + 4 + flen + match_bin = <<1::16, match_len::16, match_field_bin::bytes, 0::size(4)-unit(8)>> + {[{dst_field, value}|_], _rest} = Openflow.Match.read(match_bin) + %NxRegLoad2{dst_field: dst_field, value: value} + end +end diff --git a/lib/openflow/actions/nx_reg_move.ex b/lib/openflow/actions/nx_reg_move.ex new file mode 100644 index 0000000..f50a3f7 --- /dev/null +++ b/lib/openflow/actions/nx_reg_move.ex @@ -0,0 +1,56 @@ +defmodule Openflow.Action.NxRegMove do + defstruct( + n_bits: 0, + src_offset: 0, + dst_offset: 0, + src_field: nil, + dst_field: nil + ) + + @experimenter 0x00002320 + @nxast 6 + + alias __MODULE__ + + def new(options) do + src_field = Keyword.get(options, :src_field) + dst_field = Keyword.get(options, :dst_field) + default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + src_ofs = Keyword.get(options, :src_offset, 0) + dst_ofs = Keyword.get(options, :dst_offset, 0) + %NxRegMove{n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs, + src_field: src_field, + dst_field: dst_field} + end + + def to_binary(%NxRegMove{n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs, + src_field: src_field, + dst_field: dst_field}) do + src_field_bin = Openflow.Match.codec_header(src_field) + dst_field_bin = Openflow.Match.codec_header(dst_field) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + src_field = Openflow.Match.codec_header(src_field_bin) + dst_field = Openflow.Match.codec_header(dst_field_bin) + %NxRegMove{n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs, + src_field: src_field, + dst_field: dst_field} + end +end diff --git a/lib/openflow/actions/nx_resubmit.ex b/lib/openflow/actions/nx_resubmit.ex new file mode 100644 index 0000000..4167a19 --- /dev/null +++ b/lib/openflow/actions/nx_resubmit.ex @@ -0,0 +1,26 @@ +defmodule Openflow.Action.NxResubmit do + defstruct([in_port: :in_port]) + + @experimenter 0x00002320 + @nxast 1 + + alias __MODULE__ + + def new(in_port \\ :in_port) do + %NxResubmit{in_port: in_port} + end + + def to_binary(%NxResubmit{in_port: in_port}) do + in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no) + exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, 0::size(4)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, in_port_int::16, _::size(4)-unit(8)>>) do + in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no) + %NxResubmit{in_port: in_port} + end +end diff --git a/lib/openflow/actions/nx_resubmit_table.ex b/lib/openflow/actions/nx_resubmit_table.ex new file mode 100644 index 0000000..663ad28 --- /dev/null +++ b/lib/openflow/actions/nx_resubmit_table.ex @@ -0,0 +1,33 @@ +defmodule Openflow.Action.NxResubmitTable do + defstruct([in_port: :in_port, table_id: :all]) + + @experimenter 0x00002320 + @nxast 14 + + alias __MODULE__ + + def new(table_id) when is_atom(table_id) or is_integer(table_id) do + new(table_id: table_id) + end + def new(options) do + in_port = Keyword.get(options, :in_port, :in_port) + table_id = Keyword.get(options, :table_id, :all) + %NxResubmitTable{in_port: in_port, table_id: table_id} + end + + def to_binary(%NxResubmitTable{in_port: in_port, table_id: table_id}) do + in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no) + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::24>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::size(3)-unit(8)>>) do + in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + %NxResubmitTable{in_port: in_port, table_id: table_id} + end +end diff --git a/lib/openflow/actions/nx_resubmit_table_ct.ex b/lib/openflow/actions/nx_resubmit_table_ct.ex new file mode 100644 index 0000000..01398e1 --- /dev/null +++ b/lib/openflow/actions/nx_resubmit_table_ct.ex @@ -0,0 +1,30 @@ +defmodule Openflow.Action.NxResubmitTableCt do + defstruct(in_port: :in_port, table_id: :all) + + @experimenter 0x00002320 + @nxast 44 + + alias __MODULE__ + + def new(options) do + in_port = Keyword.get(options, :in_port, :in_port) + table_id = Keyword.get(options, :table_id, :all) + %NxResubmitTableCt{in_port: in_port, table_id: table_id} + end + + def to_binary(%NxResubmitTableCt{in_port: in_port, table_id: table_id}) do + in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no) + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::24>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> +end + + def read(<<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::size(3)-unit(8)>>) do + in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + %NxResubmitTableCt{in_port: in_port, table_id: table_id} + end +end diff --git a/lib/openflow/actions/nx_sample.ex b/lib/openflow/actions/nx_sample.ex new file mode 100644 index 0000000..b133e38 --- /dev/null +++ b/lib/openflow/actions/nx_sample.ex @@ -0,0 +1,44 @@ +defmodule Openflow.Action.NxSample do + defstruct( + probability: 0, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0 + ) + + @experimenter 0x00002320 + @nxast 29 + + alias __MODULE__ + + def new(options) do + probability = Keyword.get(options, :probability, 0) + collector_set_id = Keyword.get(options, :collector_set_id, 0) + obs_domain_id = Keyword.get(options, :obs_domain_id, 0) + obs_point_id = Keyword.get(options, :obs_point_id, 0) + %NxSample{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id} + end + + def to_binary(%NxSample{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id}) do + exp_body = <<@experimenter::32, @nxast::16, probability::16, + collector_set_id::32, obs_domain_id::32, obs_point_id::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, probability::16, + collector_set_id::32, obs_domain_id::32, obs_point_id::32>>) do + %NxSample{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id} + end +end diff --git a/lib/openflow/actions/nx_sample2.ex b/lib/openflow/actions/nx_sample2.ex new file mode 100644 index 0000000..fd15c5b --- /dev/null +++ b/lib/openflow/actions/nx_sample2.ex @@ -0,0 +1,49 @@ +defmodule Openflow.Action.NxSample2 do + defstruct( + probability: 0, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0, + sampling_port: 0 + ) + + @experimenter 0x00002320 + @nxast 38 + + alias __MODULE__ + + def new(options) do + probability = Keyword.get(options, :probability, 0) + collector_set_id = Keyword.get(options, :collector_set_id, 0) + obs_domain_id = Keyword.get(options, :obs_domain_id, 0) + obs_point_id = Keyword.get(options, :obs_point_id, 0) + sampling_port = Keyword.get(options, :sampling_port, 0) + %NxSample2{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port} + end + + def to_binary(%NxSample2{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port}) do + exp_body = <<@experimenter::32, @nxast::16, probability::16, + collector_set_id::32, obs_domain_id::32, obs_point_id::32, sampling_port::16, 0::size(6)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, + obs_domain_id::32, obs_point_id::32, sampling_port::16, 0::size(6)-unit(8)>>) do + %NxSample2{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port} + end +end diff --git a/lib/openflow/actions/nx_sample3.ex b/lib/openflow/actions/nx_sample3.ex new file mode 100644 index 0000000..f7e10ac --- /dev/null +++ b/lib/openflow/actions/nx_sample3.ex @@ -0,0 +1,56 @@ +defmodule Openflow.Action.NxSample3 do + defstruct( + probability: 0, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0, + sampling_port: 0, + direction: :default + ) + + @experimenter 0x00002320 + @nxast 41 + + alias __MODULE__ + + def new(options) do + probability = Keyword.get(options, :probability, 0) + collector_set_id = Keyword.get(options, :collector_set_id, 0) + obs_domain_id = Keyword.get(options, :obs_domain_id, 0) + obs_point_id = Keyword.get(options, :obs_point_id, 0) + sampling_port = Keyword.get(options, :sampling_port, 0) + direction = Keyword.get(options, :direction, :default) + %NxSample3{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port, + direction: direction} + end + + def to_binary(%NxSample3{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port, + direction: direction}) do + direction_int = Openflow.Enums.to_int(direction, :nx_action_sample_direction) + exp_body = <<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, + obs_domain_id::32, obs_point_id::32, sampling_port::16, direction_int::8, 0::size(5)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, + obs_domain_id::32, obs_point_id::32, sampling_port::16, direction_int::8, 0::size(5)-unit(8)>>) do + direction = Openflow.Enums.to_atom(direction_int, :nx_action_sample_direction) + %NxSample3{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port, + direction: direction} + end +end diff --git a/lib/openflow/actions/nx_set_mpls_label.ex b/lib/openflow/actions/nx_set_mpls_label.ex new file mode 100644 index 0000000..99436da --- /dev/null +++ b/lib/openflow/actions/nx_set_mpls_label.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetMplsLabel do + defstruct(label: 0) + + @experimenter 0x00002320 + @nxast 30 + + alias __MODULE__ + + def new(label) do + %NxSetMplsLabel{label: label} + end + + def to_binary(%NxSetMplsLabel{label: label}) do + exp_body = <<@experimenter::32, @nxast::16, 0::16, label::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, _::16, label::32>>) do + %NxSetMplsLabel{label: label} + end +end diff --git a/lib/openflow/actions/nx_set_mpls_tc.ex b/lib/openflow/actions/nx_set_mpls_tc.ex new file mode 100644 index 0000000..16414f2 --- /dev/null +++ b/lib/openflow/actions/nx_set_mpls_tc.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetMplsTc do + defstruct(tc: 0) + + @experimenter 0x00002320 + @nxast 31 + + alias __MODULE__ + + def new(tc) do + %NxSetMplsTc{tc: tc} + end + + def to_binary(%NxSetMplsTc{tc: tc}) do + exp_body = <<@experimenter::32, @nxast::16, tc::8, 0::size(5)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, tc::8, _::size(5)-unit(8)>>) do + %NxSetMplsTc{tc: tc} + end +end diff --git a/lib/openflow/actions/nx_set_mpls_ttl.ex b/lib/openflow/actions/nx_set_mpls_ttl.ex new file mode 100644 index 0000000..929398f --- /dev/null +++ b/lib/openflow/actions/nx_set_mpls_ttl.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetMplsTtl do + defstruct(ttl: 0) + + @experimenter 0x00002320 + @nxast 25 + + alias __MODULE__ + + def new(ttl) do + %NxSetMplsTtl{ttl: ttl} + end + + def to_binary(%NxSetMplsTtl{ttl: ttl}) do + exp_body = <<@experimenter::32, @nxast::16, ttl::8, 0::size(5)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ttl::8, _::size(5)-unit(8)>>) do + %NxSetMplsTtl{ttl: ttl} + end +end diff --git a/lib/openflow/actions/nx_set_queue.ex b/lib/openflow/actions/nx_set_queue.ex new file mode 100644 index 0000000..d133276 --- /dev/null +++ b/lib/openflow/actions/nx_set_queue.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetQueue do + defstruct([queue_id: 0]) + + @experimenter 0x00002320 + @nxast 4 + + alias __MODULE__ + + def new(queue_id) do + %NxSetQueue{queue_id: queue_id} + end + + def to_binary(%NxSetQueue{queue_id: queue_id}) do + exp_body = <<@experimenter::32, @nxast::16, 0::16, queue_id::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(2)-unit(8), queue_id::32>>) do + %NxSetQueue{queue_id: queue_id} + end +end diff --git a/lib/openflow/actions/nx_set_tunnel.ex b/lib/openflow/actions/nx_set_tunnel.ex new file mode 100644 index 0000000..0ed3d14 --- /dev/null +++ b/lib/openflow/actions/nx_set_tunnel.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetTunnel do + defstruct(tunnel_id: 0) + + @experimenter 0x00002320 + @nxast 2 + + alias __MODULE__ + + def new(tunnel_id) do + %NxSetTunnel{tunnel_id: tunnel_id} + end + + def to_binary(%NxSetTunnel{tunnel_id: tunnel_id}) do + exp_body = <<@experimenter::32, @nxast::16, 0::16, tunnel_id::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, _::16, tunnel_id::32>>) do + %NxSetTunnel{tunnel_id: tunnel_id} + end +end diff --git a/lib/openflow/actions/nx_set_tunnel64.ex b/lib/openflow/actions/nx_set_tunnel64.ex new file mode 100644 index 0000000..c70b230 --- /dev/null +++ b/lib/openflow/actions/nx_set_tunnel64.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetTunnel64 do + defstruct([tunnel_id: 0]) + + @experimenter 0x00002320 + @nxast 9 + + alias __MODULE__ + + def new(tunnel_id) do + %NxSetTunnel64{tunnel_id: tunnel_id} + end + + def to_binary(%NxSetTunnel64{tunnel_id: tunnel_id}) do + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>>) do + %NxSetTunnel64{tunnel_id: tunnel_id} + end +end diff --git a/lib/openflow/actions/nx_stack_pop.ex b/lib/openflow/actions/nx_stack_pop.ex new file mode 100644 index 0000000..c106678 --- /dev/null +++ b/lib/openflow/actions/nx_stack_pop.ex @@ -0,0 +1,36 @@ +defmodule Openflow.Action.NxStackPop do + defstruct( + n_bits: 0, + offset: 0, + field: nil + ) + + @experimenter 0x00002320 + @nxast 28 + + alias __MODULE__ + + def new(options) do + field = Keyword.get(options, :field) + default_n_bits = Openflow.Match.Field.n_bits_of(field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + %NxStackPop{n_bits: n_bits, offset: ofs, field: field} + end + + def to_binary(%NxStackPop{n_bits: n_bits, offset: ofs, field: field}) do + field_bin = Openflow.Match.codec_header(field) + exp_body = <<@experimenter::32, @nxast::16, ofs::16, + field_bin::4-bytes, n_bits::16, 0::size(6)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ofs::16, + field_bin::4-bytes, n_bits::16, _::size(6)-unit(8)>>) do + field = Openflow.Match.codec_header(field_bin) + %NxStackPop{n_bits: n_bits, offset: ofs, field: field} + end +end diff --git a/lib/openflow/actions/nx_stack_push.ex b/lib/openflow/actions/nx_stack_push.ex new file mode 100644 index 0000000..21dc9c0 --- /dev/null +++ b/lib/openflow/actions/nx_stack_push.ex @@ -0,0 +1,36 @@ +defmodule Openflow.Action.NxStackPush do + defstruct( + n_bits: 0, + offset: 0, + field: nil + ) + + @experimenter 0x00002320 + @nxast 27 + + alias __MODULE__ + + def new(options) do + field = Keyword.get(options, :field) + default_n_bits = Openflow.Match.Field.n_bits_of(field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + %NxStackPush{n_bits: n_bits, offset: ofs, field: field} + end + + def to_binary(%NxStackPush{n_bits: n_bits, offset: ofs, field: field}) do + field_bin = Openflow.Match.codec_header(field) + exp_body = <<@experimenter::32, @nxast::16, ofs::16, + field_bin::4-bytes, n_bits::16, 0::size(6)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ofs::16, + field_bin::4-bytes, n_bits::16, _::size(6)-unit(8)>>) do + field = Openflow.Match.codec_header(field_bin) + %NxStackPush{n_bits: n_bits, offset: ofs, field: field} + end +end diff --git a/lib/openflow/actions/nx_write_metadata.ex b/lib/openflow/actions/nx_write_metadata.ex new file mode 100644 index 0000000..5450257 --- /dev/null +++ b/lib/openflow/actions/nx_write_metadata.ex @@ -0,0 +1,32 @@ +defmodule Openflow.Action.NxWriteMetadata do + defstruct( + metadata: 0, + metadata_mask: 0xffffffffffffffff + ) + + @experimenter 0x00002320 + @nxast 22 + + alias __MODULE__ + + def new(metadata) when is_integer(metadata) do + new(metadata: metadata) + end + def new(options) when is_list(options) do + metadata = Keyword.get(options, :metadata, 0) + metadata_mask = Keyword.get(options, :metadata_mask, 0xffffffffffffffff) + %NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask} + end + + def to_binary(%NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask}) do + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), metadata::64, metadata_mask::64>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8), metadata::64, metadata_mask::64>>) do + %NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask} + end +end diff --git a/lib/openflow/actions/output.ex b/lib/openflow/actions/output.ex new file mode 100644 index 0000000..116dc96 --- /dev/null +++ b/lib/openflow/actions/output.ex @@ -0,0 +1,31 @@ +defmodule Openflow.Action.Output do + defstruct( + port_number: 0, + max_len: :no_buffer + ) + + alias __MODULE__ + + def ofpat, do: 0 + + def new(port) when not is_list(port) do + new(port_number: port) + end + def new(options) when is_list(options) do + port_no = Keyword.get(options, :port_number) + max_len = Keyword.get(options, :max_len, :no_buffer) + %Output{port_number: port_no, max_len: max_len} + end + + def to_binary(%Output{port_number: port_no, max_len: max_len}) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + <<0::16, 16::16, port_no_int::32, max_len::16, 0::size(6)-unit(8)>> + end + + def read(<<0::16, 16::16, port_no_int::32, max_len::16, _pad::size(6)-unit(8)>>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + %Output{port_number: port_no, max_len: max_len} + end +end diff --git a/lib/openflow/actions/pop_mpls.ex b/lib/openflow/actions/pop_mpls.ex new file mode 100644 index 0000000..7ea7950 --- /dev/null +++ b/lib/openflow/actions/pop_mpls.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PopMpls do + defstruct(ethertype: 0x8847) + + alias __MODULE__ + + def ofpat, do: 20 + + def new(ethertype) do + %PopMpls{ethertype: ethertype} + end + + def to_binary(%PopMpls{ethertype: ethertype}) do + <<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> + end + + def read(<<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do + %PopMpls{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/pop_pbb.ex b/lib/openflow/actions/pop_pbb.ex new file mode 100644 index 0000000..d2e4c0d --- /dev/null +++ b/lib/openflow/actions/pop_pbb.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PopPbb do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 27 + + def new do + %PopPbb{} + end + + def to_binary(%PopPbb{}) do + <<27::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<27::16, 8::16, _::size(4)-unit(8)>>) do + %PopPbb{} + end +end diff --git a/lib/openflow/actions/pop_vlan.ex b/lib/openflow/actions/pop_vlan.ex new file mode 100644 index 0000000..8ea4f26 --- /dev/null +++ b/lib/openflow/actions/pop_vlan.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PopVlan do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 18 + + def new do + %PopVlan{} + end + + def to_binary(%PopVlan{}) do + <<18::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<18::16, 8::16, _::size(4)-unit(8)>>) do + %PopVlan{} + end +end diff --git a/lib/openflow/actions/push_mpls.ex b/lib/openflow/actions/push_mpls.ex new file mode 100644 index 0000000..f9c7fe0 --- /dev/null +++ b/lib/openflow/actions/push_mpls.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PushMpls do + defstruct(ethertype: 0x8847) + + alias __MODULE__ + + def ofpat, do: 19 + + def new(ethertype) do + %PushMpls{ethertype: ethertype} + end + + def to_binary(%PushMpls{ethertype: ethertype}) do + <<19::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> + end + + def read(<<19::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do + %PushMpls{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/push_pbb.ex b/lib/openflow/actions/push_pbb.ex new file mode 100644 index 0000000..f509b70 --- /dev/null +++ b/lib/openflow/actions/push_pbb.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PushPbb do + defstruct(ethertype: 0x88e7) + + alias __MODULE__ + + def ofpat, do: 26 + + def new(ethertype) do + %PushPbb{ethertype: ethertype} + end + + def to_binary(%PushPbb{ethertype: ethertype}) do + <<26::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> + end + + def read(<<26::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do + %PushPbb{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/push_vlan.ex b/lib/openflow/actions/push_vlan.ex new file mode 100644 index 0000000..056f033 --- /dev/null +++ b/lib/openflow/actions/push_vlan.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PushVlan do + defstruct(ethertype: 0x8100) + + alias __MODULE__ + + def ofpat, do: 17 + + def new(ethertype) do + %PushVlan{ethertype: ethertype} + end + + def to_binary(%PushVlan{ethertype: ethertype}) do + <<17::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> + end + + def read(<<17::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do + %PushVlan{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/set_field.ex b/lib/openflow/actions/set_field.ex new file mode 100644 index 0000000..ed962ab --- /dev/null +++ b/lib/openflow/actions/set_field.ex @@ -0,0 +1,34 @@ +defmodule Openflow.Action.SetField do + defstruct(field: nil) + + alias __MODULE__ + + @set_field_size 8 + + def ofpat, do: 25 + + def new({_field, _value} = oxm_field) do + %SetField{field: oxm_field} + end + + def to_binary(%SetField{field: {field, value}}) do + match_bin = + [{field, value}] + |> Openflow.Match.new + |> Openflow.Match.to_binary + + <<1::16, _length::16, padded_field::bytes>> = match_bin + patial_len = @set_field_size - 4 + byte_size(padded_field) + padding = Openflow.Utils.padding(patial_len, 8) + length = patial_len + padding + <<25::16, length::16, padded_field::bytes, 0::size(padding)-unit(8)>> + end + + def read(<<25::16, _length::16, match_field_bin::bytes>>) do + <<_class::16, _field::7, _hm::1, flen::8, _rest::bytes>>= match_field_bin + match_len = 4 + 4 + flen + match_bin = <<1::16, match_len::16, match_field_bin::bytes>> + {[field|_], _rest} = Openflow.Match.read(match_bin) + %SetField{field: field} + end +end diff --git a/lib/openflow/actions/set_mpls_ttl.ex b/lib/openflow/actions/set_mpls_ttl.ex new file mode 100644 index 0000000..a82a0ec --- /dev/null +++ b/lib/openflow/actions/set_mpls_ttl.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.SetMplsTtl do + defstruct(ttl: 0) + + alias __MODULE__ + + def ofpat, do: 15 + + def new(ttl) do + %SetMplsTtl{ttl: ttl} + end + + def to_binary(%SetMplsTtl{ttl: ttl}) do + <<15::16, 8::16, ttl::8, 0::size(3)-unit(8)>> + end + + def read(<<15::16, 8::16, ttl::8, _::size(3)-unit(8)>>) do + %SetMplsTtl{ttl: ttl} + end +end diff --git a/lib/openflow/actions/set_nw_ttl.ex b/lib/openflow/actions/set_nw_ttl.ex new file mode 100644 index 0000000..48a296a --- /dev/null +++ b/lib/openflow/actions/set_nw_ttl.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.SetNwTtl do + defstruct(ttl: 0) + + alias __MODULE__ + + def ofpat, do: 23 + + def new(ttl) do + %SetNwTtl{ttl: ttl} + end + + def to_binary(%SetNwTtl{ttl: ttl}) do + <<23::16, 8::16, ttl::8, 0::size(3)-unit(8)>> + end + + def read(<<23::16, 8::16, ttl::8, _::size(3)-unit(8)>>) do + %SetNwTtl{ttl: ttl} + end +end diff --git a/lib/openflow/actions/set_queue.ex b/lib/openflow/actions/set_queue.ex new file mode 100644 index 0000000..001f4ff --- /dev/null +++ b/lib/openflow/actions/set_queue.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.SetQueue do + defstruct(id: 0) + + alias __MODULE__ + + def ofpat, do: 21 + + def new(id) do + %SetQueue{id: id} + end + + def to_binary(%SetQueue{id: id}) do + <<21::16, 8::16, id::32>> + end + + def read(<<21::16, 8::16, id::32>>) do + %SetQueue{id: id} + end +end diff --git a/lib/openflow/barrier/reply.ex b/lib/openflow/barrier/reply.ex new file mode 100644 index 0000000..a392f3d --- /dev/null +++ b/lib/openflow/barrier/reply.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Barrier.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 21 + + def new do + %Reply{} + end + + def read(_) do + %Reply{} + end + + def to_binary(%Reply{}) do + <<>> + end +end diff --git a/lib/openflow/barrier/request.ex b/lib/openflow/barrier/request.ex new file mode 100644 index 0000000..6ca6437 --- /dev/null +++ b/lib/openflow/barrier/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Barrier.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 20 + + def new do + %Request{} + end + + def read(_) do + %Request{} + end + + def to_binary(%Request{}) do + <<>> + end +end diff --git a/lib/openflow/buckets.ex b/lib/openflow/buckets.ex new file mode 100644 index 0000000..806f3b6 --- /dev/null +++ b/lib/openflow/buckets.ex @@ -0,0 +1,54 @@ +defmodule Openflow.Bucket do + defstruct( + weight: 0, + watch_port: 0, + watch_group: 0, + actions: [] + ) + + alias __MODULE__ + + @header_size 16 + + def new(options) do + weight = Keyword.get(options, :weight, 0) + watch_port = Keyword.get(options, :watch_port, :any) + watch_group = Keyword.get(options, :watch_group, :any) + actions = Keyword.get(options, :actions, []) + %Bucket{weight: weight, watch_port: watch_port, watch_group: watch_group, actions: actions} + end + + def read(buckets_bin) do + do_read([], buckets_bin) + end + + def to_binary(buckets) do + to_binary("", buckets) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do + actions_len = length - @header_size + <> = binary + watch_port = Openflow.Utils.get_enum(watch_port_int, :openflow13_port_no) + watch_group = Openflow.Utils.get_enum(watch_group_int, :group_id) + actions = Openflow.Action.read(actions_bin) + bucket = %Bucket{weight: weight, watch_port: watch_port, watch_group: watch_group, actions: actions} + do_read([bucket|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [bucket|rest]) do + %Bucket{weight: weight, watch_port: watch_port, watch_group: watch_group, actions: actions} = bucket + watch_port_int = Openflow.Utils.get_enum(watch_port, :openflow13_port_no) + watch_group_int = Openflow.Utils.get_enum(watch_group, :group_id) + actions_bin = Openflow.Action.to_binary(actions) + length = byte_size(actions_bin) + @header_size + bucket_bin = <> + to_binary(<>, rest) + end +end diff --git a/lib/openflow/echo.ex b/lib/openflow/echo.ex new file mode 100644 index 0000000..d0cea11 --- /dev/null +++ b/lib/openflow/echo.ex @@ -0,0 +1,2 @@ +defmodule Openflow.Echo do +end diff --git a/lib/openflow/echo/reply.ex b/lib/openflow/echo/reply.ex new file mode 100644 index 0000000..5955bf3 --- /dev/null +++ b/lib/openflow/echo/reply.ex @@ -0,0 +1,25 @@ +defmodule Openflow.Echo.Reply do + defstruct( + version: 4, + xid: 0, + data: "", + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 3 + + def new(data \\ "") do + %Reply{data: data} + end + + def read(data) do + %Reply{data: data} + end + + def to_binary(%Reply{data: data}) do + data + end +end diff --git a/lib/openflow/echo/request.ex b/lib/openflow/echo/request.ex new file mode 100644 index 0000000..248dede --- /dev/null +++ b/lib/openflow/echo/request.ex @@ -0,0 +1,26 @@ +defmodule Openflow.Echo.Request do + defstruct( + version: 4, + xid: 0, + data: "", + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 2 + + def new(data \\ "") do + %Request{data: data} + end + + def read(data) do + %Request{data: data} + end + + def to_binary(%Request{data: data}) do + data + end +end + diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex new file mode 100644 index 0000000..32f36d4 --- /dev/null +++ b/lib/openflow/enums.ex @@ -0,0 +1,1081 @@ +defmodule Openflow.Enums do + import Bitwise + + @enums [ + openflow_codec: [ + {Openflow.Hello, 0}, + {Openflow.ErrorMsg, 1}, + {Openflow.Echo.Request, 2}, + {Openflow.Echo.Reply, 3}, + {Openflow.Experimenter, 4}, + {Openflow.Features.Request, 5}, + {Openflow.Features.Reply, 6}, + {Openflow.GetConfig.Request, 7}, + {Openflow.GetConfig.Reply, 8}, + {Openflow.SetConfig, 9}, + {Openflow.PacketIn, 10}, + {Openflow.FlowRemoved, 11}, + {Openflow.PortStatus, 12}, + {Openflow.PacketOut, 13}, + {Openflow.FlowMod, 14}, + {Openflow.GroupMod, 15}, + {Openflow.PortMod, 16}, + {Openflow.TableMod, 17}, + {Openflow.Multipart.Request, 18}, + {Openflow.Multipart.Reply, 19}, + {Openflow.Barrier.Request, 20}, + {Openflow.Barrier.Reply, 21}, + {Openflow.Role.Request, 24}, + {Openflow.Role.Reply, 25}, + {Openflow.GetAsync.Request, 26}, + {Openflow.GetAsync.Reply, 27}, + {Openflow.SetAsync, 28}, + {Openflow.MeterMod, 29} + ], + + experimenter_id: [ + nicira_ext_message: 0x00002320, + onf_ext_message: 0x4f4e4600 + ], + + nicira_ext_message: [ + # {Openflow.NxRole.Request, 10}, /* Openflow 1.3 support role request/reply */ + # {Openflow.NxRole.Reply, 11}, + # {Openflow.NxSetFlowFormat, 12}, /* No special reason for implement this struct codec. */ + # {Openflow.NxFlowMod, 13}, /* Prefer use ofp_flow_mod to nx_flow_mod */ + # {Openflow.NxFlowRemoved, 14}, /* Prefer use ofp_flow_removed to nx_flow_removed */ + # {Openflow.NxSetFlowModTableId, 15}, /* OpenFlow 1.3 support multiple flow table. */ + {Openflow.NxSetPacketInFormat, 16}, + # {Openflow.NxPacketIn, 17}, /* No special reason for implement this struct codec. */ + # {Openflow.NxFlowAge, 18}, /* No special reason for implement this struct codec. */ + # {Openflow.NxSetAsyncConfig, 19}, /* Openflow 1.3 support async config. */ + {Openflow.NxSetControllerId, 20}, + {Openflow.NxFlowMonitor.Cancel, 21}, + {Openflow.NxFlowMonitor.Paused, 22}, + {Openflow.NxFlowMonitor.Resumed, 23}, + {Openflow.NxTLVTableMod, 24}, + {Openflow.NxTLVTable.Request, 25}, + {Openflow.NxTLVTable.Reply, 26}, + {Openflow.NxSetAsyncConfig2, 27}, + {Openflow.NxResume, 28}, + {Openflow.NxCtFlushZone, 29}, + {Openflow.NxPacketIn2, 30} + ], + + onf_ext_message: [ + {Openflow.OnfBundleControl, 2300}, + {Openflow.OnfBundleAddMessage, 2301}, + ], + + multipart_request_flags: [ + more: 1 <<< 0 + ], + + multipart_reply_flags: [ + more: 1 <<< 0 + ], + + multipart_request_codec: [ + {Openflow.Multipart.Desc.Request, 0}, + {Openflow.Multipart.Flow.Request, 1}, + {Openflow.Multipart.Aggregate.Request, 2}, + {Openflow.Multipart.Table.Request, 3}, + {Openflow.Multipart.PortStats.Request, 4}, + {Openflow.Multipart.Queue.Request, 5}, + {Openflow.Multipart.Group.Request, 6}, + {Openflow.Multipart.GroupDesc.Request, 7}, + {Openflow.Multipart.GroupFeatures.Request, 8}, + {Openflow.Multipart.Meter.Request, 9}, + {Openflow.Multipart.MeterConfig.Request, 10}, + {Openflow.Multipart.MeterFeatures.Request, 11}, + {Openflow.Multipart.TableFeatures.Request, 12}, + {Openflow.Multipart.PortDesc.Request, 13}, + {Openflow.Multipart.Experimenter.Request, 0xffff}, + ], + + multipart_reply_codec: [ + {Openflow.Multipart.Desc.Reply, 0}, + {Openflow.Multipart.Flow.Reply, 1}, + {Openflow.Multipart.Aggregate.Reply, 2}, + {Openflow.Multipart.Table.Reply, 3}, + {Openflow.Multipart.PortStats.Reply, 4}, + {Openflow.Multipart.Queue.Reply, 5}, + {Openflow.Multipart.Group.Reply, 6}, + {Openflow.Multipart.GroupDesc.Reply, 7}, + {Openflow.Multipart.GroupFeatures.Reply, 8}, + {Openflow.Multipart.Meter.Reply, 9}, + {Openflow.Multipart.MeterConfig.Reply, 10}, + {Openflow.Multipart.MeterFeatures.Reply, 11}, + {Openflow.Multipart.TableFeatures.Reply, 12}, + {Openflow.Multipart.PortDesc.Reply, 13}, + {Openflow.Multipart.Experimenter.Reply, 0xffff}, + ], + + nicira_ext_stats: [ + {Openflow.Multipart.NxFlow, 0}, + {Openflow.Multipart.NxAggregate, 1}, + {Openflow.Multipart.NxFlowMonitor, 2}, + {Openflow.Multipart.NxIPFIXBridge, 3}, + {Openflow.Multipart.NxIPFIXFlow, 4}, + ], + + hello_elem: [ + versionbitmap: 1 + ], + + error_type: [ + hello_failed: 0, + bad_request: 1, + bad_action: 2, + bad_instruction: 3, + bad_match: 4, + flow_mod_failed: 5, + group_mod_failed: 6, + port_mod_failed: 7, + table_mod_failed: 8, + queue_op_failed: 9, + switch_config_failed: 10, + role_request_failed: 11, + meter_mod_failed: 12, + table_features_failed: 13, + experimenter: 0xffff + ], + + hello_failed: [ + inconpatible: 0, + eperm: 1 + ], + + bad_request: [ + bad_version: 0, + bad_type: 1, + bad_multipart: 2, + bad_experimeter: 3, + bad_exp_type: 4, + eperm: 5, + bad_len: 6, + buffer_empty: 7, + buffer_unknown: 8, + bad_table_id: 9, + is_slave: 10, + bad_port: 11, + bad_packet: 12, + multipart_buffer_overflow: 13 + ], + + bad_action: [ + bad_type: 0, + bad_len: 1, + bad_experimeter: 2, + bad_exp_type: 3, + bad_out_port: 4, + bad_argument: 5, + eperm: 6, + too_many: 7, + bad_queue: 8, + bad_out_group: 9, + match_inconsistent: 10, + unsupported_order: 11, + bad_tag: 12, + bad_set_type: 13, + bad_set_len: 14, + bad_set_argument: 15 + ], + + bad_instruction: [ + unknown_instruction: 0, + unsupported_instruction: 1, + bad_table_id: 2, + unsupported_metadata: 3, + unsupported_metadata_mask: 4, + bad_experimeter: 5, + bad_exp_type: 6, + bad_len: 7, + eperm: 8 + ], + + bad_match: [ + bad_type: 0, + bad_len: 1, + bad_tag: 2, + bad_dl_addr_mask: 3, + bad_nw_addr_mask: 4, + bad_wildcards: 5, + bad_field: 6, + bad_value: 7, + bad_mask: 8, + bad_prereq: 9, + dup_field: 10, + eperm: 11 + ], + + flow_mod_failed: [ + unknown: 0, + table_full: 1, + bad_table_id: 2, + overlap: 3, + eperm: 4, + bad_timeout: 5, + bad_command: 6, + bad_flags: 7 + ], + + group_mod_failed: [ + group_exists: 0, + invalid_group: 1, + weight_unsupported: 2, + out_of_groups: 3, + ouf_of_buckets: 4, + chaining_unsupported: 5, + watch_unsupported: 6, + loop: 7, + unknown_group: 8, + chained_group: 9, + bad_type: 10, + bad_command: 11, + bad_bucket: 12, + bad_watch: 13, + eperm: 14 + ], + + port_mod_failed: [ + bad_port: 0, + bad_hw_addr: 1, + bad_config: 2, + bad_advertise: 3, + eperm: 4 + ], + + table_mod_failed: [ + bad_table: 0, + bad_config: 1, + eperm: 2 + ], + + queue_op_failed: [ + bad_port: 0, + bad_queue: 1, + eperm: 2 + ], + + switch_config_failed: [ + bad_flags: 0, + bad_len: 1, + eperm: 2 + ], + + role_request_failed: [ + stale: 0, + unsup: 1, + bad_role: 2 + ], + + meter_mod_failed: [ + unknown: 0, + meter_exists: 1, + invalid_meter: 2, + unknown_meter: 3, + bad_command: 4, + bad_flags: 5, + bad_rate: 6, + bad_burst: 7, + bad_band: 8, + bad_band_value: 9, + out_of_meters: 10, + out_of_bands: 11 + ], + + table_features_failed: [ + bad_table: 0, + bad_metadata: 1, + bad_type: 2, + bad_len: 3, + bad_argument: 4, + eperm: 5 + ], + + switch_capabilities: [ + flow_stats: 1 <<< 0, + table_stats: 1 <<< 1, + port_stats: 1 <<< 2, + group_stats: 1 <<< 3, + ip_reasm: 1 <<< 5, + queue_stats: 1 <<< 6, + arp_match_ip: 1 <<< 7, + port_blocked: 1 <<< 8 + ], + + config_flags: [ + drop: 1 <<< 0, + reasm: 1 <<< 1 + ], + + controller_max_len: [ + max: 0xffe5, + no_buffer: 0xffff + ], + + experimenter_oxm_vendors: [ + nicira_ext_match: 0x00002320, + onf_ext_match: 0x4f4e4600 + ], + + match_type: [ + standard: 0, + oxm: 1 + ], + + oxm_class: [ + nxm_0: 0x0000, + nxm_1: 0x0001, + openflow_basic: 0x8000, + packet_register: 0x8001, + experimenter: 0xffff + ], + + nxm_0: [ + nx_in_port: 0, + nx_eth_dst: 1, + nx_eth_src: 2, + nx_eth_type: 3, + nx_vlan_tci: 4, + nx_ip_tos: 5, + nx_ip_proto: 6, + nx_ipv4_src: 7, + nx_ipv4_dst: 8, + nx_tcp_src: 9, + nx_tcp_dst: 10, + nx_udp_src: 11, + nx_udp_dst: 12, + nx_icmpv4_type: 13, + nx_icmpv4_code: 14, + nx_arp_op: 15, + nx_arp_spa: 16, + nx_arp_tpa: 17, + nx_tcp_flags: 34, + ], + + nxm_1: [ + reg0: 0, + reg1: 1, + reg2: 2, + reg3: 3, + reg4: 4, + reg5: 5, + reg6: 6, + reg7: 7, + reg8: 8, + reg9: 9, + reg10: 10, + reg11: 11, + reg12: 12, + reg13: 13, + reg14: 14, + reg15: 15, + tun_id: 16, + nx_arp_sha: 17, + nx_arp_tha: 18, + nx_ipv6_src: 19, + nx_ipv6_dst: 20, + nx_icmpv6_type: 21, + nx_icmpv6_code: 22, + nx_ipv6_nd_target: 23, + nx_ipv6_nd_sll: 24, + nx_ipv6_nd_tll: 25, + nx_ip_frag: 26, + nx_ipv6_label: 27, + nx_ip_ecn: 28, + nx_ip_ttl: 29, + nx_mpls_ttl: 30, + tun_src: 31, + tun_dst: 32, + pkt_mark: 33, + dp_hash: 35, + recirc_id: 36, + conj_id: 37, + tun_gbp_id: 38, + tun_gbp_flags: 39, + tun_metadata0: 40, + tun_metadata1: 41, + tun_metadata2: 42, + tun_metadata3: 43, + tun_metadata4: 44, + tun_metadata5: 45, + tun_metadata6: 46, + tun_metadata7: 47, + tun_metadata8: 48, + tun_metadata9: 49, + tun_metadata10: 50, + tun_metadata11: 51, + tun_metadata12: 52, + tun_metadata13: 53, + tun_metadata14: 54, + tun_metadata15: 55, + tun_metadata16: 56, + tun_metadata17: 57, + tun_metadata18: 58, + tun_metadata19: 59, + tun_metadata20: 60, + tun_metadata21: 61, + tun_metadata22: 62, + tun_metadata23: 63, + tun_metadata24: 64, + tun_metadata25: 65, + tun_metadata26: 66, + tun_metadata27: 67, + tun_metadata28: 68, + tun_metadata29: 69, + tun_metadata30: 70, + tun_metadata31: 71, + tun_metadata32: 72, + tun_metadata33: 73, + tun_metadata34: 74, + tun_metadata35: 75, + tun_metadata36: 76, + tun_metadata37: 77, + tun_metadata38: 78, + tun_metadata39: 79, + tun_metadata40: 80, + tun_metadata41: 81, + tun_metadata42: 82, + tun_metadata43: 83, + tun_metadata44: 84, + tun_metadata45: 85, + tun_metadata46: 86, + tun_metadata47: 87, + tun_metadata48: 88, + tun_metadata49: 89, + tun_metadata50: 90, + tun_metadata51: 91, + tun_metadata52: 92, + tun_metadata53: 93, + tun_metadata54: 94, + tun_metadata55: 95, + tun_metadata56: 96, + tun_metadata57: 97, + tun_metadata58: 98, + tun_metadata59: 99, + tun_metadata60: 100, + tun_metadata61: 101, + tun_metadata62: 102, + tun_metadata63: 103, + tun_flags: 104, + ct_state: 105, + ct_zone: 106, + ct_mark: 107, + ct_label: 108, + tun_ipv6_src: 109, + tun_ipv6_dst: 110, + xxreg0: 111, + xxreg1: 112, + xxreg2: 113, + xxreg3: 114, + xxreg4: 115, + xxreg5: 116, + xxreg6: 117, + xxreg7: 118, + ct_nw_proto: 119, + ct_nw_src: 120, + ct_nw_dst: 121, + ct_ipv6_src: 122, + ct_ipv6_dst: 123, + ct_tp_src: 124, + ct_tp_dst: 125 + ], + + openflow_basic: [ + in_port: 0, + in_phy_port: 1, + metadata: 2, + eth_dst: 3, + eth_src: 4, + eth_type: 5, + vlan_vid: 6, + vlan_pcp: 7, + ip_dscp: 8, + ip_ecn: 9, + ip_proto: 10, + ipv4_src: 11, + ipv4_dst: 12, + tcp_src: 13, + tcp_dst: 14, + udp_src: 15, + udp_dst: 16, + sctp_src: 17, + sctp_dst: 18, + icmpv4_type: 19, + icmpv4_code: 20, + arp_op: 21, + arp_spa: 22, + arp_tpa: 23, + arp_sha: 24, + arp_tha: 25, + ipv6_src: 26, + ipv6_dst: 27, + ipv6_flabel: 28, + icmpv6_type: 29, + icmpv6_code: 30, + ipv6_nd_target: 31, + ipv6_nd_sll: 32, + ipv6_nd_tll: 33, + mpls_label: 34, + mpls_tc: 35, + mpls_bos: 36, + pbb_isid: 37, + tunnel_id: 38, + ipv6_exthdr: 39, + + # Lagopus extended match fields + pbb_uca: 41, + packet_type: 42, + gre_flags: 43, + gre_ver: 44, + gre_protocol: 45, + gre_key: 46, + gre_seqnum: 47, + lisp_flags: 48, + lisp_nonce: 49, + lisp_id: 50, + vxlan_flags: 51, + vxlan_vni: 52, + mpls_data_first_nibble: 53, + mpls_ach_version: 54, + mpls_ach_channel: 55, + mpls_pw_metadata: 56, + mpls_cw_flags: 57, + mpls_cw_fragment: 58, + mpls_cw_len: 59, + mpls_cw_seq_num: 60, + gtpu_flags: 61, + gtpu_ver: 62, + gtpu_msg_type: 63, + gtpu_teid: 64, + gtpu_extn_hdr: 65, + gtpu_extn_udp_port: 66, + gtpu_extn_sci: 67 + ], + + vlan_id: [ + present: 0x1000, + none: 0x0000 + ], + + ipv6exthdr_flags: [ + nonext: 1 <<< 0, + esp: 1 <<< 1, + auth: 1 <<< 2, + dest: 1 <<< 3, + frag: 1 <<< 4, + router: 1 <<< 5, + hop: 1 <<< 6, + unrep: 1 <<< 7, + unseq: 1 <<< 8 + ], + + tcp_flags: [ + fin: 1 <<< 0, + syn: 1 <<< 1, + rst: 1 <<< 2, + psh: 1 <<< 3, + ack: 1 <<< 4, + urg: 1 <<< 5, + ece: 1 <<< 6, + cwr: 1 <<< 7, + ns: 1 <<< 8 + ], + + ct_state_flags: [ + new: 1 <<< 0, # Beginning of a new connection. + est: 1 <<< 1, # Part of an existing connection. + rel: 1 <<< 2, # Related to an established connection. + rep: 1 <<< 3, # Flow is in the reply direction. + inv: 1 <<< 4, # Could not track connection. + trk: 1 <<< 5, # Conntrack has occurred. + snat: 1 <<< 6, # Packet's source address/port was mangled by NAT. + dnat: 1 <<< 7 # Packet's destination address/port was mangled by NAT. + ], + + packet_register: [ + xreg0: 0, + xreg1: 1, + xreg2: 2, + xreg3: 3, + xreg4: 4, + xreg5: 5, + xreg6: 6, + xreg7: 7 + ], + + nicira_ext_match: [ + nsh_flags: 1, + nsh_mdtype: 2, + nsh_np: 3, + nsh_spi: 4, + nsh_si: 5, + nsh_c1: 6, + nsh_c2: 7, + nsh_c3: 8, + nsh_c4: 9 + ], + + onf_ext_match: [ + onf_tcp_flags: 42, + onf_actset_output: 43, + onf_pbb_uca: 2560, + ], + + packet_in_reason: [ + no_match: 0, + action: 1, + invalid_ttl: 2 + ], + + buffer_id: [ + no_buffer: 0xffffffff + ], + + port_config: [ + port_down: 1 <<< 0, + no_receive: 1 <<< 2, + no_forward: 1 <<< 5, + no_packet_in: 1 <<< 6 + ], + + port_state: [ + link_down: 1 <<< 0, + blocked: 1 <<< 1, + live: 1 <<< 2 + ], + + port_features: [ + {:"10mb_hd", 1 <<< 0}, + {:"10mb_fd", 1 <<< 1}, + {:"100mb_hd", 1 <<< 2}, + {:"100mb_fd", 1 <<< 3}, + {:"1gb_hd", 1 <<< 4}, + {:"1gb_fd", 1 <<< 5}, + {:"10gb_fd", 1 <<< 6}, + {:"40gb_fd", 1 <<< 7}, + {:"100gb_fd", 1 <<< 8}, + {:"1tb_fd", 1 <<< 9}, + {:other, 1 <<< 10}, + {:copper, 1 <<< 11}, + {:fiber, 1 <<< 12}, + {:autoneg, 1 <<< 13}, + {:pause, 1 <<< 14}, + {:pause_asym, 1 <<< 15} + ], + + openflow10_port_no: [ + max: 0xff00, + in_port: 0xfff8, + table: 0xfff9, + normal: 0xfffa, + flood: 0xfffb, + all: 0xfffc, + controller: 0xfffd, + local: 0xfffe, + none: 0xffff + ], + + openflow13_port_no: [ + max: 0xffffff00, + in_port: 0xfffffff8, + table: 0xfffffff9, + normal: 0xfffffffa, + flood: 0xfffffffb, + all: 0xfffffffc, + controller: 0xfffffffd, + local: 0xfffffffe, + any: 0xffffffff + ], + + packet_in_reason: [ + no_match: 0, + action: 1, + invalid_ttl: 2, + action_set: 3, + group: 4, + packet_out: 5 + ], + + flow_mod_command: [ + add: 0, + modify: 1, + modify_strict: 2, + delete: 3, + delete_strict: 4 + ], + + flow_mod_flags: [ + send_flow_rem: 1 <<< 0, + check_overlap: 1 <<< 1, + reset_counts: 1 <<< 2, + no_packet_counts: 1 <<< 3, + no_byte_counts: 1 <<< 4 + ], + + flow_removed_reason: [ + idle_timeout: 0, + hard_timeout: 1, + delete: 2, + group_delete: 3, + meter_delete: 4, + eviction: 5 + ], + + port_reason: [ + add: 0, + delete: 1, + modify: 2 + ], + + group_mod_command: [ + add: 0, + modify: 1, + delete: 2 + ], + + group_type: [ + all: 0, + select: 1, + indirect: 2, + fast_failover: 3 + ], + + group_id: [ + max: 0xffffff00, + all: 0xfffffffc, + any: 0xffffffff + ], + + group_capabilities: [ + select_weight: 1 <<< 0, + select_liveness: 1 <<< 1, + chaining: 1 <<< 2, + chaining_checks: 1 <<< 3 + ], + + table_id: [ + max: 0xfe, + all: 0xff + ], + + queue_id: [ + all: 0xffffffff + ], + + meter_mod_command: [ + add: 0, + modify: 1, + delete: 2 + ], + + meter_id: [ + max: 0xffff0000, + slowpath: 0xfffffffd, + controller: 0xfffffffe, + all: 0xffffffff + ], + + meter_flags: [ + kbps: 1 <<< 0, + pktps: 1 <<< 1, + burst: 1 <<< 2, + stats: 1 <<< 3 + ], + + meter_band_type: [ + {Openflow.MeterBand.Drop, 1}, + {Openflow.MeterBand.Remark, 2}, + {Openflow.MeterBand.Experimenter, 0xffff}, + ], + + table_config: [ + table_miss_controller: 0 <<< 0, + table_miss_continue: 1 <<< 0, + table_miss_drop: 2 <<< 0, + table_miss_mask: 3 <<< 0, + eviction: 1 <<< 2, + vacancy_events: 1 <<< 3 + ], + + action_type: [ + {Openflow.Action.Output, 0}, + {Openflow.Action.CopyTtlOut, 11}, + {Openflow.Action.CopyTtlIn, 12}, + {Openflow.Action.SetMplsTtl, 15}, + {Openflow.Action.DecMplsTtl, 16}, + {Openflow.Action.PushVlan, 17}, + {Openflow.Action.PopVlan, 18}, + {Openflow.Action.PushMpls, 19}, + {Openflow.Action.PopMpls, 20}, + {Openflow.Action.SetQueue, 21}, + {Openflow.Action.Group, 22}, + {Openflow.Action.SetNwTtl, 23}, + {Openflow.Action.DecNwTtl, 24}, + {Openflow.Action.SetField, 25}, + {Openflow.Action.PushPbb, 26}, + {Openflow.Action.PopPbb, 27}, + {Openflow.Action.Encap, 28}, + {Openflow.Action.Decap, 29}, + {Openflow.Action.SetSequence, 30}, + {Openflow.Action.ValidateSequence, 31}, + {Openflow.Action.Experimenter, 0xffff} + ], + + action_vendor: [ + nicira_ext_action: 0x00002320, + onf_ext_action: 0x4f4e4600 + ], + + onf_ext_action: [ + {Openflow.Action.OnfCopyField, 3200} + ], + + nicira_ext_action: [ + {Openflow.Action.NxResubmit, 1}, + {Openflow.Action.NxSetTunnel, 2}, + {Openflow.Action.NxSetQueue, 4}, + {Openflow.Action.NxPopQueue, 5}, + {Openflow.Action.NxRegMove, 6}, + {Openflow.Action.NxRegLoad, 7}, + {Openflow.Action.NxNote, 8}, + {Openflow.Action.NxSetTunnel64, 9}, + {Openflow.Action.NxMultipath, 10}, + {Openflow.Action.NxBundle, 12}, + {Openflow.Action.NxBundleLoad, 13}, + {Openflow.Action.NxResubmitTable, 14}, + {Openflow.Action.NxOutputReg, 15}, + {Openflow.Action.NxLearn, 16}, + {Openflow.Action.NxExit, 17}, + {Openflow.Action.NxDecTtl, 18}, + {Openflow.Action.NxFinTimeout, 19}, + {Openflow.Action.NxController, 20}, + {Openflow.Action.NxDecTtlCntIds, 21}, + {Openflow.Action.NxWriteMetadata, 22}, + {Openflow.Action.NxPushMpls, 23}, + {Openflow.Action.NxPopMpls, 24}, + {Openflow.Action.NxSetMplsTtl, 25}, + {Openflow.Action.NxDecMplsTtl, 26}, + {Openflow.Action.NxStackPush, 27}, + {Openflow.Action.NxStackPop, 28}, + {Openflow.Action.NxSample, 29}, + {Openflow.Action.NxSetMplsLabel, 30}, + {Openflow.Action.NxSetMplsTc, 31}, + {Openflow.Action.NxOutputReg2, 32}, + {Openflow.Action.NxRegLoad2, 33}, + {Openflow.Action.NxConjunction, 34}, + {Openflow.Action.NxConntrack, 35}, + {Openflow.Action.NxNat, 36}, + {Openflow.Action.NxController2, 37}, + {Openflow.Action.NxSample2, 38}, + {Openflow.Action.NxOutputTrunc, 39}, + {Openflow.Action.NxGroup, 40}, + {Openflow.Action.NxSample3, 41}, + {Openflow.Action.NxClone, 42}, + {Openflow.Action.NxCtClear, 43}, + {Openflow.Action.NxResubmitTableCt, 44}, + {Openflow.Action.NxLearn2, 45}, + {Openflow.Action.NxEncap, 46}, + {Openflow.Action.NxDecap, 47}, + {Openflow.Action.NxDebugRecirc, 0xff}, + ], + + nx_mp_algorithm: [ + modulo_n: 0, + hash_threshold: 1, + highest_random_weight: 2, + iterative_hash: 3 + ], + + nx_hash_fields: [ + eth_src: 0, + symmetric_l4: 1, + symmetric_l3l4: 2, + symmetric_l3l4_udp: 3, + nw_src: 4, + nw_dst: 5, + ], + + nx_bd_algorithm: [ + active_backup: 0, + highest_random_weight: 1 + ], + + nx_learn_flag: [ + send_flow_rem: 1 <<< 0, + delete_learned: 1 <<< 1, + write_result: 1 <<< 2 + ], + + nx_conntrack_flags: [ + commit: 1 <<< 0, + force: 1 <<< 1 + ], + + nx_nat_flags: [ + src: 1 <<< 0, + dst: 1 <<< 1, + persistent: 1 <<< 2, + protocol_hash: 1 <<< 3, + protocol_random: 1 <<< 4 + ], + + nx_nat_range: [ + ipv4_min: 1 <<< 0, + ipv4_max: 1 <<< 1, + ipv6_min: 1 <<< 2, + ipv6_max: 1 <<< 3, + proto_min: 1 <<< 4, + proto_max: 1 <<< 5 + ], + + nx_action_controller2_prop_type: [ + max_len: 0, + controller_id: 1, + reason: 2, + userdata: 3, + pause: 4 + ], + + nx_action_sample_direction: [ + default: 0, + ingress: 1, + egress: 2 + ], + + nx_flow_spec_type: [ + {Openflow.Action.NxFlowSpecMatch, 0}, + {Openflow.Action.NxFlowSpecLoad, 1}, + {Openflow.Action.NxFlowSpecOutput, 2} + ], + + instruction_type: [ + {Openflow.Instruction.GotoTable, 1}, + {Openflow.Instruction.WriteMetadata, 2}, + {Openflow.Instruction.WriteActions, 3}, + {Openflow.Instruction.ApplyActions, 4}, + {Openflow.Instruction.ClearActions, 5}, + {Openflow.Instruction.Meter, 6}, + {Openflow.Instruction.Experimenter, 0xffff} + ], + + controller_role: [ + nochange: 0, + equal: 1, + master: 2, + slave: 3 + ], + + nx_role: [ + other: 0, + master: 1, + slave: 2 + ], + + packet_in_format: [ + standard: 0, + nxt_packet_in: 1, + nxt_packet_in2: 2 + ], + + flow_format: [ + openflow10: 0, + nxm: 1 + ], + + packet_in2_prop_type: [ + packet: 0, + full_len: 1, + buffer_id: 2, + table_id: 3, + cookie: 4, + reason: 5, + metadata: 6, + userdata: 7, + continuation: 8 + ], + + continuation_prop_type: [ + bridge: 0x8000, + stack: 0x8001, + mirrors: 0x8002, + conntracked: 0x8003, + table_id: 0x8004, + cookie: 0x8005, + actions: 0x8006, + action_set: 0x8007 + ], + + flow_monitor_flag: [ + initial: 1 <<< 0, + add: 1 <<< 1, + delete: 1 <<< 2, + modify: 1 <<< 3, + actions: 1 <<< 4, + own: 1 <<< 5 + ], + + flow_update_event: [ + added: 0, + deleted: 1, + modified: 2, + abbrev: 3 + ], + + tlv_table_mod_command: [ + add: 0, + delete: 1, + clear: 2 + ] + ] + + for {enum_name, enum_def} <- @enums do + enum_name = to_string(enum_name) + to_int_fn_name = String.to_atom(enum_name <> "_to_int") + to_atom_fn_name = String.to_atom(enum_name <> "_to_atom") + + for {key, value} <- enum_def do + def to_int(unquote(key), unquote(String.to_atom(enum_name))) do + try do + unquote(to_int_fn_name)(unquote(key)) + catch + _ -> unquote(key) + end + end + + def to_atom(unquote(value), unquote(String.to_atom(enum_name))) do + try do + unquote(to_atom_fn_name)(unquote(value)) + catch + _ -> unquote(value) + end + end + end + def to_int(_int, unquote(String.to_atom(enum_name))) do + throw(:bad_enum) + end + + def to_atom(_, unquote(String.to_atom(enum_name))) do + throw(:bad_enum) + end + + for {key, value} <- enum_def do + def unquote(to_int_fn_name)(unquote(key)), do: unquote(value) + def unquote(to_atom_fn_name)(unquote(value)), do: unquote(key) + end + def unquote(to_int_fn_name)(_), do: throw(:bad_enum) + def unquote(to_atom_fn_name)(_), do: throw(:bad_enum) + + def int_to_flags(int, unquote(String.to_atom(enum_name))) do + Openflow.Utils.int_to_flags([], int, enum_of(unquote(String.to_atom(enum_name)))) + end + + def flags_to_int(flags, unquote(String.to_atom(enum_name))) do + Openflow.Utils.flags_to_int(0, flags, enum_of(unquote(String.to_atom(enum_name)))) + end + + defp enum_of(unquote(String.to_atom(enum_name))), do: unquote(enum_def) + end +end diff --git a/lib/openflow/error_msg.ex b/lib/openflow/error_msg.ex new file mode 100644 index 0000000..4a6f354 --- /dev/null +++ b/lib/openflow/error_msg.ex @@ -0,0 +1,53 @@ +defmodule Openflow.ErrorMsg do + @moduledoc "OpenFlow Error codec module" + + defstruct( + version: 4, + xid: 0, + type: nil, + code: nil, + data: "", + exp_type: nil, + experimenter: nil, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 1 + + def read(<<0xffff::16, exp_type::16, experimenter::32, data::bytes>>) do + error_type = Openflow.Enums.to_atom(0xffff, :error_type) + %ErrorMsg{ + type: error_type, + exp_type: exp_type, + experimenter: experimenter, + data: data + } + end + def read(<>) do + error_type = Openflow.Enums.to_atom(type, :error_type) + error_code = Openflow.Enums.to_atom(code, error_type) + %ErrorMsg{ + type: error_type, + code: error_code, + data: data + } + end + + def to_binary(%ErrorMsg{type: :experimenter, + exp_type: exp_type, + experimenter: experimenter, + data: data}) do + error_type = Openflow.Enums.to_int(:experimenter, :error_type) + <> + end + def to_binary(%ErrorMsg{type: type, + code: code, + data: data}) do + error_type = Openflow.Enums.to_int(type, :error_type) + error_code = Openflow.Enums.to_int(code, type) + <> + end +end diff --git a/lib/openflow/experimenter.ex b/lib/openflow/experimenter.ex new file mode 100644 index 0000000..bfead7a --- /dev/null +++ b/lib/openflow/experimenter.ex @@ -0,0 +1,39 @@ +defmodule Openflow.Experimenter do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + exp_id: 0, + exp_type: 0, + data: "" + ) + + alias __MODULE__ + + def ofp_type, do: 4 + + def new(options) do + %Experimenter{exp_id: Keyword.get(options, :exp_id, 0), + exp_type: Keyword.get(options, :exp_type, 0), + data: Keyword.get(options, :data, "")} + end + + def to_binary(%Experimenter{exp_id: exp_id, exp_type: exp_type, data: data}) do + <> + end + + def read(<>) do + case Openflow.Utils.get_enum(exp_id, :experimenter_id) do + ^exp_id -> + %Experimenter{exp_id: exp_id, exp_type: exp_type, data: data} + experimenter when is_atom(experimenter) -> + case Openflow.Utils.get_enum(exp_type, experimenter) do + ^exp_type -> + %Experimenter{exp_id: exp_id, exp_type: exp_type, data: data} + codec when is_atom(codec) -> + codec.read(<>) + end + end + end +end diff --git a/lib/openflow/features.ex b/lib/openflow/features.ex new file mode 100644 index 0000000..43b26b8 --- /dev/null +++ b/lib/openflow/features.ex @@ -0,0 +1,2 @@ +defmodule Openflow.Features do +end diff --git a/lib/openflow/features/reply.ex b/lib/openflow/features/reply.ex new file mode 100644 index 0000000..1bac1a5 --- /dev/null +++ b/lib/openflow/features/reply.ex @@ -0,0 +1,39 @@ +defmodule Openflow.Features.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + n_buffers: 0, + n_tables: 0, + aux_id: 0, + capabilities: [], + ) + + alias __MODULE__ + + def ofp_type, do: 6 + + def read(<>) do + dpid = Openflow.Utils.to_hex_string(datapath_id) + flags = Openflow.Enums.int_to_flags(caps_int, :switch_capabilities) + %Reply{ + datapath_id: dpid, + n_buffers: n_buf, + n_tables: n_tab, + aux_id: aux_id, + capabilities: flags + } + end + + def to_binary(%Reply{ + datapath_id: datapath_id, + n_buffers: n_buf, + n_tables: n_tab, + aux_id: aux_id, + capabilities: flags}) do + dpid_int = String.to_integer(datapath_id, 16) + flags_int = Openflow.Enums.flags_to_int(flags, :switch_capabilities) + <> + end +end diff --git a/lib/openflow/features/request.ex b/lib/openflow/features/request.ex new file mode 100644 index 0000000..d23aee1 --- /dev/null +++ b/lib/openflow/features/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Features.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 5 + + def new do + %Request{} + end + + def read(_) do + %Request{} + end + + def to_binary(%Request{}) do + <<>> + end +end diff --git a/lib/openflow/flow_mod.ex b/lib/openflow/flow_mod.ex new file mode 100644 index 0000000..338b0c7 --- /dev/null +++ b/lib/openflow/flow_mod.ex @@ -0,0 +1,109 @@ +defmodule Openflow.FlowMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + cookie: 0, + cookie_mask: 0, + table_id: 0, + command: :add, + idle_timeout: 0, + hard_timeout: 0, + priority: 0, + buffer_id: :no_buffer, + out_port: :any, + out_group: :any, + flags: [], + match: [], + instructions: [] + ) + + alias __MODULE__ + + def ofp_type, do: 14 + + def new(options \\ []) do + cookie = Keyword.get(options, :cookie, 0) + cookie_mask = Keyword.get(options, :cookie_mask, 0) + table_id = Keyword.get(options, :table_id, 0) + command = Keyword.get(options, :command, :add) + idle = Keyword.get(options, :idle_timeout, 0) + hard = Keyword.get(options, :hard_timeout, 0) + priority = Keyword.get(options, :priority, 0) + buffer_id = Keyword.get(options, :buffer_id, :no_buffer) + out_port = Keyword.get(options, :out_port, :any) + out_group = Keyword.get(options, :out_group, :any) + flags = Keyword.get(options, :flags, []) + match = Keyword.get(options, :match, Openflow.Match.new) + instructions = Keyword.get(options, :instructions, []) + %FlowMod{cookie: cookie, + cookie_mask: cookie_mask, + priority: priority, + table_id: table_id, + command: command, + idle_timeout: idle, + hard_timeout: hard, + buffer_id: buffer_id, + out_port: out_port, + out_group: out_group, + flags: flags, + match: match, + instructions: instructions} + end + + def read(<>) do + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + command = Openflow.Utils.get_enum(command_int, :flow_mod_command) + buffer_id = Openflow.Utils.get_enum(buffer_id_int, :buffer_id) + out_port = Openflow.Utils.get_enum(out_port_int, :openflow13_port_no) + out_group = Openflow.Utils.get_enum(out_group_int, :group_id) + flags = Openflow.Enums.int_to_flags(flags_int, :flow_mod_flags) + {match_fields, instructions_bin} = Openflow.Match.read(rest) + match = Openflow.Match.new(match_fields) + instructions = Openflow.Instruction.read(instructions_bin) + %FlowMod{cookie: cookie, + cookie_mask: cookie_mask, + priority: prio, + table_id: table_id, + command: command, + idle_timeout: idle, + hard_timeout: hard, + buffer_id: buffer_id, + out_port: out_port, + out_group: out_group, + flags: flags, + match: match, + instructions: instructions} + end + + def to_binary(flow_mod) do + %FlowMod{cookie: cookie, + cookie_mask: cookie_mask, + priority: prio, + table_id: table_id, + command: command, + idle_timeout: idle, + hard_timeout: hard, + buffer_id: buffer_id, + out_port: out_port, + out_group: out_group, + flags: flags, + match: match_fields, + instructions: instructions} = flow_mod + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + command_int = Openflow.Utils.get_enum(command, :flow_mod_command) + buffer_id_int = Openflow.Utils.get_enum(buffer_id, :buffer_id) + out_port_int = Openflow.Utils.get_enum(out_port, :openflow13_port_no) + out_group_int = Openflow.Utils.get_enum(out_group, :group_id) + flags_int = Openflow.Enums.flags_to_int(flags, :flow_mod_flags) + match_fields_bin = Openflow.Match.to_binary(match_fields) + instructions_bin = Openflow.Instruction.to_binary(instructions) + <> + end +end diff --git a/lib/openflow/flow_removed.ex b/lib/openflow/flow_removed.ex new file mode 100644 index 0000000..3b3fe68 --- /dev/null +++ b/lib/openflow/flow_removed.ex @@ -0,0 +1,42 @@ +defmodule Openflow.FlowRemoved do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + cookie: 0, + priority: 0, + reason: :idle_timeout, + table_id: 0, + duration_sec: 0, + duration_nsec: 0, + idle_timeout: 0, + hard_timeout: 0, + packet_count: 0, + byte_count: 0, + match: [] + ) + + alias __MODULE__ + + def ofp_type, do: 11 + + def read(<>) do + reason = Openflow.Enums.to_atom(reason_int, :flow_removed_reason) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + {match_fields, _rest} = Openflow.Match.read(rest) + %FlowRemoved{cookie: cookie, + priority: priority, + reason: reason, + table_id: table_id, + duration_sec: dsec, + duration_nsec: dnsec, + idle_timeout: idle, + hard_timeout: hard, + packet_count: pkt, + byte_count: byt, + match: match_fields} + end +end diff --git a/lib/openflow/get_async/reply.ex b/lib/openflow/get_async/reply.ex new file mode 100644 index 0000000..58b8f45 --- /dev/null +++ b/lib/openflow/get_async/reply.ex @@ -0,0 +1,40 @@ +defmodule Openflow.GetAsync.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0, # virtual field + packet_in_mask_master: 0, + packet_in_mask_slave: 0, + port_status_mask_master: 0, + port_status_mask_slave: 0, + flow_removed_mask_master: 0, + flow_removed_mask_slave: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 27 + + def read(<>) do + %Reply{packet_in_mask_master: packet_in_mask_master, + packet_in_mask_slave: packet_in_mask_slave, + port_status_mask_master: port_status_mask_master, + port_status_mask_slave: port_status_mask_slave, + flow_removed_mask_master: flow_removed_mask_master, + flow_removed_mask_slave: flow_removed_mask_slave} + end + + def to_binary(%Reply{packet_in_mask_master: packet_in_mask_master, + packet_in_mask_slave: packet_in_mask_slave, + port_status_mask_master: port_status_mask_master, + port_status_mask_slave: port_status_mask_slave, + flow_removed_mask_master: flow_removed_mask_master, + flow_removed_mask_slave: flow_removed_mask_slave}) do + <> + end +end diff --git a/lib/openflow/get_async/request.ex b/lib/openflow/get_async/request.ex new file mode 100644 index 0000000..7433daf --- /dev/null +++ b/lib/openflow/get_async/request.ex @@ -0,0 +1,20 @@ +defmodule Openflow.GetAsync.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 26 + + def read(_) do + %Request{} + end + + def to_binary(%Request{}) do + <<>> + end +end diff --git a/lib/openflow/get_config.ex b/lib/openflow/get_config.ex new file mode 100644 index 0000000..ceb38e2 --- /dev/null +++ b/lib/openflow/get_config.ex @@ -0,0 +1,2 @@ +defmodule Openflow.GetConfig do +end diff --git a/lib/openflow/get_config/reply.ex b/lib/openflow/get_config/reply.ex new file mode 100644 index 0000000..db1870d --- /dev/null +++ b/lib/openflow/get_config/reply.ex @@ -0,0 +1,26 @@ +defmodule Openflow.GetConfig.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + flags: [], # default = "normal" is no special handling + miss_send_len: 128 + ) + + alias __MODULE__ + + def ofp_type, do: 8 + + def read(<>) do + flags = Openflow.Enums.int_to_flags(flags_int, :config_flags) + miss_send_len = Openflow.Utils.get_enum(miss_send_len0, :controller_max_len) + %Reply{flags: flags, miss_send_len: miss_send_len} + end + + def to_binary(%Reply{flags: flags, miss_send_len: miss_send_len0}) do + flags_int = Openflow.Enums.flags_to_int(flags, :config_flags) + miss_send_len = Openflow.Utils.get_enum(miss_send_len0, :controller_max_len) + <> + end +end diff --git a/lib/openflow/get_config/request.ex b/lib/openflow/get_config/request.ex new file mode 100644 index 0000000..c1ec4d9 --- /dev/null +++ b/lib/openflow/get_config/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.GetConfig.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 7 + + def new do + %Request{} + end + + def read(_) do + %Request{} + end + + def to_binary(%Request{}) do + <<>> + end +end diff --git a/lib/openflow/group_mod.ex b/lib/openflow/group_mod.ex new file mode 100644 index 0000000..276e9af --- /dev/null +++ b/lib/openflow/group_mod.ex @@ -0,0 +1,49 @@ +defmodule Openflow.GroupMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + command: :add, + type: :all, + group_id: 0, + buckets: [] + ) + + alias __MODULE__ + + def ofp_type, do: 15 + + def new(options \\ []) do + command = Keyword.get(options, :command, :add) + type = Keyword.get(options, :type, :all) + group_id = Keyword.get(options, :group_id, 0) + buckets = Keyword.get(options, :buckets, []) + %GroupMod{command: command, + type: type, + group_id: group_id, + buckets: buckets} + end + + def read(<>) do + command = Openflow.Utils.get_enum(command_int, :group_mod_command) + type = Openflow.Utils.get_enum(type_int, :group_type) + group_id = Openflow.Utils.get_enum(group_id_int, :group_id) + buckets = Openflow.Bucket.read(buckets_bin) + %GroupMod{command: command, + type: type, + group_id: group_id, + buckets: buckets} + end + + def to_binary(%GroupMod{command: command, + type: type, + group_id: group_id, + buckets: buckets}) do + command_int = Openflow.Utils.get_enum(command, :group_mod_command) + type_int = Openflow.Utils.get_enum(type, :group_type) + group_id_int = Openflow.Utils.get_enum(group_id, :group_id) + buckets_bin = Openflow.Bucket.to_binary(buckets) + <> + end +end diff --git a/lib/openflow/hello.ex b/lib/openflow/hello.ex new file mode 100644 index 0000000..a3f4a75 --- /dev/null +++ b/lib/openflow/hello.ex @@ -0,0 +1,103 @@ +defmodule Openflow.Hello do + @moduledoc "OpenFlow Hello codec module" + + import Bitwise + + @ofp_hello_size 4 + + defstruct(version: 4, xid: 0, elements: []) + + alias __MODULE__ + + def ofp_type, + do: 0 + + def new(version) when is_integer(version) do + %Hello{elements: [versionbitmap: [version]]} + end + def new(versions) when is_list(versions) do + %Hello{elements: [versionbitmap: versions]} + end + + def supported_version?(%Hello{version: 4, elements: []}), do: true + def supported_version?(%Hello{elements: []}), do: false + def supported_version?(%Hello{elements: elements}) do + versionbitmaps = for {:versionbitmap, versions} <- elements, do: versions + Enum.any?(versionbitmaps, fn(versions) -> 4 in versions end) + end + + def read(binary), + do: %Hello{elements: decode([], binary)} + + def to_binary(%Hello{elements: elements}), + do: encode([], elements) + + # private functions + + defp decode(acc, <<>>), do: acc + defp decode(acc, <>) do + data_len = length - @ofp_hello_size + <> = rest + + try do + typeint + |> Openflow.Enums.to_atom(:hello_elem) + |> decode_hello_elem(acc, data) + |> decode(rest2) + catch + :bad_enum -> + decode(acc, rest2) + end + end + + defp encode(acc, []), + do: to_string(acc) + defp encode(acc, [h|rest]), + do: encode([encode_hello_elem(h)|acc], rest) + + defp decode_hello_elem(:versionbitmap, acc, binary), + do: [{:versionbitmap, decode_bitmap([], binary, 0)}|acc] + defp decode_hello_elem(_, acc, _binary), + do: acc + + defp encode_hello_elem({:versionbitmap, versions}) do + bitmap_bin = encode_bitmap(versions) + type_int = Openflow.Enums.to_int(:versionbitmap, :hello_elem) + size_int = @ofp_hello_size + byte_size(bitmap_bin) + <> + end + defp encode_hello_elem(_) do + <<>> + end + + defp decode_bitmap(acc, "", _), do: acc + defp decode_bitmap(acc, <>, base) do + acc + |> decode_bitmap(int, 0, base) + |> decode_bitmap(rest, base + 32) + end + + defp encode_bitmap(list) do + size = + list + |> Enum.max + |> div(32) + encode_bitmap(0, list, size) + end + + defp decode_bitmap(acc, _, index, _) when index >= 32, + do: acc + defp decode_bitmap(acc, int, index, base) when (int &&& (1 <<< index)) == 0, + do: decode_bitmap(acc, int, index + 1, base) + defp decode_bitmap(acc, int, index, base), + do: decode_bitmap([base + index|acc], int, index + 1, base) + + defp encode_bitmap(acc, [], size) do + bytes = (size + 1) * 32 + <> + end + defp encode_bitmap(acc, [h|rest], size) do + index = (size - div(h, 32) * 32 + rem(h, 32)) + encode_bitmap(acc ||| (1 <<< index), rest, size) + end +end diff --git a/lib/openflow/instruction.ex b/lib/openflow/instruction.ex new file mode 100644 index 0000000..c33d631 --- /dev/null +++ b/lib/openflow/instruction.ex @@ -0,0 +1,28 @@ +defmodule Openflow.Instruction do + + def read(instruction_bin) do + do_read([], instruction_bin) + end + + def to_binary(instructions) when is_list(instructions) do + to_binary(<<>>, instructions) + end + def to_binary(instruction) do + to_binary([instruction]) + end + + # private functions + + defp do_read(acc, <<>>), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + codec = Openflow.Enums.to_atom(type, :instruction_type) + do_read([codec.read(instruction_bin)|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [instruction|rest]) do + codec = instruction.__struct__ + to_binary(<>, rest) + end +end diff --git a/lib/openflow/instructions/apply_actions.ex b/lib/openflow/instructions/apply_actions.ex new file mode 100644 index 0000000..b331419 --- /dev/null +++ b/lib/openflow/instructions/apply_actions.ex @@ -0,0 +1,20 @@ +defmodule Openflow.Instruction.ApplyActions do + defstruct(actions: []) + + alias __MODULE__ + + def new(actions) do + %ApplyActions{actions: actions} + end + + def to_binary(%ApplyActions{actions: actions}) do + actions_bin = Openflow.Action.to_binary(actions) + length = 8 + byte_size(actions_bin) + <<4::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>> + end + + def read(<<4::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do + actions = Openflow.Action.read(actions_bin) + %ApplyActions{actions: actions} + end +end diff --git a/lib/openflow/instructions/clear_actions.ex b/lib/openflow/instructions/clear_actions.ex new file mode 100644 index 0000000..6d52c08 --- /dev/null +++ b/lib/openflow/instructions/clear_actions.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Instruction.ClearActions do + defstruct([]) + + alias __MODULE__ + + def new do + %ClearActions{} + end + + def to_binary(%ClearActions{}) do + actions_bin = "" + length = 8 + byte_size(actions_bin) + <<5::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>> + end + + def read(<<5::16, _length::16, _::size(4)-unit(8), _::bytes>>) do + %ClearActions{} + end +end diff --git a/lib/openflow/instructions/experimenter.ex b/lib/openflow/instructions/experimenter.ex new file mode 100644 index 0000000..138b10b --- /dev/null +++ b/lib/openflow/instructions/experimenter.ex @@ -0,0 +1,18 @@ +defmodule Openflow.Instruction.Experimenter do + defstruct(exp_id: 0, data: "") + + alias __MODULE__ + + def new(exp_id, data) do + %Experimenter{exp_id: exp_id, data: data} + end + + def to_binary(%Experimenter{exp_id: exp_id, data: data}) do + length = 8 + byte_size(data) + <<0xffff::16, length::16, exp_id::32, data::bytes>> + end + + def read(<<0xffff::16, _::16, exp_id::32, data::bytes>>) do + %Experimenter{exp_id: exp_id, data: data} + end +end diff --git a/lib/openflow/instructions/goto_table.ex b/lib/openflow/instructions/goto_table.ex new file mode 100644 index 0000000..f78c2c9 --- /dev/null +++ b/lib/openflow/instructions/goto_table.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Instruction.GotoTable do + defstruct(table_id: 0) + + alias __MODULE__ + + def new(table_id) do + %GotoTable{table_id: table_id} + end + + def to_binary(%GotoTable{table_id: table_id}) do + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + <<1::16, 8::16, table_id_int::8, 0::size(3)-unit(8)>> + end + + def read(<<1::16, 8::16, table_id_int::8, _::size(3)-unit(8)>>) do + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + %GotoTable{table_id: table_id} + end +end diff --git a/lib/openflow/instructions/meter.ex b/lib/openflow/instructions/meter.ex new file mode 100644 index 0000000..f038fc9 --- /dev/null +++ b/lib/openflow/instructions/meter.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Instruction.Meter do + defstruct(meter_id: 0) + + alias __MODULE__ + + def new(meter_id) do + %Meter{meter_id: meter_id} + end + + def to_binary(%Meter{meter_id: meter_id}) do + meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) + <<6::16, 8::16, meter_id_int::32>> + end + + def read(<<6::16, _::16, meter_id_int::32>>) do + meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) + %Meter{meter_id: meter_id} + end +end diff --git a/lib/openflow/instructions/write_actions.ex b/lib/openflow/instructions/write_actions.ex new file mode 100644 index 0000000..fb9eeea --- /dev/null +++ b/lib/openflow/instructions/write_actions.ex @@ -0,0 +1,20 @@ +defmodule Openflow.Instruction.WriteActions do + defstruct(actions: []) + + alias __MODULE__ + + def new(actions) do + %WriteActions{actions: actions} + end + + def to_binary(%WriteActions{actions: actions}) do + actions_bin = Openflow.Action.to_binary(actions) + length = 8 + byte_size(actions_bin) + <<3::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>> + end + + def read(<<3::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do + actions = Openflow.Action.read(actions_bin) + %WriteActions{actions: actions} + end +end diff --git a/lib/openflow/instructions/write_metadata.ex b/lib/openflow/instructions/write_metadata.ex new file mode 100644 index 0000000..034e8d7 --- /dev/null +++ b/lib/openflow/instructions/write_metadata.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Instruction.WriteMetadata do + defstruct(metadata: 0, metadata_mask: 0xffffffffffffffff) + + alias __MODULE__ + + def new(options) do + metadata = Keyword.get(options, :metadata, 0) + metadata_mask = Keyword.get(options, :metadata_mask, 0xffffffffffffffff) + %WriteMetadata{metadata: metadata, metadata_mask: metadata_mask} + end + + def to_binary(%WriteMetadata{metadata: metadata, metadata_mask: metadata_mask}) do + <<2::16, 24::16, 0::size(4)-unit(8), metadata::64, metadata_mask::64>> + end + + def read(<<2::16, 24::16, _::size(4)-unit(8), metadata::64, metadata_mask::64>>) do + %WriteMetadata{metadata: metadata, metadata_mask: metadata_mask} + end +end diff --git a/lib/openflow/match.ex b/lib/openflow/match.ex new file mode 100644 index 0000000..baac231 --- /dev/null +++ b/lib/openflow/match.ex @@ -0,0 +1,154 @@ +defmodule Openflow.Match do + @match_size 8 + @header_size 4 + + defstruct([ + type: :oxm, + fields: [] + ]) + + alias __MODULE__ + + def new(fields \\ []) do + oxm_fields = + fields + |> keyword_to_oxm_fields([]) + %Match{type: :oxm, fields: oxm_fields} + end + + def read(binary) do + <<1::16, no_pad_len::16, binary1::binary>> = binary + padding_length = @match_size - rem(no_pad_len, 8) + match_field_len = no_pad_len - @header_size + <> = binary1 + {decode_fields(match_fields, []), rest} + end + + def to_binary(%Match{fields: fields}) do + fields_bin = encode_fields(fields, <<>>) + length = byte_size(fields_bin) + @match_size - @header_size + type_int = Openflow.Enums.to_int(:oxm, :match_type) + padding = Openflow.Utils.padding(length, 8) + <> + end + + def codec_header(oxm_field) when is_atom(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_int = Openflow.Enums.to_int(oxm_class, :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) + <> + experimenter when experimenter in [:nicira_ext_match, :onf_ext_match] -> + oxm_class_int = 0xffff + experimenter_int = Openflow.Enums.to_int(experimenter, :experimenter_oxm_vendors) + oxm_field_int = Openflow.Enums.to_int(oxm_field, experimenter) + oxm_length = div(Openflow.Match.Field.n_bits_of(oxm_field) + 4, 8) + <> + end + end + def codec_header(<>) do + oxm_class = Openflow.Enums.to_atom(oxm_class_int, :oxm_class) + Openflow.Enums.to_atom(oxm_field_int, oxm_class) + end + 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) + Openflow.Enums.to_atom(oxm_field_int, experimenter) + end + + def header_size(<<_oxm_class_int::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _::bytes>>), + do: 4 + def header_size(<<0xffff::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _exp_int::32, _::bytes>>), + do: 8 + + # private functions + + defp decode_fields(<<>>, acc), do: Enum.reverse(acc) + defp decode_fields(<<0xffff::16, _::7, 1::1, length::8, vendor_int::32, field_int::16, binary::bytes>>, acc) do + length = length - 6 + field_len = div(length, 2) + <> = binary + experimenter = Openflow.Enums.to_atom(vendor_int, :experimenter_oxm_vendors) + field_name = Openflow.Enums.to_atom(field_int, experimenter) + value = Openflow.Match.Field.codec(value_bin, field_name) + mask = Openflow.Match.Field.codec(mask_bin, field_name) + decode_fields(rest, [{field_name, {value, mask}}|acc]) + end + defp decode_fields(<<0xffff::16, _::7, 0::1, length::8, vendor_int::32, field_int::16, binary::bytes>>, acc) do + length = length - 6 + <> = binary + experimenter = Openflow.Enums.to_atom(vendor_int, :experimenter_oxm_vendors) + field_name = Openflow.Enums.to_atom(field_int, experimenter) + value = Openflow.Match.Field.codec(value_bin, field_name) + decode_fields(rest, [{field_name, value}|acc]) + end + defp decode_fields(<>, acc) do + field_len = div(length, 2) + <> = binary + class = Openflow.Enums.to_atom(class_int, :oxm_class) + field_name = Openflow.Enums.to_atom(field_int, class) + value = Openflow.Match.Field.codec(value_bin, field_name) + mask = Openflow.Match.Field.codec(mask_bin, field_name) + decode_fields(rest, [{field_name, {value, mask}}|acc]) + end + defp decode_fields(<>, acc) do + <> = binary + class = Openflow.Enums.to_atom(class_int, :oxm_class) + field_name = Openflow.Enums.to_atom(field_int, class) + value = Openflow.Match.Field.codec(value_bin, field_name) + decode_fields(rest, [{field_name, value}|acc]) + end + + defp encode_fields([], acc), do: acc + defp encode_fields([field|fields], acc) do + encode_fields(fields, <>) + end + + defp encode_field(%{class: class, field: field, has_mask: true, value: value, mask: mask}) + when class == :nicira_ext_match or class == :onf_ext_match do + vendor_int = Openflow.Enums.to_int(class, :experimenter_oxm_vendors) + field_int = Openflow.Enums.to_int(field, class) + has_mask_int = 1 + length = (byte_size(value) * 2) + 6 + <<0xffff::16, 0::7, has_mask_int::1, length::8, vendor_int::32, field_int::16, value::bytes, mask::bytes>> + end + defp encode_field(%{class: class, field: field, has_mask: false, value: value}) + when class == :nicira_ext_match or class == :onf_ext_match do + vendor_int = Openflow.Enums.to_int(class, :experimenter_oxm_vendors) + field_int = Openflow.Enums.to_int(field, class) + has_mask_int = 0 + length = byte_size(value) + 6 + <<0xffff::16, 0::7, has_mask_int::1, length::8, vendor_int::32, field_int::16, value::bytes>> + end + defp encode_field(%{class: class, field: field, has_mask: true, value: value, mask: mask}) do + class_int = Openflow.Enums.to_int(class, :oxm_class) + field_int = Openflow.Enums.to_int(field, class) + has_mask_int = 1 + length = byte_size(value) * 2 + <> + end + defp encode_field(%{class: class, field: field, has_mask: false, value: value}) do + class_int = Openflow.Enums.to_int(class, :oxm_class) + field_int = Openflow.Enums.to_int(field, class) + has_mask_int = 0 + length = byte_size(value) + <> + end + + defp keyword_to_oxm_fields([], acc), do: Enum.reverse(acc) + defp keyword_to_oxm_fields([{field_name, field_value}|fields], acc) do + keyword_to_oxm_fields(fields, [oxm_field(field_name, field_value)|acc]) + end + + defp oxm_field(field_name, {value, mask}) do + value_bin = Openflow.Match.Field.codec(value, field_name) + mask_bin = Openflow.Match.Field.codec(mask, field_name) + match_class = Openflow.Match.Field.vendor_of(field_name) + %{class: match_class, field: field_name, has_mask: true, value: value_bin, mask: mask_bin} + end + defp oxm_field(field_name, value) do + value_bin = Openflow.Match.Field.codec(value, field_name) + match_class = Openflow.Match.Field.vendor_of(field_name) + %{class: match_class, field: field_name, has_mask: false, value: value_bin} + end +end diff --git a/lib/openflow/match/field.ex b/lib/openflow/match/field.ex new file mode 100644 index 0000000..4c45221 --- /dev/null +++ b/lib/openflow/match/field.ex @@ -0,0 +1,636 @@ +defmodule Openflow.Match.Field do + + def codec(value0, field) when is_binary(value0) do + {type, format} = format_of(field) + n_bits = n_bits_of(field) + bit_size = bit_size(value0) + value = if bit_size < n_bits do + head_pad_len = n_bits - bit_size + <<0::size(head_pad_len), value0::bytes>> + else + if bit_size > n_bits and type != :mac do + head_pad_len = bit_size - n_bits + <<_::size(head_pad_len), value::size(n_bits)-bits>> = value0 + value + else + value0 + end + end + formatting(value, type, format) + end + def codec(value, type) do + {type, format} = format_of(type) + formatting(value, type, format) + end + + def n_bits_of(field) do + field + |> format_of + |> bit_size_of + end + + def bit_size_of({:u8, _}), do: 8 + def bit_size_of({:u24, _}), do: 24 + def bit_size_of({:be16, _}), do: 16 + def bit_size_of({:be32, _}), do: 32 + def bit_size_of({:be64, _}), do: 64 + def bit_size_of({:be128, _}), do: 128 + def bit_size_of({:mac, _}), do: 48 + + # NXM0 + def vendor_of(:nx_in_port), do: :nxm_0 + def vendor_of(:nx_eth_dst), do: :nxm_0 + def vendor_of(:nx_eth_src), do: :nxm_0 + def vendor_of(:nx_eth_type), do: :nxm_0 + def vendor_of(:nx_vlan_tci), do: :nxm_0 + def vendor_of(:nx_ip_tos), do: :nxm_0 + def vendor_of(:nx_ip_proto), do: :nxm_0 + def vendor_of(:nx_ipv4_src), do: :nxm_0 + def vendor_of(:nx_ipv4_dst), do: :nxm_0 + def vendor_of(:nx_tcp_src), do: :nxm_0 + def vendor_of(:nx_tcp_dst), do: :nxm_0 + def vendor_of(:nx_udp_src), do: :nxm_0 + def vendor_of(:nx_udp_dst), do: :nxm_0 + def vendor_of(:nx_icmpv4_type), do: :nxm_0 + def vendor_of(:nx_icmpv4_code), do: :nxm_0 + def vendor_of(:nx_arp_op), do: :nxm_0 + def vendor_of(:nx_arp_spa), do: :nxm_0 + def vendor_of(:nx_arp_tpa), do: :nxm_0 + def vendor_of(:nx_tcp_flags), do: :nxm_0 + + # NXM1 + def vendor_of(:reg0), do: :nxm_1 + def vendor_of(:reg1), do: :nxm_1 + def vendor_of(:reg2), do: :nxm_1 + def vendor_of(:reg3), do: :nxm_1 + def vendor_of(:reg4), do: :nxm_1 + def vendor_of(:reg5), do: :nxm_1 + def vendor_of(:reg6), do: :nxm_1 + def vendor_of(:reg7), do: :nxm_1 + def vendor_of(:reg8), do: :nxm_1 + def vendor_of(:reg9), do: :nxm_1 + def vendor_of(:reg10), do: :nxm_1 + def vendor_of(:reg11), do: :nxm_1 + def vendor_of(:reg12), do: :nxm_1 + def vendor_of(:reg13), do: :nxm_1 + def vendor_of(:reg14), do: :nxm_1 + def vendor_of(:reg15), do: :nxm_1 + def vendor_of(:tun_id), do: :nxm_1 + def vendor_of(:nx_arp_sha), do: :nxm_1 + def vendor_of(:nx_arp_tha), do: :nxm_1 + def vendor_of(:nx_ipv6_src), do: :nxm_1 + def vendor_of(:nx_ipv6_dst), do: :nxm_1 + def vendor_of(:nx_icmpv6_type), do: :nxm_1 + def vendor_of(:nx_icmpv6_code), do: :nxm_1 + def vendor_of(:nx_ipv6_nd_target), do: :nxm_1 + def vendor_of(:nx_ipv6_nd_sll), do: :nxm_1 + def vendor_of(:nx_ipv6_nd_tll), do: :nxm_1 + def vendor_of(:nx_ip_frag), do: :nxm_1 + def vendor_of(:nx_ipv6_label), do: :nxm_1 + def vendor_of(:nx_ip_ecn), do: :nxm_1 + def vendor_of(:nx_ip_ttl), do: :nxm_1 + def vendor_of(:nx_mpls_ttl), do: :nxm_1 + def vendor_of(:tun_src), do: :nxm_1 + def vendor_of(:tun_dst), do: :nxm_1 + def vendor_of(:pkt_mark), do: :nxm_1 + def vendor_of(:dp_hash), do: :nxm_1 + def vendor_of(:recirc_id), do: :nxm_1 + def vendor_of(:conj_id), do: :nxm_1 + def vendor_of(:nx_tun_gbp_id), do: :nxm_1 + def vendor_of(:nx_tun_gbp_flags), do: :nxm_1 + def vendor_of(:tun_metadata0), do: :nxm_1 + def vendor_of(:tun_metadata1), do: :nxm_1 + def vendor_of(:tun_metadata2), do: :nxm_1 + def vendor_of(:tun_metadata3), do: :nxm_1 + def vendor_of(:tun_metadata4), do: :nxm_1 + def vendor_of(:tun_metadata5), do: :nxm_1 + def vendor_of(:tun_metadata6), do: :nxm_1 + def vendor_of(:tun_metadata7), do: :nxm_1 + def vendor_of(:tun_metadata8), do: :nxm_1 + def vendor_of(:tun_metadata9), do: :nxm_1 + def vendor_of(:tun_metadata10), do: :nxm_1 + def vendor_of(:tun_metadata11), do: :nxm_1 + def vendor_of(:tun_metadata12), do: :nxm_1 + def vendor_of(:tun_metadata13), do: :nxm_1 + def vendor_of(:tun_metadata14), do: :nxm_1 + def vendor_of(:tun_metadata15), do: :nxm_1 + def vendor_of(:tun_metadata16), do: :nxm_1 + def vendor_of(:tun_metadata17), do: :nxm_1 + def vendor_of(:tun_metadata18), do: :nxm_1 + def vendor_of(:tun_metadata19), do: :nxm_1 + def vendor_of(:tun_metadata20), do: :nxm_1 + def vendor_of(:tun_metadata21), do: :nxm_1 + def vendor_of(:tun_metadata22), do: :nxm_1 + def vendor_of(:tun_metadata23), do: :nxm_1 + def vendor_of(:tun_metadata24), do: :nxm_1 + def vendor_of(:tun_metadata25), do: :nxm_1 + def vendor_of(:tun_metadata26), do: :nxm_1 + def vendor_of(:tun_metadata27), do: :nxm_1 + def vendor_of(:tun_metadata28), do: :nxm_1 + def vendor_of(:tun_metadata29), do: :nxm_1 + def vendor_of(:tun_metadata30), do: :nxm_1 + def vendor_of(:tun_metadata31), do: :nxm_1 + def vendor_of(:tun_metadata32), do: :nxm_1 + def vendor_of(:tun_metadata33), do: :nxm_1 + def vendor_of(:tun_metadata34), do: :nxm_1 + def vendor_of(:tun_metadata35), do: :nxm_1 + def vendor_of(:tun_metadata36), do: :nxm_1 + def vendor_of(:tun_metadata37), do: :nxm_1 + def vendor_of(:tun_metadata38), do: :nxm_1 + def vendor_of(:tun_metadata39), do: :nxm_1 + def vendor_of(:tun_metadata40), do: :nxm_1 + def vendor_of(:tun_metadata41), do: :nxm_1 + def vendor_of(:tun_metadata42), do: :nxm_1 + def vendor_of(:tun_metadata43), do: :nxm_1 + def vendor_of(:tun_metadata44), do: :nxm_1 + def vendor_of(:tun_metadata45), do: :nxm_1 + def vendor_of(:tun_metadata46), do: :nxm_1 + def vendor_of(:tun_metadata47), do: :nxm_1 + def vendor_of(:tun_metadata48), do: :nxm_1 + def vendor_of(:tun_metadata49), do: :nxm_1 + def vendor_of(:tun_metadata50), do: :nxm_1 + def vendor_of(:tun_metadata51), do: :nxm_1 + def vendor_of(:tun_metadata52), do: :nxm_1 + def vendor_of(:tun_metadata53), do: :nxm_1 + def vendor_of(:tun_metadata54), do: :nxm_1 + def vendor_of(:tun_metadata55), do: :nxm_1 + def vendor_of(:tun_metadata56), do: :nxm_1 + def vendor_of(:tun_metadata57), do: :nxm_1 + def vendor_of(:tun_metadata58), do: :nxm_1 + def vendor_of(:tun_metadata59), do: :nxm_1 + def vendor_of(:tun_metadata60), do: :nxm_1 + def vendor_of(:tun_metadata61), do: :nxm_1 + def vendor_of(:tun_metadata62), do: :nxm_1 + def vendor_of(:tun_metadata63), do: :nxm_1 + def vendor_of(:tun_flags), do: :nxm_1 + def vendor_of(:ct_state), do: :nxm_1 + def vendor_of(:ct_zone), do: :nxm_1 + def vendor_of(:ct_mark), do: :nxm_1 + def vendor_of(:ct_label), do: :nxm_1 + def vendor_of(:tun_ipv6_src), do: :nxm_1 + def vendor_of(:tun_ipv6_dst), do: :nxm_1 + def vendor_of(:xxreg0), do: :nxm_1 + def vendor_of(:xxreg1), do: :nxm_1 + def vendor_of(:xxreg2), do: :nxm_1 + def vendor_of(:xxreg3), do: :nxm_1 + def vendor_of(:xxreg4), do: :nxm_1 + def vendor_of(:xxreg5), do: :nxm_1 + def vendor_of(:xxreg6), do: :nxm_1 + def vendor_of(:xxreg7), do: :nxm_1 + def vendor_of(:ct_ip_proto), do: :nxm_1 + def vendor_of(:ct_ipv4_src), do: :nxm_1 + def vendor_of(:ct_ipv4_dst), do: :nxm_1 + def vendor_of(:ct_ipv6_src), do: :nxm_1 + def vendor_of(:ct_ipv6_dst), do: :nxm_1 + def vendor_of(:ct_tp_src), do: :nxm_1 + def vendor_of(:ct_tp_dst), do: :nxm_1 + + # OpenFlow Basic + def vendor_of(:in_port), do: :openflow_basic + def vendor_of(:in_phy_port), do: :openflow_basic + def vendor_of(:metadata), do: :openflow_basic + def vendor_of(:eth_dst), do: :openflow_basic + def vendor_of(:eth_src), do: :openflow_basic + def vendor_of(:eth_type), do: :openflow_basic + def vendor_of(:vlan_vid), do: :openflow_basic + def vendor_of(:vlan_pcp), do: :openflow_basic + def vendor_of(:ip_dscp), do: :openflow_basic + def vendor_of(:ip_ecn), do: :openflow_basic + def vendor_of(:ip_proto), do: :openflow_basic + def vendor_of(:ipv4_src), do: :openflow_basic + def vendor_of(:ipv4_dst), do: :openflow_basic + def vendor_of(:tcp_src), do: :openflow_basic + def vendor_of(:tcp_dst), do: :openflow_basic + def vendor_of(:udp_src), do: :openflow_basic + def vendor_of(:udp_dst), do: :openflow_basic + def vendor_of(:sctp_src), do: :openflow_basic + def vendor_of(:sctp_dst), do: :openflow_basic + def vendor_of(:icmpv4_type), do: :openflow_basic + def vendor_of(:icmpv4_code), do: :openflow_basic + def vendor_of(:arp_op), do: :openflow_basic + def vendor_of(:arp_spa), do: :openflow_basic + def vendor_of(:arp_tpa), do: :openflow_basic + def vendor_of(:arp_sha), do: :openflow_basic + def vendor_of(:arp_tha), do: :openflow_basic + def vendor_of(:ipv6_src), do: :openflow_basic + def vendor_of(:ipv6_dst), do: :openflow_basic + def vendor_of(:ipv6_flabel), do: :openflow_basic + def vendor_of(:icmpv6_type), do: :openflow_basic + def vendor_of(:icmpv6_code), do: :openflow_basic + def vendor_of(:ipv6_nd_target), do: :openflow_basic + def vendor_of(:ipv6_nd_sll), do: :openflow_basic + def vendor_of(:ipv6_nd_tll), do: :openflow_basic + def vendor_of(:mpls_label), do: :openflow_basic + def vendor_of(:mpls_tc), do: :openflow_basic + def vendor_of(:mpls_bos), do: :openflow_basic + def vendor_of(:pbb_isid), do: :openflow_basic + def vendor_of(:tunnel_id), do: :openflow_basic + def vendor_of(:ipv6_exthdr), do: :openflow_basic + def vendor_of(:pbb_uca), do: :openflow_basic + def vendor_of(:packet_type), do: :openflow_basic + def vendor_of(:gre_flags), do: :openflow_basic + def vendor_of(:gre_ver), do: :openflow_basic + def vendor_of(:gre_protocol), do: :openflow_basic + def vendor_of(:gre_key), do: :openflow_basic + def vendor_of(:gre_seqnum), do: :openflow_basic + def vendor_of(:lisp_flags), do: :openflow_basic + def vendor_of(:lisp_nonce), do: :openflow_basic + def vendor_of(:lisp_id), do: :openflow_basic + def vendor_of(:vxlan_flags), do: :openflow_basic + def vendor_of(:vxlan_vni), do: :openflow_basic + def vendor_of(:mpls_data_first_nibble), do: :openflow_basic + def vendor_of(:mpls_ach_version), do: :openflow_basic + def vendor_of(:mpls_ach_channel), do: :openflow_basic + def vendor_of(:mpls_pw_metadata), do: :openflow_basic + def vendor_of(:mpls_cw_flags), do: :openflow_basic + def vendor_of(:mpls_cw_fragment), do: :openflow_basic + def vendor_of(:mpls_cw_len), do: :openflow_basic + def vendor_of(:mpls_cw_seq_num), do: :openflow_basic + def vendor_of(:gtpu_flags), do: :openflow_basic + def vendor_of(:gtpu_ver), do: :openflow_basic + def vendor_of(:gtpu_msg_type), do: :openflow_basic + def vendor_of(:gtpu_teid), do: :openflow_basic + def vendor_of(:gtpu_extn_hdr), do: :openflow_basic + def vendor_of(:gtpu_extn_udp_port), do: :openflow_basic + def vendor_of(:gtpu_extn_sci), do: :openflow_basic + + # Packet Register + def vendor_of(:xreg0), do: :packet_register + def vendor_of(:xreg1), do: :packet_register + def vendor_of(:xreg2), do: :packet_register + def vendor_of(:xreg3), do: :packet_register + def vendor_of(:xreg4), do: :packet_register + def vendor_of(:xreg5), do: :packet_register + def vendor_of(:xreg6), do: :packet_register + def vendor_of(:xreg7), do: :packet_register + + # Nicira Ext Match + def vendor_of(:nsh_flags), do: :nicira_ext_match + def vendor_of(:nsh_mdtype), do: :nicira_ext_match + def vendor_of(:nsh_np), do: :nicira_ext_match + def vendor_of(:nsh_spi), do: :nicira_ext_match + def vendor_of(:nsh_si), do: :nicira_ext_match + def vendor_of(:nsh_c1), do: :nicira_ext_match + def vendor_of(:nsh_c2), do: :nicira_ext_match + def vendor_of(:nsh_c3), do: :nicira_ext_match + def vendor_of(:nsh_c4), do: :nicira_ext_match + + # ONF Ext Match + def vendor_of(:onf_tcp_flags), do: :onf_ext_match + def vendor_of(:onf_actset_output), do: :onf_ext_match + def vendor_of(:onf_pbb_uca), do: :onf_ext_match + + # NXM0 + def format_of(:nx_in_port), do: {:be16, :openflow10_port} + def format_of(:nx_eth_dst), do: {:mac, :ethernet} + def format_of(:nx_eth_src), do: {:mac, :ethernet} + def format_of(:nx_eth_type), do: {:be16, :hexadecimal} + def format_of(:nx_vlan_tci), do: {:be16, :hexadecimal} + def format_of(:nx_ip_tos), do: {:u8, :decimal} + def format_of(:nx_ip_proto), do: {:u8, :decimal} + def format_of(:nx_ipv4_src), do: {:be32, :ipv4} + def format_of(:nx_ipv4_dst), do: {:be32, :ipv4} + def format_of(:nx_tcp_src), do: {:be16, :decimal} + def format_of(:nx_tcp_dst), do: {:be16, :decimal} + def format_of(:nx_udp_src), do: {:be16, :decimal} + def format_of(:nx_udp_dst), do: {:be16, :decimal} + def format_of(:nx_icmpv4_type), do: {:u8, :decimal} + def format_of(:nx_icmpv4_code), do: {:u8, :decimal} + def format_of(:nx_arp_op), do: {:be16, :decimal} + def format_of(:nx_arp_spa), do: {:be32, :ipv4} + def format_of(:nx_arp_tpa), do: {:be32, :ipv4} + def format_of(:nx_tcp_flags), do: {:be16, :tcp_flags} + + # NXM1 + def format_of(:reg0), do: {:be32, :hexadecimal} + def format_of(:reg1), do: {:be32, :hexadecimal} + def format_of(:reg2), do: {:be32, :hexadecimal} + def format_of(:reg3), do: {:be32, :hexadecimal} + def format_of(:reg4), do: {:be32, :hexadecimal} + def format_of(:reg5), do: {:be32, :hexadecimal} + def format_of(:reg6), do: {:be32, :hexadecimal} + def format_of(:reg7), do: {:be32, :hexadecimal} + def format_of(:reg8), do: {:be32, :hexadecimal} + def format_of(:reg9), do: {:be32, :hexadecimal} + def format_of(:reg10), do: {:be32, :hexadecimal} + def format_of(:reg11), do: {:be32, :hexadecimal} + def format_of(:reg12), do: {:be32, :hexadecimal} + def format_of(:reg13), do: {:be32, :hexadecimal} + def format_of(:reg14), do: {:be32, :hexadecimal} + def format_of(:reg15), do: {:be32, :hexadecimal} + def format_of(:tun_id), do: {:be64, :hexadecimal} + def format_of(:nx_arp_sha), do: {:mac, :ethernet} + def format_of(:nx_arp_tha), do: {:mac, :ethernet} + def format_of(:nx_ipv6_src), do: {:be128, :ipv6} + def format_of(:nx_ipv6_dst), do: {:be128, :ipv6} + def format_of(:nx_icmpv6_type), do: {:u8, :decimal} + def format_of(:nx_icmpv6_code), do: {:u8, :decimal} + def format_of(:nx_ipv6_nd_target), do: {:be128, :ipv6} + def format_of(:nx_ipv6_nd_sll), do: {:mac, :ethernet} + def format_of(:nx_ipv6_nd_tll), do: {:mac, :ethernet} + def format_of(:nx_ip_frag), do: {:u8, :decimal} + def format_of(:nx_ipv6_label), do: {:be32, :hexadecimal} + def format_of(:nx_ip_ecn), do: {:u8, :decimal} + def format_of(:nx_ip_ttl), do: {:u8, :decimal} + def format_of(:nx_mpls_ttl), do: {:u8, :decimal} + def format_of(:tun_src), do: {:be32, :ipv4} + def format_of(:tun_dst), do: {:be32, :ipv4} + def format_of(:pkt_mark), do: {:be32, :hexadecimal} + def format_of(:dp_hash), do: {:be32, :hexadecimal} + def format_of(:recirc_id), do: {:be32, :hexadecimal} + def format_of(:conj_id), do: {:be32, :hexadecimal} + def format_of(:nx_tun_gbp_id), do: {:be16, :decimal} + def format_of(:nx_tun_gbp_flags), do: {:u8, :decimal} + def format_of(:tun_metadata0), do: {:dynamic, :bytes} + def format_of(:tun_metadata1), do: {:dynamic, :bytes} + def format_of(:tun_metadata2), do: {:dynamic, :bytes} + def format_of(:tun_metadata3), do: {:dynamic, :bytes} + def format_of(:tun_metadata4), do: {:dynamic, :bytes} + def format_of(:tun_metadata5), do: {:dynamic, :bytes} + def format_of(:tun_metadata6), do: {:dynamic, :bytes} + def format_of(:tun_metadata7), do: {:dynamic, :bytes} + def format_of(:tun_metadata8), do: {:dynamic, :bytes} + def format_of(:tun_metadata9), do: {:dynamic, :bytes} + def format_of(:tun_metadata10), do: {:dynamic, :bytes} + def format_of(:tun_metadata11), do: {:dynamic, :bytes} + def format_of(:tun_metadata12), do: {:dynamic, :bytes} + def format_of(:tun_metadata13), do: {:dynamic, :bytes} + def format_of(:tun_metadata14), do: {:dynamic, :bytes} + def format_of(:tun_metadata15), do: {:dynamic, :bytes} + def format_of(:tun_metadata16), do: {:dynamic, :bytes} + def format_of(:tun_metadata17), do: {:dynamic, :bytes} + def format_of(:tun_metadata18), do: {:dynamic, :bytes} + def format_of(:tun_metadata19), do: {:dynamic, :bytes} + def format_of(:tun_metadata20), do: {:dynamic, :bytes} + def format_of(:tun_metadata21), do: {:dynamic, :bytes} + def format_of(:tun_metadata22), do: {:dynamic, :bytes} + def format_of(:tun_metadata23), do: {:dynamic, :bytes} + def format_of(:tun_metadata24), do: {:dynamic, :bytes} + def format_of(:tun_metadata25), do: {:dynamic, :bytes} + def format_of(:tun_metadata26), do: {:dynamic, :bytes} + def format_of(:tun_metadata27), do: {:dynamic, :bytes} + def format_of(:tun_metadata28), do: {:dynamic, :bytes} + def format_of(:tun_metadata29), do: {:dynamic, :bytes} + def format_of(:tun_metadata30), do: {:dynamic, :bytes} + def format_of(:tun_metadata31), do: {:dynamic, :bytes} + def format_of(:tun_metadata32), do: {:dynamic, :bytes} + def format_of(:tun_metadata33), do: {:dynamic, :bytes} + def format_of(:tun_metadata34), do: {:dynamic, :bytes} + def format_of(:tun_metadata35), do: {:dynamic, :bytes} + def format_of(:tun_metadata36), do: {:dynamic, :bytes} + def format_of(:tun_metadata37), do: {:dynamic, :bytes} + def format_of(:tun_metadata38), do: {:dynamic, :bytes} + def format_of(:tun_metadata39), do: {:dynamic, :bytes} + def format_of(:tun_metadata40), do: {:dynamic, :bytes} + def format_of(:tun_metadata41), do: {:dynamic, :bytes} + def format_of(:tun_metadata42), do: {:dynamic, :bytes} + def format_of(:tun_metadata43), do: {:dynamic, :bytes} + def format_of(:tun_metadata44), do: {:dynamic, :bytes} + def format_of(:tun_metadata45), do: {:dynamic, :bytes} + def format_of(:tun_metadata46), do: {:dynamic, :bytes} + def format_of(:tun_metadata47), do: {:dynamic, :bytes} + def format_of(:tun_metadata48), do: {:dynamic, :bytes} + def format_of(:tun_metadata49), do: {:dynamic, :bytes} + def format_of(:tun_metadata50), do: {:dynamic, :bytes} + def format_of(:tun_metadata51), do: {:dynamic, :bytes} + def format_of(:tun_metadata52), do: {:dynamic, :bytes} + def format_of(:tun_metadata53), do: {:dynamic, :bytes} + def format_of(:tun_metadata54), do: {:dynamic, :bytes} + def format_of(:tun_metadata55), do: {:dynamic, :bytes} + def format_of(:tun_metadata56), do: {:dynamic, :bytes} + def format_of(:tun_metadata57), do: {:dynamic, :bytes} + def format_of(:tun_metadata58), do: {:dynamic, :bytes} + def format_of(:tun_metadata59), do: {:dynamic, :bytes} + def format_of(:tun_metadata60), do: {:dynamic, :bytes} + def format_of(:tun_metadata61), do: {:dynamic, :bytes} + def format_of(:tun_metadata62), do: {:dynamic, :bytes} + def format_of(:tun_metadata63), do: {:dynamic, :bytes} + def format_of(:tun_flags), do: {:be16, :decimal} + def format_of(:ct_state), do: {:be32, :ct_state} + def format_of(:ct_zone), do: {:be16, :hexadecimal} + def format_of(:ct_mark), do: {:be32, :hexadecimal} + def format_of(:ct_label), do: {:be128, :hexadecimal} + def format_of(:tun_ipv6_src), do: {:be128, :ipv6} + def format_of(:tun_ipv6_dst), do: {:be128, :ipv6} + def format_of(:xxreg0), do: {:be128, :hexadecimal} + def format_of(:xxreg1), do: {:be128, :hexadecimal} + def format_of(:xxreg2), do: {:be128, :hexadecimal} + def format_of(:xxreg3), do: {:be128, :hexadecimal} + def format_of(:xxreg4), do: {:be128, :hexadecimal} + def format_of(:xxreg5), do: {:be128, :hexadecimal} + def format_of(:xxreg6), do: {:be128, :hexadecimal} + def format_of(:xxreg7), do: {:be128, :hexadecimal} + def format_of(:ct_ip_proto), do: {:u8, :decimal} + def format_of(:ct_ipv4_src), do: {:be32, :ipv4} + def format_of(:ct_ipv4_dst), do: {:be32, :ipv4} + def format_of(:ct_ipv6_src), do: {:be128, :ipv6} + def format_of(:ct_ipv6_dst), do: {:be128, :ipv6} + def format_of(:ct_tp_src), do: {:be16, :decimal} + def format_of(:ct_tp_dst), do: {:be16, :decimal} + + # OpenFlow Basic + def format_of(:in_port), do: {:be32, :openflow13_port} + def format_of(:in_phy_port), do: {:be32, :openflow13_port} + def format_of(:metadata), do: {:be64, :hexadecimal} + def format_of(:eth_dst), do: {:mac, :ethernet} + def format_of(:eth_src), do: {:mac, :ethernet} + def format_of(:eth_type), do: {:be16, :hexadecimal} + def format_of(:vlan_vid), do: {:be16, :hexadecimal} + def format_of(:vlan_pcp), do: {:u8, :decimal} + def format_of(:ip_dscp), do: {:u8, :decimal} + def format_of(:ip_ecn), do: {:u8, :decimal} + def format_of(:ip_proto), do: {:u8, :decimal} + def format_of(:ipv4_src), do: {:be32, :ipv4} + def format_of(:ipv4_dst), do: {:be32, :ipv4} + def format_of(:tcp_src), do: {:be16, :decimal} + def format_of(:tcp_dst), do: {:be16, :decimal} + def format_of(:udp_src), do: {:be16, :decimal} + def format_of(:udp_dst), do: {:be16, :decimal} + def format_of(:sctp_src), do: {:be16, :decimal} + def format_of(:sctp_dst), do: {:be16, :decimal} + def format_of(:icmpv4_type), do: {:u8, :decimal} + def format_of(:icmpv4_code), do: {:u8, :decimal} + def format_of(:arp_op), do: {:be16, :decimal} + def format_of(:arp_spa), do: {:be32, :ipv4} + def format_of(:arp_tpa), do: {:be32, :ipv4} + def format_of(:arp_sha), do: {:mac, :ethernet} + def format_of(:arp_tha), do: {:mac, :ethernet} + def format_of(:ipv6_src), do: {:be128, :ipv6} + def format_of(:ipv6_dst), do: {:be128, :ipv6} + def format_of(:ipv6_flabel), do: {:be32, :hexadecimal} + def format_of(:icmpv6_type), do: {:u8, :decimal} + def format_of(:icmpv6_code), do: {:u8, :decimal} + def format_of(:ipv6_nd_target), do: {:be128, :ipv6} + def format_of(:ipv6_nd_sll), do: {:mac, :ethernet} + def format_of(:ipv6_nd_tll), do: {:mac, :ethernet} + def format_of(:mpls_label), do: {:be32, :decimal} + def format_of(:mpls_tc), do: {:u8, :decimal} + def format_of(:mpls_bos), do: {:u8, :decimal} + def format_of(:pbb_isid), do: {:u24, :decimal} + def format_of(:tunnel_id), do: {:be64, :hexadecimal} + def format_of(:ipv6_exthdr), do: {:be16, :ipv6exthdr_flags} + def format_of(:pbb_uca), do: {:u8, :decimal} + def format_of(:packet_type), do: {:be32, :decimal} + def format_of(:gre_flags), do: {:be16, :decimal} + def format_of(:gre_ver), do: {:u8, :decimal} + def format_of(:gre_protocol), do: {:be16, :decimal} + def format_of(:gre_key), do: {:be32, :decimal} + def format_of(:gre_seqnum), do: {:be32, :decimal} + def format_of(:lisp_flags), do: {:u8, :decimal} + def format_of(:lisp_nonce), do: {:u24, :decimal} + def format_of(:lisp_id), do: {:be32, :decimal} + def format_of(:vxlan_flags), do: {:u8, :decimal} + def format_of(:vxlan_vni), do: {:u24, :decimal} + def format_of(:mpls_data_first_nibble), do: {:u8, :decimal} + def format_of(:mpls_ach_version), do: {:u8, :decimal} + def format_of(:mpls_ach_channel), do: {:be16, :decimal} + def format_of(:mpls_pw_metadata), do: {:u8, :decimal} + def format_of(:mpls_cw_flags), do: {:u8, :decimal} + def format_of(:mpls_cw_fragment), do: {:u8, :decimal} + def format_of(:mpls_cw_len), do: {:u8, :decimal} + def format_of(:mpls_cw_seq_num), do: {:be16, :decimal} + def format_of(:gtpu_flags), do: {:u8, :decimal} + def format_of(:gtpu_ver), do: {:u8, :decimal} + def format_of(:gtpu_msg_type), do: {:u8, :decimal} + def format_of(:gtpu_teid), do: {:be32, :decimal} + def format_of(:gtpu_extn_hdr), do: {:u8, :decimal} + def format_of(:gtpu_extn_udp_port), do: {:be16, :decimal} + def format_of(:gtpu_extn_sci), do: {:be16, :decimal} + + # Packet Register + def format_of(:xreg0), do: {:be64, :hexadecimal} + def format_of(:xreg1), do: {:be64, :hexadecimal} + def format_of(:xreg2), do: {:be64, :hexadecimal} + def format_of(:xreg3), do: {:be64, :hexadecimal} + def format_of(:xreg4), do: {:be64, :hexadecimal} + def format_of(:xreg5), do: {:be64, :hexadecimal} + def format_of(:xreg6), do: {:be64, :hexadecimal} + def format_of(:xreg7), do: {:be64, :hexadecimal} + + # Nicira Ext Match + def format_of(:nsh_flags), do: {:u8, :decimal} + def format_of(:nsh_mdtype), do: {:u8, :decimal} + def format_of(:nsh_np), do: {:u8, :decimal} + def format_of(:nsh_spi), do: {:be32, :decimal} + def format_of(:nsh_si), do: {:be32, :decimal} + def format_of(:nsh_c1), do: {:be32, :decimal} + def format_of(:nsh_c2), do: {:be32, :decimal} + def format_of(:nsh_c3), do: {:be32, :decimal} + def format_of(:nsh_c4), do: {:be32, :decimal} + + # ONF Ext Match + def format_of(:onf_tcp_flags), do: {:be16, :tcp_flags} + def format_of(:onf_actset_output), do: {:be32, :openflow13_port} + def format_of(:onf_pbb_uca), do: {:u8, :decimal} + + # Formatting = decimal + def formatting(<>, :u8, :decimal), do: value + def formatting(value, :u8, :decimal) when is_integer(value), do: <> + def formatting(<>, :be16, :decimal), do: value + def formatting(value, :be16, :decimal) when is_integer(value), do: <> + def formatting(<>, :u24, :decimal), do: value + def formatting(value, :u24, :decimal) when is_integer(value), do: <> + def formatting(<>, :be32, :decimal), do: value + def formatting(value, :be32, :decimal) when is_integer(value), do: <> + def formatting(<>, :be64, :decimal), do: value + def formatting(value, :be64, :decimal) when is_integer(value), do: <> + def formatting(<>, :be128, :decimal), do: value + def formatting(value, :be128, :decimal) when is_integer(value), do: <> + + # Formatting = hexadecimal + def formatting(<>, :be16, :hexadecimal), do: value + def formatting(value, :be16, :hexadecimal) when is_integer(value), do: <> + + def formatting(<>, :u24, :hexadecimal), do: value + def formatting(value, :u24, :hexadecimal) when is_integer(value), do: <> + + def formatting(<>, :be32, :hexadecimal), do: value + def formatting(value, :be32, :hexadecimal) when is_integer(value), do: <> + + def formatting(<>, :be64, :hexadecimal), do: value + def formatting(value, :be64, :hexadecimal) when is_integer(value), do: <> + + def formatting(<>, :be128, :hexadecimal), do: value + def formatting(value, :be128, :hexadecimal) when is_integer(value), do: <> + + # Formatting = ethernet + def formatting(<>, :mac, :ethernet), do: Openflow.Utils.to_hex_string(value) + def formatting(value, :mac, :ethernet), do: <<(String.to_integer(value, 16))::48>> + + # Formatting = IPv4 + def formatting(<>, :be32, :ipv4), do: {a1, a2, a3, a4} + def formatting({a1, a2, a3, a4}, :be32, :ipv4), do: <> + + # Formatting = IPv6 + def formatting(<>, :be128, :ipv6) do + {a1, a2, a3, a4, a5, a6, a7, a8} + end + def formatting({a1, a2, a3, a4, a5, a6, a7, a8},:be128, :ipv6) do + <> + end + + # Formatting = OpenFlow 1.0 port + def formatting(<>, :be16, :openflow10_port) do + try do + Openflow.Enums.to_atom(value, :openflow10_port_no) + catch + :bad_enum -> value + end + end + def formatting(value, :be16, :openflow10_port) do + port_no = + try do + Openflow.Enums.to_int(value, :openflow10_port_no) + catch + :bad_enum -> value + end + <> + end + + # Formatting = OpenFlow 1.3 port + def formatting(<>, :be32, :openflow13_port) do + try do + Openflow.Enums.to_atom(value, :openflow13_port_no) + catch + :bad_enum -> value + end + end + def formatting(value, :be32, :openflow13_port) do + port_no = + try do + Openflow.Enums.to_int(value, :openflow13_port_no) + catch + :bad_enum -> value + end + <> + end + + # TCP flags + def formatting(<>, :be16, :tcp_flags) do + Openflow.Enums.int_to_flags(value, :tcp_flags) + end + def formatting(value, :be16, :tcp_flags) do + <<(Openflow.Enums.flags_to_int(value, :tcp_flags))::16>> + end + + # CT State + def formatting(<>, :be32, :ct_state) do + Openflow.Enums.int_to_flags(value, :ct_state_flags) + end + def formatting(value, :be32, :ct_state) do + <<(Openflow.Enums.flags_to_int(value, :ct_state_flags))::32>> + end + + # CT State + def formatting(<>, :be16, :ipv6exthdr_flags) do + Openflow.Enums.int_to_flags(value, :ipv6exthdr_flags) + end + def formatting(value, :be16, :ipv6exthdr_flags) do + <<(Openflow.Enums.flags_to_int(value, :ipv6exthdr_flags))::16>> + end + + # Other + def formatting(value, _, _) do + value + end +end diff --git a/lib/openflow/meter_band.ex b/lib/openflow/meter_band.ex new file mode 100644 index 0000000..cbc4805 --- /dev/null +++ b/lib/openflow/meter_band.ex @@ -0,0 +1,28 @@ +defmodule Openflow.MeterBand do + + def read(meter_band_bin) do + do_read([], meter_band_bin) + end + + def to_binary(meter_bands) when is_list(meter_bands) do + to_binary(<<>>, meter_bands) + end + def to_binary(meter_band) do + to_binary([meter_band]) + end + + # private functions + + defp do_read(acc, <<>>), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + codec = Openflow.Enums.to_atom(type, :meter_band_type) + do_read([codec.read(meter_band_bin)|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [meter_band|rest]) do + codec = meter_band.__struct__ + to_binary(<>, rest) + end +end diff --git a/lib/openflow/meter_band/drop.ex b/lib/openflow/meter_band/drop.ex new file mode 100644 index 0000000..6336652 --- /dev/null +++ b/lib/openflow/meter_band/drop.ex @@ -0,0 +1,22 @@ +defmodule Openflow.MeterBand.Drop do + defstruct( + rate: 0, + burst_size: 0 + ) + + alias __MODULE__ + + def new(options) do + rate = Keyword.get(options, :rate, 0) + burst_size = Keyword.get(options, :burst_size, 0) + %Drop{rate: rate, burst_size: burst_size} + end + + def read(<<1::16, 16::16, rate::32, burst_size::32, _::size(4)-unit(8)>>) do + %Drop{rate: rate, burst_size: burst_size} + end + + def to_binary(%Drop{rate: rate, burst_size: burst_size}) do + <<1::16, 16::16, rate::32, burst_size::32, 0::size(4)-unit(8)>> + end +end diff --git a/lib/openflow/meter_band/experimenter.ex b/lib/openflow/meter_band/experimenter.ex new file mode 100644 index 0000000..fa70d4d --- /dev/null +++ b/lib/openflow/meter_band/experimenter.ex @@ -0,0 +1,24 @@ +defmodule Openflow.MeterBand.Experimenter do + defstruct( + rate: 0, + burst_size: 0, + experimenter: 0, + ) + + alias __MODULE__ + + def new(options) do + rate = Keyword.get(options, :rate, 0) + burst_size = Keyword.get(options, :burst_size, 0) + experimenter = Keyword.get(options, :experimenter, 0) + %Experimenter{rate: rate, burst_size: burst_size, experimenter: experimenter} + end + + def read(<<0xffff::16, _::16, rate::32, burst_size::32, experimenter::32>>) do + %Experimenter{rate: rate, burst_size: burst_size, experimenter: experimenter} + end + + def to_binary(%Experimenter{rate: rate, burst_size: burst_size, experimenter: experimenter}) do + <<0xffff::16, 16::16, rate::32, burst_size::32, experimenter::32>> + end +end diff --git a/lib/openflow/meter_band/remark.ex b/lib/openflow/meter_band/remark.ex new file mode 100644 index 0000000..6e0f4bf --- /dev/null +++ b/lib/openflow/meter_band/remark.ex @@ -0,0 +1,24 @@ +defmodule Openflow.MeterBand.Remark do + defstruct( + rate: 0, + burst_size: 0, + prec_level: 0 + ) + + alias __MODULE__ + + def new(options) do + rate = Keyword.get(options, :rate, 0) + burst_size = Keyword.get(options, :burst_size, 0) + prec_level = Keyword.get(options, :prec_level, 0) + %Remark{rate: rate, burst_size: burst_size, prec_level: prec_level} + end + + def read(<<2::16, 16::16, rate::32, burst_size::32, prec_level::8, _::size(3)-unit(8)>>) do + %Remark{rate: rate, burst_size: burst_size, prec_level: prec_level} + end + + def to_binary(%Remark{rate: rate, burst_size: burst_size, prec_level: prec_level}) do + <<2::16, 16::16, rate::32, burst_size::32, prec_level::8, 0::size(3)-unit(8)>> + end +end diff --git a/lib/openflow/meter_mod.ex b/lib/openflow/meter_mod.ex new file mode 100644 index 0000000..24aab63 --- /dev/null +++ b/lib/openflow/meter_mod.ex @@ -0,0 +1,32 @@ +defmodule Openflow.MeterMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + command: :add, + flags: [], + meter_id: 0, + bands: [] + ) + + alias __MODULE__ + + def ofp_type, do: 29 + + def read(<>) do + command = Openflow.Enums.to_atom(command_int, :meter_mod_command) + flags = Openflow.Enums.int_to_flags(flags_int, :meter_flags) + meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) + bands = Openflow.MeterBand.read(bands_bin) + %MeterMod{command: command, flags: flags, meter_id: meter_id, bands: bands} + end + + def to_binary(%MeterMod{command: command, flags: flags, meter_id: meter_id, bands: bands}) do + command_int = Openflow.Enums.to_int(command, :meter_mod_command) + flags_int = Openflow.Enums.flags_to_int(flags, :meter_flags) + meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) + bands_bin = Openflow.MeterBand.to_binary(bands) + <> + end +end diff --git a/lib/openflow/multipart/aggregate/reply.ex b/lib/openflow/multipart/aggregate/reply.ex new file mode 100644 index 0000000..bcc208f --- /dev/null +++ b/lib/openflow/multipart/aggregate/reply.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Multipart.Aggregate.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + packet_count: 0, + byte_count: 0, + flow_count: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def read(<>) do + %Reply{packet_count: packet_count, byte_count: byte_count, flow_count: flow_count} + end +end diff --git a/lib/openflow/multipart/aggregate/request.ex b/lib/openflow/multipart/aggregate/request.ex new file mode 100644 index 0000000..e7fa041 --- /dev/null +++ b/lib/openflow/multipart/aggregate/request.ex @@ -0,0 +1,67 @@ +defmodule Openflow.Multipart.Aggregate.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + table_id: :all, + out_port: :any, + out_group: :any, + cookie: 0, + cookie_mask: 0, + match: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(options) do + 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, []) + %Request{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, + cookie: cookie, + cookie_mask: cookie_mask, + match: match} + end + + def to_binary(%Request{table_id: table_id, + out_port: out_port, + out_group: out_group, + cookie: cookie, + cookie_mask: cookie_mask, + 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) + <> + end +end diff --git a/lib/openflow/multipart/desc/reply.ex b/lib/openflow/multipart/desc/reply.ex new file mode 100644 index 0000000..db3c818 --- /dev/null +++ b/lib/openflow/multipart/desc/reply.ex @@ -0,0 +1,30 @@ +defmodule Openflow.Multipart.Desc.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + mfr_desc: "", + hw_desc: "", + sw_desc: "", + serial_num: "", + dp_desc: "" + ) + + alias __MODULE__ + + @desc_str_len 256 + @serial_num_len 32 + + def ofp_type, do: 19 + + def read(<>) do + %Reply{mfr_desc: Openflow.Utils.decode_string(mfr_desc), + hw_desc: Openflow.Utils.decode_string(hw_desc), + sw_desc: Openflow.Utils.decode_string(sw_desc), + serial_num: Openflow.Utils.decode_string(serial_num), + dp_desc: Openflow.Utils.decode_string(dp_desc)} + end +end diff --git a/lib/openflow/multipart/desc/request.ex b/lib/openflow/multipart/desc/request.ex new file mode 100644 index 0000000..ffd55e2 --- /dev/null +++ b/lib/openflow/multipart/desc/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.Desc.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new do + %Request{} + end + + def read("") do + %Request{} + end + + def to_binary(%Request{} = msg) do + Openflow.Multipart.Request.header(msg) + end +end diff --git a/lib/openflow/multipart/flow/reply.ex b/lib/openflow/multipart/flow/reply.ex new file mode 100644 index 0000000..5d41dbc --- /dev/null +++ b/lib/openflow/multipart/flow/reply.ex @@ -0,0 +1,75 @@ +defmodule Openflow.Multipart.Flow.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + flows: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(flows \\ []) do + %Reply{flows: flows} + end + + def read(<>) do + flows = Openflow.Multipart.FlowStats.read(flows_bin) + %Reply{flows: flows} + end +end + +defmodule Openflow.Multipart.FlowStats do + defstruct( + table_id: 0, + duration_sec: 0, + duration_nsec: 0, + priority: 0, + idle_timeout: 0, + hard_timeout: 0, + flags: 0, + cookie: 0, + packet_count: 0, + byte_count: 0, + match: [], + instructions: [] + ) + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + do_read([codec(flow_stats_bin)|acc], 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) + flags = Openflow.Enums.int_to_flags(flags_int, :flow_mod_flags) + instructions = Openflow.Instruction.read(instructions_bin) + %FlowStats{table_id: table_id, + duration_sec: duration_sec, + duration_nsec: duration_nsec, + priority: priority, + idle_timeout: idle, + hard_timeout: hard, + flags: flags, + cookie: cookie, + packet_count: packet_count, + byte_count: byte_count, + match: match, + instructions: instructions} + end +end diff --git a/lib/openflow/multipart/flow/request.ex b/lib/openflow/multipart/flow/request.ex new file mode 100644 index 0000000..9485c63 --- /dev/null +++ b/lib/openflow/multipart/flow/request.ex @@ -0,0 +1,67 @@ +defmodule Openflow.Multipart.Flow.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + table_id: :all, + out_port: :any, + out_group: :any, + cookie: 0, + cookie_mask: 0, + match: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(options) do + 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, []) + %Request{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, + cookie: cookie, + cookie_mask: cookie_mask, + match: match} + end + + def to_binary(%Request{table_id: table_id, + out_port: out_port, + out_group: out_group, + cookie: cookie, + cookie_mask: cookie_mask, + 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) + <> + end +end diff --git a/lib/openflow/multipart/group/ *Minibuf-1* b/lib/openflow/multipart/group/ *Minibuf-1* new file mode 100644 index 0000000..04e6d8d --- /dev/null +++ b/lib/openflow/multipart/group/ *Minibuf-1* @@ -0,0 +1 @@ +Replace regexp Queue with: Group \ No newline at end of file diff --git a/lib/openflow/multipart/group/.# *Minibuf-1* b/lib/openflow/multipart/group/.# *Minibuf-1* new file mode 120000 index 0000000..7cdb737 --- /dev/null +++ b/lib/openflow/multipart/group/.# *Minibuf-1* @@ -0,0 +1 @@ +shun159@shun159.8967:1509553730 \ No newline at end of file diff --git a/lib/openflow/multipart/group/reply.ex b/lib/openflow/multipart/group/reply.ex new file mode 100644 index 0000000..8d96406 --- /dev/null +++ b/lib/openflow/multipart/group/reply.ex @@ -0,0 +1,69 @@ +defmodule Openflow.Multipart.Group.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + groups: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(groups \\ []) do + %Reply{groups: groups} + end + + def read(<>) do + groups = Openflow.Multipart.Group.read(groups_bin) + %Reply{groups: groups} + end +end + +defmodule Openflow.Multipart.Group do + defstruct( + group_id: 0, + ref_count: 0, + packet_count: 0, + byte_count: 0, + duration_sec: 0, + duration_nsec: 0, + bucket_stats: [] + ) + + @ofp_group_stats_size 40 + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + do_read([codec(group_bin)|acc], rest) + end + + defp codec(<>) do + bucket_stats_size = length - @ofp_group_stats_size + <> = tail + bucket_stats = for <> do + %{packet_count: packet_count, byte_count: byte_count} + end + %Group{group_id: group_id, + ref_count: ref_count, + packet_count: packet_count, + byte_count: byte_count, + duration_sec: duration_sec, + duration_nsec: duration_nsec, + bucket_stats: bucket_stats} + end +end diff --git a/lib/openflow/multipart/group/request.ex b/lib/openflow/multipart/group/request.ex new file mode 100644 index 0000000..288123a --- /dev/null +++ b/lib/openflow/multipart/group/request.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Multipart.Group.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + group_id: :all + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(group_id \\ :all) do + %Request{group_id: group_id} + end + + def read(<>) do + group_id = Openflow.Utils.get_enum(group_id_int, :group_id) + %Request{group_id: group_id} + end + + def to_binary(%Request{group_id: group_id} = msg) do + group_id_int = Openflow.Utils.get_enum(group_id, :group_id) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/group_desc/reply.ex b/lib/openflow/multipart/group_desc/reply.ex new file mode 100644 index 0000000..49bd412 --- /dev/null +++ b/lib/openflow/multipart/group_desc/reply.ex @@ -0,0 +1,54 @@ +defmodule Openflow.Multipart.GroupDesc.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + groups: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(groups \\ []) do + %Reply{groups: groups} + end + + def read(<>) do + groups = Openflow.Multipart.GroupDescStats.read(groups_bin) + %Reply{groups: groups} + end +end + +defmodule Openflow.Multipart.GroupDescStats do + defstruct( + type: :all, + group_id: 0, + buckets: [] + ) + + alias __MODULE__ + + @ofp_group_desc_size 8 + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + do_read([codec(group_stats_bin)|acc], rest) + end + + defp codec(<>) do + buckets_bin_len = length - @ofp_group_desc_size + <> = tail + type = Openflow.Enums.to_atom(type_int, :group_type) + buckets = Openflow.Bucket.read(buckets_bin) + %GroupDescStats{type: type, group_id: group_id, buckets: buckets} + end +end diff --git a/lib/openflow/multipart/group_desc/request.ex b/lib/openflow/multipart/group_desc/request.ex new file mode 100644 index 0000000..6f7f390 --- /dev/null +++ b/lib/openflow/multipart/group_desc/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.GroupDesc.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new do + %Request{} + end + + def read("") do + %Request{} + end + + def to_binary(%Request{} = msg) do + Openflow.Multipart.Request.header(msg) + end +end diff --git a/lib/openflow/multipart/group_features/reply.ex b/lib/openflow/multipart/group_features/reply.ex new file mode 100644 index 0000000..498d9ea --- /dev/null +++ b/lib/openflow/multipart/group_features/reply.ex @@ -0,0 +1,44 @@ +defmodule Openflow.Multipart.GroupFeatures.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + types: 0, + capabilities: [], + max_groups_for_all: 0, + max_groups_for_select: 0, + max_groups_for_indirect: 0, + max_groups_for_fast_failover: 0, + actions_for_all: 0, + actions_for_select: 0, + actions_for_indirect: 0, + actions_for_fast_failover: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def read(<>) do + capabilities = Openflow.Enums.int_to_flags(capabilities_int, :group_capabilities) + %Reply{types: types_int, + capabilities: capabilities, + max_groups_for_all: max_groups_for_all_int, + max_groups_for_select: max_groups_for_select_int, + max_groups_for_indirect: max_groups_for_indirect_int, + max_groups_for_fast_failover: max_groups_for_fast_failover_int, + actions_for_all: actions_for_all_int, + actions_for_select: actions_for_select_int, + actions_for_indirect: actions_for_indirect_int, + actions_for_fast_failover: actions_for_fast_failover_int} + end +end diff --git a/lib/openflow/multipart/group_features/request.ex b/lib/openflow/multipart/group_features/request.ex new file mode 100644 index 0000000..c51278b --- /dev/null +++ b/lib/openflow/multipart/group_features/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.GroupFeatures.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new do + %Request{} + end + + def read("") do + %Request{} + end + + def to_binary(%Request{} = msg) do + Openflow.Multipart.Request.header(msg) + end +end diff --git a/lib/openflow/multipart/meter/reply.ex b/lib/openflow/multipart/meter/reply.ex new file mode 100644 index 0000000..bafd74f --- /dev/null +++ b/lib/openflow/multipart/meter/reply.ex @@ -0,0 +1,63 @@ +defmodule Openflow.Multipart.Meter.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + meters: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def read(<>) do + meters = Openflow.Multipart.Meter.read(meters_bin) + %Reply{meters: meters} + end +end + +defmodule Openflow.Multipart.Meter do + defstruct( + meter_id: 0, + flow_count: 0, + packet_in_count: 0, + byte_in_count: 0, + duration_sec: 0, + duration_nsec: 0, + band_stats: [] + ) + + @ofp_meter_stats_size 40 + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <<_::32, length::16, _binary::bytes>> = binary) do + <> = binary + do_read([codec(meter_bin)|acc], rest) + end + + defp codec(<>) do + band_stats_size = length - @ofp_meter_stats_size + <> = tail + band_stats = for <> do + %{packet_band_count: packet_band_count,byte_band_count: byte_band_count} + end + %Meter{meter_id: meter_id, + flow_count: flow_count, + packet_in_count: packet_in_count, + byte_in_count: byte_in_count, + duration_sec: duration_sec, + duration_nsec: duration_nsec, + band_stats: band_stats} + end +end diff --git a/lib/openflow/multipart/meter/request.ex b/lib/openflow/multipart/meter/request.ex new file mode 100644 index 0000000..d9f1944 --- /dev/null +++ b/lib/openflow/multipart/meter/request.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Multipart.Meter.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + meter_id: :all + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(meter_id \\ :all) do + %Request{meter_id: meter_id} + end + + def read(<>) do + meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) + %Request{meter_id: meter_id} + end + + def to_binary(%Request{meter_id: meter_id} = msg) do + meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/meter_config/request.ex b/lib/openflow/multipart/meter_config/request.ex new file mode 100644 index 0000000..ced8c02 --- /dev/null +++ b/lib/openflow/multipart/meter_config/request.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Multipart.MeterConfig.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + meter_id: :all + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(meter_id \\ :all) do + %Request{meter_id: meter_id} + end + + def read(<>) do + meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) + %Request{meter_id: meter_id} + end + + def to_binary(%Request{meter_id: meter_id} = msg) do + meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/port_stats/reply.ex b/lib/openflow/multipart/port_stats/reply.ex new file mode 100644 index 0000000..e51d0a3 --- /dev/null +++ b/lib/openflow/multipart/port_stats/reply.ex @@ -0,0 +1,78 @@ +defmodule Openflow.Multipart.PortStats.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + ports: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(ports \\ []) do + %Reply{ports: ports} + end + + def read(<>) do + ports = Openflow.Multipart.PortStats.read(ports_bin) + %Reply{ports: ports} + end +end + +defmodule Openflow.Multipart.PortStats do + defstruct( + port_number: 0, + rx_packets: 0, + tx_packets: 0, + rx_bytes: 0, + tx_bytes: 0, + rx_dropped: 0, + tx_dropped: 0, + rx_errors: 0, + tx_errors: 0, + rx_frame_err: 0, + rx_over_err: 0, + rx_crc_err: 0, + collisions: 0, + duration_sec: 0, + duration_nsec: 0 + ) + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do + do_read([codec(port_stats_bin)|acc], rest) + end + + defp codec(<>) do + %PortStats{port_number: port_no, + rx_packets: rx_packets, + tx_packets: tx_packets, + rx_bytes: rx_bytes, + tx_bytes: tx_bytes, + rx_dropped: rx_dropped, + tx_dropped: tx_dropped, + rx_errors: rx_errors, + tx_errors: tx_errors, + rx_frame_err: rx_frame_err, + rx_over_err: rx_over_err, + rx_crc_err: rx_crc_err, + collisions: collisions, + duration_sec: duration_sec, + duration_nsec: duration_nsec} + end +end diff --git a/lib/openflow/multipart/port_stats/request.ex b/lib/openflow/multipart/port_stats/request.ex new file mode 100644 index 0000000..e432be6 --- /dev/null +++ b/lib/openflow/multipart/port_stats/request.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Multipart.PortStats.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + port_number: :any + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(port_no \\ :any) do + %Request{port_number: port_no} + end + + def read(<>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + %Request{port_number: port_no} + end + + def to_binary(%Request{port_number: port_no} = msg) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/queue/reply.ex b/lib/openflow/multipart/queue/reply.ex new file mode 100644 index 0000000..53d3284 --- /dev/null +++ b/lib/openflow/multipart/queue/reply.ex @@ -0,0 +1,59 @@ +defmodule Openflow.Multipart.Queue.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + queues: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(queues \\ []) do + %Reply{queues: queues} + end + + def read(<>) do + queues = Openflow.Multipart.Queue.read(queues_bin) + %Reply{queues: queues} + end +end + +defmodule Openflow.Multipart.Queue do + defstruct( + port_number: 0, + queue_id: 0, + tx_bytes: 0, + tx_packets: 0, + tx_errors: 0, + duration_sec: 0, + duration_nsec: 0 + ) + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do + do_read([codec(queue_bin)|acc], rest) + end + + defp codec(<>) do + %Queue{port_number: port_no, + queue_id: queue_id, + tx_bytes: tx_bytes, + tx_packets: tx_packets, + tx_errors: tx_errors, + duration_sec: duration_sec, + duration_nsec: duration_nsec} + end +end diff --git a/lib/openflow/multipart/queue/request.ex b/lib/openflow/multipart/queue/request.ex new file mode 100644 index 0000000..654a407 --- /dev/null +++ b/lib/openflow/multipart/queue/request.ex @@ -0,0 +1,34 @@ +defmodule Openflow.Multipart.Queue.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + port_number: :any, + queue_id: :all + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(options) do + port_no = Keyword.get(options, :port_number, :any) + queue_id = Keyword.get(options, :queue_id, :all) + %Request{port_number: port_no, queue_id: queue_id} + end + + def read(<>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + queue_id = Openflow.Utils.get_enum(queue_id_int, :queue_id) + %Request{port_number: port_no, queue_id: queue_id} + end + + def to_binary(%Request{port_number: port_no, queue_id: queue_id} = msg) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no) + queue_id_int = Openflow.Utils.get_enum(queue_id, :queue_id) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/reply.ex b/lib/openflow/multipart/reply.ex new file mode 100644 index 0000000..ad446cc --- /dev/null +++ b/lib/openflow/multipart/reply.ex @@ -0,0 +1,27 @@ +defmodule Openflow.Multipart.Reply do + def ofp_type, do: 19 + + def read(<>) do + codec = Openflow.Enums.to_atom(type_int, :multipart_reply_codec) + flags = Openflow.Enums.int_to_flags(flags_int, :multipart_reply_flags) + reply = codec.read(reply_bin) + %{reply|flags: flags} + end + + def to_binary(%{__struct__: codec, flags: flags} = msg) do + flags_int = Openflow.Enums.flags_to_int(flags, :multipart_reply_flags) + type_int = Openflow.Enums.to_int(codec, :multipart_reply_codec) + case codec.to_binary(msg) do + reply_bin when is_binary(reply_bin) -> + <> + {:error, reason} -> + {:error, reason} + end + end + + def header(%{__struct__: codec, flags: flags}) do + flags_int = Openflow.Enums.flags_to_int(flags, :multipart_reply_flags) + type_int = Openflow.Enums.to_int(codec, :multipart_reply_codec) + <> + end +end diff --git a/lib/openflow/multipart/request.ex b/lib/openflow/multipart/request.ex new file mode 100644 index 0000000..4f6f454 --- /dev/null +++ b/lib/openflow/multipart/request.ex @@ -0,0 +1,27 @@ +defmodule Openflow.Multipart.Request do + def ofp_type, do: 18 + + def read(<>) do + codec = Openflow.Enums.to_atom(type_int, :multipart_request_codec) + flags = Openflow.Enums.int_to_flags(flags_int, :multipart_request_flags) + request = codec.read(request_bin) + %{request|flags: flags} + end + + def to_binary(%{__struct__: codec, flags: flags} = msg) do + flags_int = Openflow.Enums.flags_to_int(flags, :multipart_request_flags) + type_int = Openflow.Enums.to_int(codec, :multipart_request_codec) + case codec.to_binary(msg) do + request_bin when is_binary(request_bin) -> + <> + {:error, reason} -> + {:error, reason} + end + end + + def header(%{__struct__: codec, flags: flags}) do + flags_int = Openflow.Enums.flags_to_int(flags, :multipart_request_flags) + type_int = Openflow.Enums.to_int(codec, :multipart_request_codec) + <> + end +end diff --git a/lib/openflow/multipart/table/reply.ex b/lib/openflow/multipart/table/reply.ex new file mode 100644 index 0000000..496450b --- /dev/null +++ b/lib/openflow/multipart/table/reply.ex @@ -0,0 +1,45 @@ +defmodule Openflow.Multipart.Table.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + tables: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def read(<>) do + tables = Openflow.Multipart.TableStats.read(tables_bin) + %Reply{tables: tables} + end +end + +defmodule Openflow.Multipart.TableStats do + defstruct( + table_id: 0, + active_count: 0, + lookup_count: 0, + matched_count: 0 + ) + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do + do_read([codec(table_stats_bin)|acc], rest) + end + + defp codec(<>) do + %TableStats{table_id: table_id, active_count: active_count, + lookup_count: lookup_count, matched_count: matched_count} + end +end diff --git a/lib/openflow/multipart/table/request.ex b/lib/openflow/multipart/table/request.ex new file mode 100644 index 0000000..8607068 --- /dev/null +++ b/lib/openflow/multipart/table/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.Table.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new do + %Request{} + end + + def read("") do + %Request{} + end + + def to_binary(%Request{} = msg) do + Openflow.Multipart.Request.header(msg) + end +end diff --git a/lib/openflow/nx_packet_in2.ex b/lib/openflow/nx_packet_in2.ex new file mode 100644 index 0000000..1ba67e0 --- /dev/null +++ b/lib/openflow/nx_packet_in2.ex @@ -0,0 +1,34 @@ +defmodule Openflow.NxPacketIn2 do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + # packet_in properties: + packet: nil, + full_len: nil, + buffer_id: nil, + table_id: nil, + cookie: nil, + reason: nil, + metadata: nil, + userdata: nil, + continuation: nil, + # continuation properties: + continuation_bridge: nil, + continuation_stack: nil, + continuation_conntracked: nil, + continuation_table_id: nil, + continuation_cookie: nil, + continuation_actions: nil, + continuation_action_set: nil + ) + + alias __MODULE__ + + @experimenter 0x00002320 + @nx_type 30 + + def ofp_type, do: 4 + +end diff --git a/lib/openflow/nx_packet_in_format.ex b/lib/openflow/nx_packet_in_format.ex new file mode 100644 index 0000000..c5f2dd5 --- /dev/null +++ b/lib/openflow/nx_packet_in_format.ex @@ -0,0 +1,30 @@ +defmodule Openflow.NxSetPacketInFormat do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + format: :standard + ) + + @experimenter 0x00002320 + @nx_type 16 + + alias __MODULE__ + + def ofp_type, do: 4 + + def new(format \\ :standard) do + %NxSetPacketInFormat{format: format} + end + + def read(<<@experimenter::32, @nx_type::32, format_int::32>>) do + format = Openflow.Enums.to_atom(format_int, :packet_in_format) + %NxSetPacketInFormat{format: format} + end + + def to_binary(%NxSetPacketInFormat{format: format}) do + format_int = Openflow.Enums.to_int(format, :packet_in_format) + <<@experimenter::32, @nx_type::32, format_int::32>> + end +end diff --git a/lib/openflow/nx_set_controller_id.ex b/lib/openflow/nx_set_controller_id.ex new file mode 100644 index 0000000..de9c95e --- /dev/null +++ b/lib/openflow/nx_set_controller_id.ex @@ -0,0 +1,26 @@ +defmodule Openflow.NxSetControllerId do + defstruct( + version: 4, + xid: 0, + id: 0 + ) + + @experimenter 0x00002320 + @nx_type 20 + + alias __MODULE__ + + def ofp_type, do: 4 + + def new(controller_id) do + %NxSetControllerId{id: controller_id} + end + + def read(<<@experimenter::32, @nx_type::32, _::size(6)-unit(8), controller_id::16>>) do + %NxSetControllerId{id: controller_id} + end + + def to_binary(%NxSetControllerId{id: controller_id}) do + <<@experimenter::32, @nx_type::32, 0::size(6)-unit(8), controller_id::16>> + end +end diff --git a/lib/openflow/packet_in.ex b/lib/openflow/packet_in.ex new file mode 100644 index 0000000..415aa92 --- /dev/null +++ b/lib/openflow/packet_in.ex @@ -0,0 +1,60 @@ +defmodule Openflow.PacketIn do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + buffer_id: :no_buffer, + total_len: 0, + reason: :no_match, + table_id: 0, + cookie: 0, + in_port: :controller, + match: [], + data: "" + ) + + alias __MODULE__ + + def ofp_type, do: 10 + + def read(<>) do + buffer_id = Openflow.Utils.get_enum(buffer_id_int, :buffer_id) + reason = Openflow.Utils.get_enum(reason_int, :packet_in_reason) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + {match_fields0, rest1} = Openflow.Match.read(rest0) + <<_pad::size(2)-unit(8), data::bytes>> = rest1 + in_port = Keyword.get(match_fields0, :in_port, :any) + match_fields = Keyword.delete(match_fields0, :in_port) + %PacketIn{buffer_id: buffer_id, + total_len: total_len, + reason: reason, + table_id: table_id, + cookie: cookie, + in_port: in_port, + match: match_fields, + data: data} + end + + def to_binary(%PacketIn{} = packet_in) do + %PacketIn{buffer_id: buffer_id, + total_len: total_len, + reason: reason, + table_id: table_id, + cookie: cookie, + in_port: in_port, + match: match_fields, + data: data} = packet_in + buffer_id_int = Openflow.Utils.get_enum(buffer_id, :buffer_id) + reason_int = Openflow.Utils.get_enum(reason, :packet_in_reason) + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + match_fields_bin = + [{:in_port, in_port}|match_fields] + |> Openflow.Match.new + |> Openflow.Match.to_binary + <> + end +end diff --git a/lib/openflow/packet_out.ex b/lib/openflow/packet_out.ex new file mode 100644 index 0000000..dcadd43 --- /dev/null +++ b/lib/openflow/packet_out.ex @@ -0,0 +1,51 @@ +defmodule Openflow.PacketOut do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + buffer_id: :no_buffer, + in_port: :controller, + actions: [], + data: "" + ) + + alias __MODULE__ + + def ofp_type, do: 13 + + def new(options) do + buffer_id = Keyword.get(options, :buffer_id, :no_buffer) + in_port = Keyword.get(options, :in_port, :controller) + actions = Keyword.get(options, :actions, []) + data = Keyword.get(options, :data, "") + %PacketOut{buffer_id: buffer_id, + in_port: in_port, + actions: actions, + data: data} + end + + def read(<>) do + buffer_id = Openflow.Utils.get_enum(buffer_id_int, :buffer_id) + in_port = Openflow.Utils.get_enum(in_port_int, :openflow13_port_no) + actions = Openflow.Action.read(actions_bin) + %PacketOut{buffer_id: buffer_id, + in_port: in_port, + actions: actions, + data: data} + end + + def to_binary(%PacketOut{} = packet_out) do + %PacketOut{buffer_id: buffer_id, + in_port: in_port, + actions: actions, + data: data} = packet_out + buffer_id_int = Openflow.Utils.get_enum(buffer_id, :buffer_id) + in_port_int = Openflow.Utils.get_enum(in_port, :openflow13_port_no) + actions_bin = Openflow.Action.to_binary(actions) + actions_len = byte_size(actions_bin) + <> + end +end diff --git a/lib/openflow/port.ex b/lib/openflow/port.ex new file mode 100644 index 0000000..1f71180 --- /dev/null +++ b/lib/openflow/port.ex @@ -0,0 +1,47 @@ +defmodule Openflow.Port do + defstruct( + number: 0, + hw_addr: "000000000000", + name: "", + config: [], + state: [], + current_features: [], + advertised_features: [], + supported_features: [], + peer_features: [], + current_speed: :"10mb_hd", + max_speed: :"10mb_hd" + ) + + @ofp_max_port_name_len 16 + + alias __MODULE__ + + def read(<>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + hw_addr = Openflow.Utils.to_hex_string(hw_addr_bin) + name = Openflow.Utils.decode_string(name_bin) + config = Openflow.Enums.int_to_flags(config_int, :port_config) + state = Openflow.Enums.int_to_flags(state_int, :port_state) + curr = Openflow.Enums.int_to_flags(curr_int, :port_features) + adv = Openflow.Enums.int_to_flags(advertised_int, :port_features) + supp = Openflow.Enums.int_to_flags(supp_int, :port_features) + peer = Openflow.Enums.int_to_flags(peer_int, :port_features) + %Port{number: port_no, + hw_addr: hw_addr, + name: name, + config: config, + state: state, + current_features: curr, + advertised_features: adv, + supported_features: supp, + peer_features: peer, + current_speed: curr_speed, + max_speed: max_speed} + end +end diff --git a/lib/openflow/port_mod.ex b/lib/openflow/port_mod.ex new file mode 100644 index 0000000..01506f5 --- /dev/null +++ b/lib/openflow/port_mod.ex @@ -0,0 +1,48 @@ +defmodule Openflow.PortMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + number: 0, + hw_addr: "000000000000", + config: [], + mask: [], + advertise: [] + ) + + alias __MODULE__ + + def ofp_type, do: 16 + + def new(options \\ []) do + port_no = Keyword.get(options, :number, :all) + hw_addr = Keyword.get(options, :hw_adddr, "000000000000") + config = Keyword.get(options, :config, []) + mask = Keyword.get(options, :mask, []) + advertise = Keyword.get(options, :advertise, []) + %PortMod{number: port_no, hw_addr: hw_addr, config: config, mask: mask, advertise: advertise} + end + + def read(<>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + hw_addr = Openflow.Utils.to_hex_string(hw_addr_bin) + config = Openflow.Enums.int_to_flags(config_int, :port_config) + mask = Openflow.Enums.int_to_flags(mask_int, :port_config) + adv = Openflow.Enums.int_to_flags(advertised_int, :port_features) + %PortMod{number: port_no, hw_addr: hw_addr, config: config, mask: mask, advertise: adv} + end + + def to_binary(%PortMod{number: port_no, hw_addr: hw_addr, config: config, mask: mask, advertise: adv}) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no) + hw_addr_bin = <<(String.to_integer(hw_addr, 16))::48>> + config_int = Openflow.Enums.flags_to_int(config, :port_config) + mask_int = Openflow.Enums.flags_to_int(mask, :port_config) + advertised_int = Openflow.Enums.flags_to_int(adv, :port_features) + <> + end +end diff --git a/lib/openflow/port_status.ex b/lib/openflow/port_status.ex new file mode 100644 index 0000000..83ea182 --- /dev/null +++ b/lib/openflow/port_status.ex @@ -0,0 +1,20 @@ +defmodule Openflow.PortStatus do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + reason: :add, + port: nil + ) + + alias __MODULE__ + + def ofp_type, do: 12 + + def read(<>) do + reason = Openflow.Enums.to_atom(reason_int, :port_reason) + port = Openflow.Port.read(port_bin) + %PortStatus{reason: reason, port: port} + end +end diff --git a/lib/openflow/role/reply.ex b/lib/openflow/role/reply.ex new file mode 100644 index 0000000..9e81329 --- /dev/null +++ b/lib/openflow/role/reply.ex @@ -0,0 +1,30 @@ +defmodule Openflow.Role.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0, # virtual field + role: :nochange, + generation_id: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 25 + + def new(options \\ []) do + role = Keyword.get(options, :role, :nochange) + generation_id = Keyword.get(options, :generation_id, 0) + %Reply{role: role, generation_id: generation_id} + end + + def read(<>) do + role = Openflow.Enums.to_atom(role_int, :controller_role) + %Reply{role: role, generation_id: generation_id} + end + + def to_binary(%Reply{role: role, generation_id: generation_id}) do + role_int = Openflow.Enums.to_int(role, :controller_role) + <> + end +end diff --git a/lib/openflow/role/request.ex b/lib/openflow/role/request.ex new file mode 100644 index 0000000..e2ed43e --- /dev/null +++ b/lib/openflow/role/request.ex @@ -0,0 +1,30 @@ +defmodule Openflow.Role.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0, # virtual field + role: :nochange, + generation_id: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 24 + + def new(options \\ []) do + role = Keyword.get(options, :role, :nochange) + generation_id = Keyword.get(options, :generation_id, 0) + %Request{role: role, generation_id: generation_id} + end + + def read(<>) do + role = Openflow.Enums.to_atom(role_int, :controller_role) + %Request{role: role, generation_id: generation_id} + end + + def to_binary(%Request{role: role, generation_id: generation_id}) do + role_int = Openflow.Enums.to_int(role, :controller_role) + <> + end +end diff --git a/lib/openflow/set_async.ex b/lib/openflow/set_async.ex new file mode 100644 index 0000000..fb245d8 --- /dev/null +++ b/lib/openflow/set_async.ex @@ -0,0 +1,40 @@ +defmodule Openflow.SetAsync do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0, # virtual field + packet_in_mask_master: 0, + packet_in_mask_slave: 0, + port_status_mask_master: 0, + port_status_mask_slave: 0, + flow_removed_mask_master: 0, + flow_removed_mask_slave: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 28 + + def read(<>) do + %SetAsync{packet_in_mask_master: packet_in_mask_master, + packet_in_mask_slave: packet_in_mask_slave, + port_status_mask_master: port_status_mask_master, + port_status_mask_slave: port_status_mask_slave, + flow_removed_mask_master: flow_removed_mask_master, + flow_removed_mask_slave: flow_removed_mask_slave} + end + + def to_binary(%SetAsync{packet_in_mask_master: packet_in_mask_master, + packet_in_mask_slave: packet_in_mask_slave, + port_status_mask_master: port_status_mask_master, + port_status_mask_slave: port_status_mask_slave, + flow_removed_mask_master: flow_removed_mask_master, + flow_removed_mask_slave: flow_removed_mask_slave}) do + <> + end +end diff --git a/lib/openflow/set_config.ex b/lib/openflow/set_config.ex new file mode 100644 index 0000000..0a08ff5 --- /dev/null +++ b/lib/openflow/set_config.ex @@ -0,0 +1,32 @@ +defmodule Openflow.SetConfig do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + flags: [], # default = "normal" is no special handling + miss_send_len: 128 + ) + + alias __MODULE__ + + def ofp_type, do: 9 + + def new(options \\ []) do + flags = Keyword.get(options, :flags, []) + miss_send_len = Keyword.get(options, :miss_send_len, :no_buffer) + %SetConfig{flags: flags, miss_send_len: miss_send_len} + end + + def read(<>) do + flags = Openflow.Enums.int_to_flags(flags_int, :config_flags) + miss_send_len = Openflow.Utils.get_enum(miss_send_len0, :controller_max_len) + %SetConfig{flags: flags, miss_send_len: miss_send_len} + end + + def to_binary(%SetConfig{flags: flags, miss_send_len: miss_send_len0}) do + flags_int = Openflow.Enums.flags_to_int(flags, :config_flags) + miss_send_len = Openflow.Utils.get_enum(miss_send_len0, :controller_max_len) + <> + end +end diff --git a/lib/openflow/table_mod.ex b/lib/openflow/table_mod.ex new file mode 100644 index 0000000..d06f45b --- /dev/null +++ b/lib/openflow/table_mod.ex @@ -0,0 +1,28 @@ +defmodule Openflow.TableMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + table_id: 0, + config: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 17 + + def new(table_id) do + %TableMod{table_id: table_id} + end + + def read(<>) do + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + %TableMod{table_id: table_id, config: config} + end + + def to_binary(%TableMod{table_id: table_id, config: config}) do + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + <> + end +end diff --git a/lib/openflow/utils.ex b/lib/openflow/utils.ex new file mode 100644 index 0000000..afe8cf8 --- /dev/null +++ b/lib/openflow/utils.ex @@ -0,0 +1,73 @@ +defmodule Openflow.Utils do + import Bitwise + + def int_to_flags(acc, int, [{type, flagint}|rest]) when (int &&& flagint) == flagint do + int_to_flags([type|acc], int, rest) + end + def int_to_flags(acc, int, [_h|rest]) do + int_to_flags(acc, int, rest) + end + def int_to_flags(acc, _int, []), do: Enum.reverse(acc) + + def flags_to_int(acc, [], _enum), do: acc + def flags_to_int(acc0, [flag|rest], enum) do + acc = acc0 ||| Keyword.get(enum, flag, 0) + flags_to_int(acc, rest, enum) + end + + def to_hex_string(binary), do: datapath_id(binary) + + def padding(length, padding) do + case (padding - rem(length, padding)) do + ^padding -> 0 + pad_len -> pad_len + end + end + + def pad_length(length, width) do + rem((width - rem(length, width)), width) + end + + def decode_string(binary) do + String.trim_trailing(binary, <<0>>) + end + + def encode_string(binary, length) do + String.pad_trailing(binary, length, <<0>>) + end + + def get_enum(int, type) when is_integer(int) do + try do + Openflow.Enums.to_atom(int, type) + catch + :bad_enum -> int + end + end + def get_enum(int, type) do + try do + Openflow.Enums.to_int(int, type) + catch + :bad_enum -> int + end + end + + # private functions + + defp datapath_id(binary) do + binary + |> split_to_hex_string + |> Enum.join("") + |> String.downcase + end + + defp split_to_hex_string(binary) do + for <>, do: integer_to_hex(int) + end + + defp integer_to_hex(int) do + case Integer.to_string(int, 16) do + <> -> <<48, d>> + dd -> dd + end + end +end diff --git a/lib/tres.ex b/lib/tres.ex new file mode 100644 index 0000000..36ddade --- /dev/null +++ b/lib/tres.ex @@ -0,0 +1,2 @@ +defmodule Tres do +end diff --git a/lib/tres/application.ex b/lib/tres/application.ex new file mode 100644 index 0000000..5dc3b16 --- /dev/null +++ b/lib/tres/application.ex @@ -0,0 +1,17 @@ +defmodule Tres.Application do + @moduledoc false + + use Application + + alias Tres.SwitchRegistry + + def start(_type, _args) do + import Supervisor.Spec + + children = [worker(Registry, [[keys: :unique, name: SwitchRegistry]], id: SwitchRegistry), + supervisor(Tres.MessageHandlerSup, [], id: MessageHandlerSup)] + opts = [strategy: :one_for_one, name: Tres.Supervisor] + {:ok, _connection_pid} = Tres.Utils.start_openflow_listener + Supervisor.start_link(children, opts) + end +end diff --git a/lib/tres/message_handler.ex b/lib/tres/message_handler.ex new file mode 100644 index 0000000..7f01ed8 --- /dev/null +++ b/lib/tres/message_handler.ex @@ -0,0 +1,52 @@ +defmodule Tres.MessageHandler do + use GenServer + + defmodule State do + defstruct [ + ip_addr: nil, + port: nil, + datapath_id: nil, + conn_pid: nil, + conn_ref: nil, + handler_pid: nil, + handler_ref: nil + ] + end + + alias Tres.MessageHandler.State + + @process_flags [trap_exit: true] + + def start_link({ip_addr, port}, conn_pid) do + GenServer.start_link(__MODULE__, [{ip_addr, port}, conn_pid]) + end + + def init([{ip_addr, port}, conn_pid]) do + init_process() + conn_ref = Process.monitor(conn_pid) + state = %State{ + conn_pid: conn_pid, + conn_ref: conn_ref, + ip_addr: ip_addr, + port: port + } + {:ok, state} + end + + def handle_info({:'EXIT', _pid, _reason}, state) do + {:stop, :normal, state} + end + def handle_info({:'DOWN', conn_ref, :process, _conn_pid, _reason}, %State{conn_ref: conn_ref} = state) do + {:stop, :normal, state} + end + + def terminate(_reason, state) do + {:shutdown, state} + end + + ## private functions + + defp init_process do + for {flag, value} <- @process_flags, do: Process.flag(flag, value) + end +end diff --git a/lib/tres/message_handler_sup.ex b/lib/tres/message_handler_sup.ex new file mode 100644 index 0000000..ca6d393 --- /dev/null +++ b/lib/tres/message_handler_sup.ex @@ -0,0 +1,16 @@ +defmodule Tres.MessageHandlerSup do + use Supervisor + + def start_link do + Supervisor.start_link(__MODULE__, [], name: __MODULE__) + end + + def init(_) do + children = [worker(Tres.MessageHandler, [], restart: :temporary)] + supervise(children, strategy: :simple_one_for_one) + end + + def start_child({ip_addr, port}) do + Supervisor.start_child(__MODULE__, [{ip_addr, port}, self()]) + end +end diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex new file mode 100644 index 0000000..013e731 --- /dev/null +++ b/lib/tres/secure_channel.ex @@ -0,0 +1,385 @@ +defmodule Tres.SecureChannel do + @behaviour :gen_statem + + import Logger + + alias Tres.SecureChannelState + alias Tres.SwitchRegistry + alias Tres.MessageHandlerSup + + @process_flags [ + trap_exit: true, + message_queue_data: :on_heap + ] + + @supported_version 4 + + @hello_handshake_timeout 1000 + @features_handshake_timeout 1000 + @ping_timeout 5000 + @transaction_timeout 5000 + + @ping_interval 5000 + @ping_fail_max_count 10 + + def callback_mode do + [:handle_event_function, :state_enter] + end + + def start_link(ref, socket, transport, opts \\ []) do + init_args = [[ref, socket, transport, opts]] + :proc_lib.start_link(__MODULE__, :init, init_args) + end + + def init([ref, socket, transport, _opts]) do + state_data = + ref + |> init_secure_channel(socket, transport) + |> init_handler + info("[#{__MODULE__}] TCP connected to Switch on #{state_data.ip_addr}:#{state_data.port} on #{inspect(self())}") + :gen_statem.enter_loop(__MODULE__, [debug: []], :INIT, state_data, []) + end + + # TCP handler + def handle_event(:info, {:tcp, socket, packet}, state, + %SecureChannelState{socket: socket, transport: transport} = state_data) do + transport.setopts(socket, [active: :once]) + handle_packet(packet, state_data, state, []) + end + def handle_event(:info, {:tcp_closed, socket}, _state, + %SecureChannelState{socket: socket} = state_data) do + close_connection(:tcp_closed, state_data) + end + def handle_event(:info, {:tcp_error, socket, reason}, _state, + %SecureChannelState{socket: socket} = state_data) do + close_connection({:tcp_error, reason}, state_data) + end + def handle_event(:info, {:'DOWN', _ref, :process, _main_pid, _reason} = signal, _state, state_data) do + handle_signal(signal, state_data) + end + def handle_event(:info, {:'EXIT', _pid, _reason} = signal, _state, state_data) do + handle_signal(signal, state_data) + end + def handle_event(type, message, :INIT, state_data) do + handle_INIT(type, message, state_data) + end + def handle_event(type, message, :CONNECTING, state_data) do + handle_CONNECTING(type, message, state_data) + end + def handle_event(type, message, :CONNECTED, state_data) do + handle_CONNECTED(type, message, state_data) + end + def handle_event(type, message, :WAITING, state_data) do + handle_WATING(type, message, state_data) + end + + def terminate(reason, state, %SecureChannelState{datapath_id: datapath_id, aux_id: aux_id}) do + warn("[#{__MODULE__}] termiate: #{inspect(reason)} state = #{inspect(state)}") + :ok = SwitchRegistry.unregister({datapath_id, aux_id}) + end + + # private functions + + defp init_secure_channel(ref, socket, transport) do + init_process(ref) + :ok = transport.setopts(socket, [active: :once]) + SecureChannelState.new(ref: ref, socket: socket, transport: transport) + end + + defp init_process(ref) do + :ok = :proc_lib.init_ack({:ok, self()}) + :ok = :ranch.accept_ack(ref) + for {flag, value} <- @process_flags, do: Process.flag(flag, value) + end + + defp init_handler(state_data) do + %SecureChannelState{ip_addr: ip_addr, port: port} = state_data + {:ok, pid} = MessageHandlerSup.start_child({ip_addr, port}) + ref = Process.monitor(pid) + %{state_data|handler_pid: pid, handler_ref: ref} + end + + # INIT state + defp handle_INIT(:enter, _old_state, state_data) do + debug("[#{__MODULE__}] Initiate HELLO handshake: #{state_data.ip_addr}:#{state_data.port}") + initiate_hello_handshake(state_data) + end + defp handle_INIT(:info, :hello_timeout, state_data) do + close_connection(:hello_handshake_timeout, state_data) + end + defp handle_INIT(:internal, {:openflow, %Openflow.Hello{} = hello}, state_data) do + handle_hello_handshake_1(hello, state_data) + end + defp handle_INIT(:internal, message, _state_data) do + debug("[#{__MODULE__}] Hello handshake in progress, dropping message: #{inspect(message)}") + :keep_state_and_data + end + + # CONNECTING state + defp handle_CONNECTING(:enter, :INIT, state_data) do + debug("[#{__MODULE__}] Initiate FEATURES handshake: #{state_data.ip_addr}:#{state_data.port}") + initiate_features_handshake(state_data) + end + defp handle_CONNECTING(:enter, :WAITING, state_data) do + debug("[#{__MODULE__}] Re-entered features handshake") + initiate_features_handshake(state_data) + end + defp handle_CONNECTING(:info, :features_timeout, state_data) do + close_connection(:features_handshake_timeout, state_data) + end + defp handle_CONNECTING(:internal, {:openflow, %Openflow.Features.Reply{} = features}, state_data) do + # TODO: Send to handler + info("[#{__MODULE__}] Switch connected datapath_id: #{features.datapath_id} auxiliary_id: #{features.aux_id}") + _ = maybe_cancel_timer(state_data.timer_ref) + handle_features_handshake(features, state_data) + end + defp handle_CONNECTING(:internal, {:openflow, message}, _state_data) do + debug("[#{__MODULE__}] Features handshake in progress, dropping message: #{inspect(message.__struct__)}") + :keep_state_and_data + end + defp handle_CONNECTING(type, _message, state_data) when type == :cast or type == :call do + {:keep_state, state_data, [{:postpone, true}]} + end + + # CONNECTED state + defp handle_CONNECTED(:enter, :CONNECTING, _state_data) do + start_periodic_idle_check() + :keep_state_and_data + end + defp handle_CONNECTED(:info, :idle_check, state_data) do + start_periodic_idle_check() + new_state_data = maybe_ping(state_data) + {:keep_state, new_state_data} + end + defp handle_CONNECTED(:info, :ping_timeout, state_data) do + handle_ping_timeout(state_data, :CONNECTED) + end + defp handle_CONNECTED(:internal, {:openflow, %Openflow.Echo.Reply{xid: xid}}, + %SecureChannelState{ping_xid: xid} = state_data) do + handle_ping_reply(state_data) + end + defp handle_CONNECTED(:internal, {:openflow, %Openflow.Echo.Request{} = echo}, state_data) do + send_echo_reply(echo.xid, echo.data, state_data) + :keep_state_and_data + end + defp handle_CONNECTED(:internal, {:openflow, _message}, _state_data) do + # TODO: Send to handler + :keep_state_and_data + end + defp handle_CONNECTED(:cast, {:send_message, message}, state_data) do + message + |> send_message(state_data) + :keep_state_and_data + end + + # WATING state + defp handle_WATING(:enter, :CONNECTING, state_data) do + warn("[#{__MODULE__}] Possible HANG Detected on datapath_id: #{state_data.datapath_id} !") + start_periodic_idle_check() + :keep_state_and_data + end + defp handle_WATING(:info, :idle_check, state_data) do + start_periodic_idle_check() + new_state_data = maybe_ping(state_data) + {:keep_state, new_state_data} + end + defp handle_WATING(:info, :ping_timeout, state_data) do + handle_ping_timeout(state_data, :WAITING) + end + defp handle_WATING(:internal, {:openflow, _message}, state_data) do + # TODO: Send to handler + {:next_state, :CONNECTING, state_data} + end + defp handle_WATING(type, message, state_data) + when type == :cast or type == :call do + debug("[#{__MODULE__}] Postponed: #{inspect(message)}, now WATING") + {:keep_state, state_data, [{:postpone, true}]} + end + + defp handle_packet("", state_data, _state, actions) do + {:keep_state, state_data, Enum.reverse(actions)} + end + defp handle_packet(packet, %SecureChannelState{buffer: buffer} = state_data, state, actions) do + binary = <> + case Openflow.read(binary) do + {:ok, message, leftovers} -> + debug("[#{__MODULE__}] Received: #{inspect(message.__struct__)}(xid: #{message.xid}) in #{state}") + action = {:next_event, :internal, {:openflow, message}} + new_state_data = %{state_data|buffer: "", last_received: :os.timestamp} + handle_packet(leftovers, new_state_data, state, [action|actions]) + {:error, :binary_too_small} -> + handle_packet("", %{state_data|buffer: binary}, state, actions) + {:error, _reason} -> + handle_packet("", state_data, state, actions) + end + end + + defp initiate_hello_handshake(state_data) do + send_hello(state_data) + ref = :erlang.send_after(@hello_handshake_timeout, self(), :hello_timeout) + {:keep_state, %{state_data|timer_ref: ref}} + end + + defp handle_hello_handshake_1(hello, state_data) do + maybe_cancel_timer(state_data.timer_ref) + SecureChannelState.set_transaction_id(state_data.xid, hello.xid) + if Openflow.Hello.supported_version?(hello) do + {:next_state, :CONNECTING, %{state_data|timer_ref: nil}} + else + close_connection(:failed_version_negotiation, state_data) + end + end + + defp initiate_features_handshake(state_data) do + new_xid = SecureChannelState.increment_transaction_id(state_data.xid) + send_features(new_xid, state_data) + ref = :erlang.send_after(@features_handshake_timeout, self(), :features_timeout) + {:keep_state, %{state_data|timer_ref: ref}} + end + + defp handle_features_handshake(%Openflow.Features.Reply{datapath_id: datapath_id, aux_id: aux_id}, state_data) do + {:ok, _} = SwitchRegistry.register({datapath_id, aux_id}) + new_state_data = %{ + state_data| + datapath_id: datapath_id, + aux_id: aux_id, + timer_ref: nil, + main_monitor_ref: monitor_connection(datapath_id, aux_id) + } + {:next_state, :CONNECTED, new_state_data} + end + + defp monitor_connection(datapath_id, aux_id) when aux_id > 0 do + datapath_id + |> SwitchRegistry.lookup_pid + |> Process.monitor + end + defp monitor_connection(_datapath_id, _aux_id), do: nil + + defp send_hello(state_data) do + @supported_version + |> Openflow.Hello.new + |> send_message(state_data) + end + + defp send_features(xid, state_data) do + %{Openflow.Features.Request.new|xid: xid} + |> send_message(state_data) + end + + defp send_echo_reply(xid, data, state_data) do + %{Openflow.Echo.Reply.new(data)|xid: xid} + |> send_message(state_data) + end + + defp send_echo_request(xid, data, state_data) do + %{Openflow.Echo.Request.new(data)|xid: xid} + |> send_message(state_data) + end + + defp start_periodic_idle_check do + :erlang.send_after(@ping_interval, self(), :idle_check) + end + + defp maybe_ping(state_data) do + case should_be_ping?(state_data) do + true -> send_ping(state_data) + false -> state_data + end + end + + defp should_be_ping?(%SecureChannelState{last_received: last_received, aux_id: 0}) do + :timer.now_diff(:os.timestamp(), last_received) > (1000 * @ping_interval) + end + defp should_be_ping?(_) do + false + end + + defp send_ping(%SecureChannelState{xid: x_agent} = state_data) do + xid = SecureChannelState.increment_transaction_id(x_agent) + send_echo_request(xid, "", state_data) + ping_ref = :erlang.send_after(@ping_timeout, self(), :ping_timeout) + %{state_data|ping_timer_ref: ping_ref, ping_xid: xid} + end + + defp handle_ping_timeout(%SecureChannelState{ping_fail_count: fail_count} = state_data, :CONNECTING) + when fail_count > @ping_fail_max_count do + {:next_state, :WAITING, state_data} + end + defp handle_ping_timeout(%SecureChannelState{ping_fail_count: fail_count} = state_data, :WAITING) + when fail_count > @ping_fail_max_count do + close_connection(:ping_failed, state_data) + end + defp handle_ping_timeout(state_data, _) do + new_state_data = maybe_ping(state_data) + {:keep_state, new_state_data} + end + + defp handle_ping_reply(state_data) do + {:keep_state, %{state_data|ping_timer_ref: nil, ping_xid: nil}} + end + + defp send_message(message, %SecureChannelState{socket: socket, transport: transport}) do + debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + Tres.Utils.send_message(message, socket, transport) + end + + defp maybe_cancel_timer(ref) when not is_reference(ref), do: :ok + defp maybe_cancel_timer(ref) do + :erlang.cancel_timer(ref) + :ok + end + + defp handle_signal({:'DOWN', mon_ref, :process, _main_pid, reason}, + %SecureChannelState{main_monitor_ref: mon_ref} = state_data) do + close_connection({:main_closed, reason}, state_data) + end + defp handle_signal({:'DOWN', handler_ref, :process, _main_pid, reason}, + %SecureChannelState{handler_ref: handler_ref} = state_data) do + close_connection({:handler_down, reason}, state_data) + end + defp handle_signal({:'EXIT', _pid, reason}, state_data) do + close_connection({:trap_detected, reason}, state_data) + end + + defp close_connection(:failed_version_negotiation, state_data) do + warn("[#{__MODULE__}] connection terminated: Version negotiation failed") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:hello_handshake_timeout, state_data) do + warn("[#{__MODULE__}] connection terminated: Hello handshake timed out") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:features_timeout, state_data) do + warn("[#{__MODULE__}] connection terminated: Features handshake timed out") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:handler_error, state_data) do + warn("[#{__MODULE__}] connection terminated: Got handler error") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:ping_failed, state_data) do + warn("[#{__MODULE__}] connection terminated: Exceeded to max_ping_fail_count") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection({:main_closed, reason}, state_data) do + warn("[#{__MODULE__}] connection terminated: Main connection down by #{reason}") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection({:handler_down, reason}, state_data) do + warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection({:trap_detected, reason}, state_data) do + warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:tcp_closed, state_data) do + warn("[#{__MODULE__}] connection terminated: TCP Closed by peer") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection({:tcp_error, reason}, state_data) do + warn("[#{__MODULE__}] connection terminated: TCP Error occured: #{reason}") + {:stop, :normal, %{state_data|socket: nil}} + end +end diff --git a/lib/tres/secure_channel_state.ex b/lib/tres/secure_channel_state.ex new file mode 100644 index 0000000..f2c163d --- /dev/null +++ b/lib/tres/secure_channel_state.ex @@ -0,0 +1,51 @@ +defmodule Tres.SecureChannelState do + defstruct( + handler_pid: nil, + handler_ref: nil, + ref: nil, + socket: nil, + transport: nil, + buffer: "", + ip_addr: nil, + port: "", + datapath_id: "", + aux_id: "", + timer_ref: nil, + xid: nil, + main_monitor_ref: nil, + ping_xid: 0, + ping_timer_ref: nil, + ping_fail_count: 0, + last_received: 0 + ) + + alias __MODULE__ + + def new(options) do + ref = Keyword.get(options, :ref) + socket = Keyword.get(options, :socket) + transport = Keyword.get(options, :transport) + {:ok, {ip_addr, port}} = :inet.peername(socket) + {:ok, xid_agent} = Agent.start_link(fn -> 0 end) + %SecureChannelState{ + ref: ref, + socket: socket, + transport: transport, + ip_addr: :inet.ntoa(ip_addr), + port: port, + xid: xid_agent + } + end + + def increment_transaction_id(xid_agent) do + Agent.get_and_update(xid_agent, &({&1 + 1, &1 + 1})) + end + + def set_transaction_id(xid_agent, xid) do + Agent.update(xid_agent, fn(_) -> xid end) + end + + def get_transaction_id(xid_agent) do + Agent.get(xid_agent, &(&1)) + end +end diff --git a/lib/tres/switch_registry.ex b/lib/tres/switch_registry.ex new file mode 100644 index 0000000..f7b8a19 --- /dev/null +++ b/lib/tres/switch_registry.ex @@ -0,0 +1,32 @@ +defmodule Tres.SwitchRegistry do + def register({_dpid, _aux_id} = datapath_id) do + {:ok, _} = Registry.register(__MODULE__, datapath_id, []) + end + + def unregister({_dpid, _aux_id} = datapath_id) do + :ok = Registry.unregister(__MODULE__, datapath_id) + end + + def lookup_pid({_dpid, _aux_id} = datapath_id) do + case Registry.lookup(__MODULE__, datapath_id) do + [{pid, _}] -> pid + [] -> nil + end + end + def lookup_pid(datapath_id) do + lookup_pid({datapath_id, 0}) + end + + def send_message(message, {_dpid, _aux_id} = datapath_id) do + Registry.dispatch(__MODULE__, datapath_id, &dispatch(&1, message)) + end + def send_message(message, dpid) when is_binary(dpid) do + send_message(message, {dpid, 0}) + end + + # private function + + defp dispatch(entries, message) do + for {pid, _} <- entries, do: :gen_statem.cast(pid, {:send_message, message}) + end +end diff --git a/lib/tres/utils.ex b/lib/tres/utils.ex new file mode 100644 index 0000000..358e44f --- /dev/null +++ b/lib/tres/utils.ex @@ -0,0 +1,26 @@ +defmodule Tres.Utils do + import Logger + + @connection_manager Tres.SecureChannel + @default_max_connections 10 + @default_num_acceptors 10 + @default_openflow_port 6633 + + def start_openflow_listener do + max_connections = Application.get_env(:tres, :max_connections, @default_max_connections) + num_acceptors = Application.get_env(:tres, :num_acceptors, @default_num_acceptors) + port = Application.get_env(:tres, :port, @default_openflow_port) + options = [max_connections: max_connections, num_acceptors: num_acceptors, port: port] + :ranch.start_listener(Tres, :ranch_tcp, options, @connection_manager, []) + end + + def send_message(message, socket, transport) do + try do + packet = Openflow.to_binary(message) + transport.send(socket, packet) + catch + _ -> + error("[#{__MODULE__}] Unencodable error: #{inspect(message)}") + end + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..7a68614 --- /dev/null +++ b/mix.exs @@ -0,0 +1,24 @@ +defmodule Tres.Mixfile do + use Mix.Project + + def project do + [app: :tres, + version: "0.1.0", + elixir: "~> 1.5", + start_permanent: Mix.env == :prod, + deps: deps()] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [extra_applications: [:logger, :ranch], + mod: {Tres.Application, []}] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [{:ranch, "~> 1.4.0"}, + {:binpp, github: "jtendo/binpp", branch: "master"}, + {:pkt, github: "msantos/pkt", ref: "3afb196"}] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..e371c67 --- /dev/null +++ b/mix.lock @@ -0,0 +1,6 @@ +%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, + "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_state_machine": {:hex, :gen_state_machine, "2.0.1", "85efd5a0376929c3a4246dd943e17564a2908c7ddd7acd242d84594e785d83f8", [], [], "hexpm"}, + "pkt": {:git, "https://github.com/msantos/pkt.git", "3afb1967f34324c1dec5035a6e36232da815c2e6", [ref: "3afb196"]}, + "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"}} diff --git a/test/ofp_action_test.exs b/test/ofp_action_test.exs new file mode 100644 index 0000000..061d036 --- /dev/null +++ b/test/ofp_action_test.exs @@ -0,0 +1,566 @@ +defmodule OfpActionTest do + use ExUnit.Case + doctest Openflow + + test "Openflow.Action.NxBundle" do + test_file = "test/packet_data/nx_bundle.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + bundle = + Openflow.Action.NxBundle.new( + algorithm: :highest_random_weight, + slaves: [4, 8] + ) + actions_bin = Openflow.Action.to_binary(bundle) + assert actions_bin == packet + assert actions == [bundle] + end + + test "Openflow.Action.NxBundleLoad" do + test_file = "test/packet_data/nx_bundle_load.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + bundle_load = + Openflow.Action.NxBundleLoad.new( + algorithm: :highest_random_weight, + slaves: [4, 8], + dst_field: :reg0 + ) + actions_bin = Openflow.Action.to_binary(bundle_load) + assert actions_bin == packet + assert actions == [bundle_load] + end + + test "Openflow.Action.NxController" do + test_file = "test/packet_data/nx_controller.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + controller = Openflow.Action.NxController.new( + max_len: 1234, + reason: :invalid_ttl, + id: 5678 + ) + actions_bin = Openflow.Action.to_binary(controller) + assert actions_bin == packet + assert actions == [controller] + end + + test "Openflow.Action.NxController2" do + test_file = "test/packet_data/nx_controller2.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + controller2 = Openflow.Action.NxController2.new( + max_len: 1234, + reason: :invalid_ttl, + userdata: <<1,2,3,4,5>>, + pause: true + ) + assert actions == [controller2] + end + + describe "Openflow.Action.NxConntrack" do + test "with ct(alg=ftp)" do + test_file = "test/packet_data/nx_ct(alg=ftp).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(alg: 21) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(alg=tftp)" do + test_file = "test/packet_data/nx_ct(alg=tftp).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(alg: 69) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit)" do + test_file = "test/packet_data/nx_ct(commit).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(flags: [:commit]) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,force)" do + test_file = "test/packet_data/nx_ct(commit, force).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(flags: [:commit, :force]) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63]))" do + test_file = + "test/packet_data/nx_ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxRegLoad.new(dst_field: :ct_label, value: 0, offset: 64, n_bits: 64), + Openflow.Action.NxRegLoad.new(dst_field: :ct_label, value: 0x1d, n_bits: 64)] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[]))" do + test_file = "test/packet_data/nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxRegLoad.new(dst_field: :ct_mark, value: 0xf009)] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[]))" do + test_file = "test/packet_data/nx_ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit, :force], + exec: [Openflow.Action.NxRegLoad.new(dst_field: :ct_mark, value: 0xf009)] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(dst))" do + test_file = "test/packet_data/nx_ct(commit,nat(dst)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxNat.new(flags: [:dst])] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash))" do + test_file = "test/packet_data/nx_ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv4_min} = :inet.parse_ipv4_address('10.0.0.128') + {:ok, ipv4_max} = :inet.parse_ipv4_address('10.0.0.254') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:dst, :protocol_hash], + ipv4_min: ipv4_min, + ipv4_max: ipv4_max + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src))" do + test_file = "test/packet_data/nx_ct(commit,nat(src)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxNat.new(flags: [:src])] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=10.0.0.240,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=10.0.0.240,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv4_min} = :inet.parse_ipv4_address('10.0.0.240') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :protocol_random], + ipv4_min: ipv4_min + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv4_min} = :inet.parse_ipv4_address('10.0.0.240') + {:ok, ipv4_max} = :inet.parse_ipv4_address('10.0.0.254') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :persistent], + ipv4_min: ipv4_min, + ipv4_max: ipv4_max, + proto_min: 32_768, + proto_max: 65_535 + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=10.0.0.240:32768-65535,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=10.0.0.240:32768-65535,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv4_min} = :inet.parse_ipv4_address('10.0.0.240') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :protocol_random], + ipv4_min: ipv4_min, + proto_min: 32_768, + proto_max: 65_535 + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv6_min} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:1') + {:ok, ipv6_max} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:a18b') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :protocol_random], + ipv6_min: ipv6_min, + ipv6_max: ipv6_max, + proto_min: 255, + proto_max: 4096 + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv6_min} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:1') + {:ok, ipv6_max} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:a18b') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :protocol_random], + ipv6_min: ipv6_min, + ipv6_max: ipv6_max + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv6_min} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:a18b') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxNat.new(flags: [:src, :protocol_random], ipv6_min: ipv6_min)] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(nat)" do + test_file = "test/packet_data/nx_ct(nat).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(exec: [Openflow.Action.NxNat.new]) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(table=10)" do + test_file = "test/packet_data/nx_ct(table=10).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(recirc_table: 10) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(zone=10)" do + test_file = "test/packet_data/nx_ct(zone=10).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(zone_imm: 10) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(zone=NXM_NX_REG0[0..15])" do + test_file = "test/packet_data/nx_ct(zone=NXM_NX_REG0[0..15]).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(zone_src: :reg0, zone_offset: 0, zone_n_bits: 16) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct" do + test_file = "test/packet_data/nx_ct.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct_clear" do + test_file = "test/packet_data/nx_ct_clear.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxCtClear.new + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with dec_ttl" do + test_file = "test/packet_data/nx_dec_ttl.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + dec_ttl = Openflow.Action.NxDecTtl.new + actions_bin = Openflow.Action.to_binary(dec_ttl) + assert actions_bin == packet + assert actions == [dec_ttl] + end + + test "with dec_ttl_cnt_ids" do + test_file = "test/packet_data/nx_dec_ttl_cnt_ids.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + dec_ttl = Openflow.Action.NxDecTtlCntIds.new([32_768, 12_345, 90, 765, 1024]) + actions_bin = Openflow.Action.to_binary(dec_ttl) + assert actions_bin == packet + assert actions == [dec_ttl] + end + + test "with exit" do + test_file = "test/packet_data/nx_exit.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + exit = Openflow.Action.NxExit.new + actions_bin = Openflow.Action.to_binary(exit) + assert actions_bin == packet + assert actions == [exit] + end + + test "with fin_timeout" do + test_file = "test/packet_data/nx_fin_timeout.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + fin_timeout = Openflow.Action.NxFinTimeout.new(idle_timeout: 10, hard_timeout: 20) + actions_bin = Openflow.Action.to_binary(fin_timeout) + assert actions_bin == packet + assert actions == [fin_timeout] + end + + test "with learn" do + test_file = "test/packet_data/nx_learn.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + learn = Openflow.Action.NxLearn.new( + idle_timeout: 10, + hard_timeout: 20, + priority: 80, + cookie: 0x123456789abcdef0, + flags: [], + table_id: 2, + fin_idle_timeout: 2, + fin_hard_timeout: 4, + flow_specs: [ + Openflow.Action.NxFlowSpecMatch.new(src: :nx_vlan_tci, dst: :nx_vlan_tci, n_bits: 12), + Openflow.Action.NxFlowSpecMatch.new(src: :nx_eth_src, dst: :nx_eth_dst), + Openflow.Action.NxFlowSpecOutput.new(src: :nx_in_port) + ] + ) + actions_bin = Openflow.Action.to_binary(learn) + assert actions_bin == packet + assert actions == [learn] + end + + test "with learn2" do + test_file = "test/packet_data/nx_learn2.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + learn2 = Openflow.Action.NxLearn2.new( + idle_timeout: 10, + hard_timeout: 20, + priority: 80, + cookie: 0x123456789abcdef0, + flags: [:write_result], + table_id: 2, + fin_idle_timeout: 2, + fin_hard_timeout: 4, + limit: 1, + result_dst: :reg0, + result_dst_offset: 8, + flow_specs: [ + Openflow.Action.NxFlowSpecMatch.new(src: :nx_vlan_tci, dst: :nx_vlan_tci, n_bits: 12), + Openflow.Action.NxFlowSpecMatch.new(src: :nx_eth_src, dst: :nx_eth_dst), + Openflow.Action.NxFlowSpecOutput.new(src: :nx_in_port) + ] + ) + actions_bin = Openflow.Action.to_binary(learn2) + assert actions_bin == packet + assert actions == [learn2] + end + + test "with multipath" do + test_file = "test/packet_data/nx_multipath.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + multipath = Openflow.Action.NxMultipath.new( + algorithm: :modulo_n, + basis: 50, + dst_field: :reg0 + ) + actions_bin = Openflow.Action.to_binary(multipath) + assert actions_bin == packet + assert actions == [multipath] + end + + test "with note" do + test_file = "test/packet_data/nx_note.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + note = Openflow.Action.NxNote.new(<<0x11, 0xe9, 0x9a, 0xad, 0x67, 0xf3>>) + actions_bin = Openflow.Action.to_binary(note) + assert actions_bin == packet + assert actions == [note] + end + + test "with output_reg" do + test_file = "test/packet_data/nx_output_reg.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + output_reg = Openflow.Action.NxOutputReg.new(src_field: :reg1, n_bits: 6, offset: 5) + actions_bin = Openflow.Action.to_binary(output_reg) + assert actions_bin == packet + assert actions == [output_reg] + end + + test "with output_trunc" do + test_file = "test/packet_data/nx_output_trunc.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + output_trunc = Openflow.Action.NxOutputTrunc.new(port_number: 1, max_len: 100) + actions_bin = Openflow.Action.to_binary(output_trunc) + assert actions_bin == packet + assert actions == [output_trunc] + end + + test "with pop_queue" do + test_file = "test/packet_data/nx_pop_queue.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + pop_queue = Openflow.Action.NxPopQueue.new + actions_bin = Openflow.Action.to_binary(pop_queue) + assert actions_bin == packet + assert actions == [pop_queue] + end + + test "with reg_load" do + test_file = "test/packet_data/nx_reg_load.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + reg_load = Openflow.Action.NxRegLoad.new(dst_field: :nx_vlan_tci, value: 0xf009) + actions_bin = Openflow.Action.to_binary(reg_load) + assert actions_bin == packet + assert actions == [reg_load] + end + + test "with reg_move" do + test_file = "test/packet_data/nx_reg_move.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + reg_move = Openflow.Action.NxRegMove.new(src_field: :nx_in_port, dst_field: :nx_vlan_tci) + actions_bin = Openflow.Action.to_binary(reg_move) + assert actions_bin == packet + assert actions == [reg_move] + end + + test "with resubmit" do + test_file = "test/packet_data/nx_resubmit.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + resubmit = Openflow.Action.NxResubmit.new(5) + actions_bin = Openflow.Action.to_binary(resubmit) + assert actions_bin == packet + assert actions == [resubmit] + end + + test "with resubmit_table" do + test_file = "test/packet_data/nx_resubmit_table.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + resubmit_table = Openflow.Action.NxResubmitTable.new(in_port: 10, table_id: 5) + actions_bin = Openflow.Action.to_binary(resubmit_table) + assert actions_bin == packet + assert actions == [resubmit_table] + end + + test "with resubmit_table_ct" do + test_file = "test/packet_data/nx_resubmit_table_ct.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + resubmit_table_ct = Openflow.Action.NxResubmitTableCt.new(in_port: 10, table_id: 5) + actions_bin = Openflow.Action.to_binary(resubmit_table_ct) + assert actions_bin == packet + assert actions == [resubmit_table_ct] + end + end +end diff --git a/test/ofp_echo_test.exs b/test/ofp_echo_test.exs new file mode 100644 index 0000000..13aa181 --- /dev/null +++ b/test/ofp_echo_test.exs @@ -0,0 +1,52 @@ +defmodule OfpEchoTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_ECHO_REQUEST packet" do + {:ok, %Openflow.Echo.Request{} = echo, ""} = + "test/packet_data/ofp_echo_request.raw" + |> File.read! + |> Openflow.read + assert echo.version == 4 + assert echo.xid == 0 + assert echo.data == "" + end + + test "with OFP_ECHO_REPLY packet" do + {:ok, %Openflow.Echo.Reply{} = echo, ""} = + "test/packet_data/ofp_echo_reply.raw" + |> File.read! + |> Openflow.read + assert echo.version == 4 + assert echo.xid == 0 + assert echo.data == "" + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.Echo.Request{}" do + echo = %Openflow.Echo.Request{ + version: 4, + xid: 0, + data: "" + } + expect = + "test/packet_data/ofp_echo_request.raw" + |> File.read! + assert Openflow.to_binary(echo) == expect + end + + test "with %Openflow.Echo.Reply{}" do + echo = %Openflow.Echo.Reply{ + version: 4, + xid: 0, + data: "" + } + expect = + "test/packet_data/ofp_echo_reply.raw" + |> File.read! + assert Openflow.to_binary(echo) == expect + end + end +end diff --git a/test/ofp_error_test.exs b/test/ofp_error_test.exs new file mode 100644 index 0000000..a7c9de1 --- /dev/null +++ b/test/ofp_error_test.exs @@ -0,0 +1,56 @@ +defmodule OfpErrorTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_ERROR packet" do + {:ok, error, ""} = + "test/packet_data/ofp_error.raw" + |> File.read! + |> Openflow.read + assert error.version == 4 + assert error.xid == 0 + assert error.type == :bad_action + assert error.code == :unsupported_order + assert error.data == "fugafuga" + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.Error{}" do + error = %Openflow.ErrorMsg{ + version: 4, + xid: 0, + type: :bad_action, + code: :unsupported_order, + data: "fugafuga", + } + + expect = + "test/packet_data/ofp_error.raw" + |> File.read! + + assert Openflow.to_binary(error) == expect + end + + test "with experimenter %Openflow.Error{}" do + error = %Openflow.ErrorMsg{ + version: 4, + xid: 0, + type: :experimenter, + exp_type: 1, + experimenter: 0xdeadbeef, + data: "hogehoge", + } + + expect = << + 4, 1, 0, 24, 0, 0, 0, + 0, 255, 255, 0, 1, 222, + 173, 190, 239, 104, 111, 103, + 101, 104, 111, 103, 101 + >> + + assert Openflow.to_binary(error) == expect + end + end +end diff --git a/test/ofp_features_test.exs b/test/ofp_features_test.exs new file mode 100644 index 0000000..b03960c --- /dev/null +++ b/test/ofp_features_test.exs @@ -0,0 +1,58 @@ +defmodule OfpFeaturesTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_FEATURES_REQUEST packet" do + {:ok, %Openflow.Features.Request{} = features, ""} = + "test/packet_data/ofp_features_request.raw" + |> File.read! + |> Openflow.read + assert features.version == 4 + assert features.xid == 0 + end + + test "with OFP_FEATURES_REPLY packet" do + {:ok, %Openflow.Features.Reply{} = features, ""} = + "test/packet_data/ofp_features_reply.raw" + |> File.read! + |> Openflow.read + assert features.version == 4 + assert features.xid == 0 + assert features.datapath_id == "0000000000000001" + assert features.n_buffers == 255 + assert features.n_tables == 255 + assert features.aux_id == 0 + assert features.capabilities == [:flow_stats, :table_stats, :port_stats, :group_stats, :queue_stats] + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.Features.Request{}" do + features = %Openflow.Features.Request{ + version: 4, + xid: 0 + } + expect = + "test/packet_data/ofp_features_request.raw" + |> File.read! + assert Openflow.to_binary(features) == expect + end + end + + test "with %Openflow.Features.Reply{}" do + features = %Openflow.Features.Reply{ + version: 4, + xid: 0, + datapath_id: "0000000000000001", + n_buffers: 255, + n_tables: 255, + aux_id: 0, + capabilities: [:flow_stats, :table_stats, :port_stats, :group_stats, :queue_stats], + } + expect = + "test/packet_data/ofp_features_reply.raw" + |> File.read! + assert Openflow.to_binary(features) == expect + end +end diff --git a/test/ofp_flow_mod_test.exs b/test/ofp_flow_mod_test.exs new file mode 100644 index 0000000..81661a2 --- /dev/null +++ b/test/ofp_flow_mod_test.exs @@ -0,0 +1,280 @@ +defmodule OfpFlowModTest do + use ExUnit.Case + doctest Openflow + + @flow_mod1 "test/packet_data/4-2-ofp_flow_mod.packet" + @flow_mod2 "test/packet_data/4-3-ofp_flow_mod.packet" + @flow_mod3 "test/packet_data/4-46-ofp_flow_mod.packet" + @flow_mod4 "test/packet_data/4-60-ofp_flow_mod.packet" + @flow_mod5 "test/packet_data/libofproto-OFP13-flow_mod.packet" + @flow_mod6 "test/packet_data/libofproto-OFP13-flow_mod.truncated64" + @flow_mod7 "test/packet_data/libofproto-OFP13-flow_mod_conjunction.packet" + @flow_mod8 "test/packet_data/libofproto-OFP13-flow_mod_match_conj.packet" + + describe "Openflow.read/1" do + test "with OFP_FLOW_MOD packet(1)" do + binary = File.read!(@flow_mod1) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0 + assert fm.cookie_mask == 0 + assert fm.table_id == 1 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 123 + assert fm.buffer_id == 0xffff + assert fm.out_port == :any + assert fm.out_group == :any + assert fm.flags == [] + assert fm.match == Openflow.Match.new(eth_dst: "f20ba47df8ea") + assert fm.instructions == [ + Openflow.Instruction.WriteActions.new([ + Openflow.Action.SetField.new({:vlan_vid, 258}), + Openflow.Action.CopyTtlOut.new, + Openflow.Action.CopyTtlIn.new, + Openflow.Action.CopyTtlIn.new, + Openflow.Action.PopPbb.new, + Openflow.Action.PushPbb.new(4660), + Openflow.Action.PopMpls.new(39030), + Openflow.Action.PushMpls.new(34887), + Openflow.Action.PopVlan.new, + Openflow.Action.PushVlan.new(33024), + Openflow.Action.DecMplsTtl.new, + Openflow.Action.SetMplsTtl.new(10), + Openflow.Action.DecNwTtl.new, + Openflow.Action.SetNwTtl.new(10), + Openflow.Action.Experimenter.new(101, <<0, 1, 2, 3, 4, 5, 6, 7>>), + Openflow.Action.SetQueue.new(3), + Openflow.Action.Group.new(99), + Openflow.Action.Output.new(6) + ]), + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.SetField.new({:eth_src, "010203040506"}), + Openflow.Action.SetField.new({:onf_pbb_uca, 1}) + ]) + ] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(2)" do + binary = File.read!(@flow_mod2) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0 + assert fm.cookie_mask == 0 + assert fm.table_id == 0 + assert fm.cookie == 0 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 123 + assert fm.buffer_id == 0xffff + assert fm.out_port == :any + assert fm.out_group == :any + assert fm.flags == [] + assert fm.match == Openflow.Match.new(in_port: 6, eth_src: "f20ba47df8ea") + assert fm.instructions == [Openflow.Instruction.GotoTable.new(1)] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(3)" do + binary = File.read!(@flow_mod3) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0 + assert fm.cookie_mask == 0 + assert fm.table_id == 1 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 123 + assert fm.buffer_id == 0xffff + assert fm.out_port == :any + assert fm.out_group == :any + assert fm.flags == [] + assert fm.match == Openflow.Match.new(eth_dst: "f20ba47df8ea") + assert fm.instructions == [ + Openflow.Instruction.Meter.new(1), + Openflow.Instruction.WriteActions.new([Openflow.Action.Output.new(6)]) + ] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(4)" do + binary = File.read!(@flow_mod4) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0 + assert fm.cookie_mask == 0 + assert fm.table_id == 1 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 123 + assert fm.buffer_id == 0xffff + assert fm.out_port == :any + assert fm.out_group == :any + assert fm.flags == [] + assert fm.match == Openflow.Match.new( + in_port: 84_281_096, + in_phy_port: 16_909_060, + metadata: 283_686_952_306_183, + eth_type: 2054, + eth_dst: "ffffffffffff", + eth_src: "f20ba47df8ea", + vlan_vid: 999, + ip_dscp: 9, + ip_ecn: 3, + ip_proto: 99, + ipv4_src: {1, 2, 3, 4}, + ipv4_dst: {1, 2, 3, 4}, + tcp_src: 8080, + tcp_dst: 18_080, + udp_src: 28_080, + udp_dst: 55_936, + sctp_src: 48_080, + sctp_dst: 59_328, + icmpv4_type: 100, + icmpv4_code: 101, + arp_op: 1, + arp_spa: {10, 0, 0, 1}, + arp_tpa: {10, 0, 0, 3}, + arp_sha: "f20ba47df8ea", + arp_tha: "000000000000", + ipv6_src: {65152, 0, 0, 0, 61451, 42239, 65096, 10405}, + ipv6_dst: {65152, 0, 0, 0, 61451, 42239, 65029, 47068}, + ipv6_flabel: 541_473, + icmpv6_type: 200, + icmpv6_code: 201, + ipv6_nd_target: {65152, 0, 0, 0, 2656, 28415, 65151, 29927}, + ipv6_nd_sll: "00000000029a", + ipv6_nd_tll: "00000000022b", + mpls_label: 624_485, + mpls_tc: 5, + mpls_bos: 1, + pbb_isid: 11_259_375, + tunnel_id: 651061555542690057, + ipv6_exthdr: [:auth, :frag, :router, :hop, :unrep, :unseq], + onf_pbb_uca: 1, + tun_src: {1, 2, 3, 4}, + tun_dst: {1, 2, 3, 4} + ) + assert fm.instructions == [] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(5)" do + binary = File.read!(@flow_mod5) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0x123456789abcdef0 + assert fm.cookie_mask == 0xffffffffffffffff + assert fm.table_id == 2 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 0 + assert fm.buffer_id == 0 + assert fm.out_port == 0 + assert fm.out_group == 0 + assert fm.flags == [] + assert fm.match == Openflow.Match.new( + in_port: 43981, + eth_dst: "aabbcc998877", + eth_type: 2048, + vlan_vid: 5095, + ipv4_dst: {192, 168, 2, 1}, + tunnel_id: 50_000, + tun_src: {192, 168, 2, 3}, + tun_dst: {192, 168, 2, 4} + ) + assert fm.instructions == [ + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:ipv4_dst, {192, 168, 2, 9}}), + Openflow.Action.NxLearn.new( + hard_timeout: 300, + priority: 1, + table_id: 99, + flow_specs: [ + Openflow.Action.NxFlowSpecMatch.new(src: :vlan_vid, dst: :vlan_vid, n_bits: 12), + Openflow.Action.NxFlowSpecMatch.new(src: :nx_eth_src, dst: :nx_eth_dst), + Openflow.Action.NxFlowSpecLoad.new(src: 0, dst: :vlan_vid, n_bits: 12), + Openflow.Action.NxFlowSpecLoad.new(src: :tun_id, dst: :tun_id), + Openflow.Action.NxFlowSpecOutput.new(src: :in_port) + ] + ) + ]), + Openflow.Instruction.GotoTable.new(100) + ] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(6)" do\ + {:error, :binary_too_small} = + @flow_mod6 + |> File.read! + |> Openflow.read + end + + test "with OFP_FLOW_MOD packet(7)" do + binary = File.read!(@flow_mod7) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0x123456789abcdef0 + assert fm.cookie_mask == 0xffffffffffffffff + assert fm.table_id == 4 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 0 + assert fm.buffer_id == 0 + assert fm.out_port == 0 + assert fm.out_group == 0 + assert fm.flags == [] + assert fm.match == Openflow.Match.new( + in_port: 43981, + eth_dst: "aabbcc998877", + eth_type: 2048, + vlan_vid: 5095, + ipv4_dst: {192, 168, 2, 1}, + tunnel_id: 50_000, + tun_src: {192, 168, 2, 3}, + tun_dst: {192, 168, 2, 4} + ) + assert fm.instructions == [ + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.NxConjunction.new(clause: 1, id: 0xabcdef, n_clauses: 2) + ]) + ] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(8)" do + binary = File.read!(@flow_mod8) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0x123456789abcdef0 + assert fm.cookie_mask == 0xffffffffffffffff + assert fm.table_id == 3 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 0 + assert fm.buffer_id == 0 + assert fm.out_port == 0 + assert fm.out_group == 0 + assert fm.flags == [] + assert fm.match == Openflow.Match.new(conj_id: 0xabcdef) + assert fm.instructions == [ + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:ipv4_dst, {192, 168, 2, 9}}) + ]), + Openflow.Instruction.GotoTable.new(100) + ] + assert Openflow.to_binary(fm) == binary + end + end +end diff --git a/test/ofp_flow_removed_test.exs b/test/ofp_flow_removed_test.exs new file mode 100644 index 0000000..4a01473 --- /dev/null +++ b/test/ofp_flow_removed_test.exs @@ -0,0 +1,57 @@ +defmodule OfpFlowRemovedTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_FLOW_REMOVED packet(with a match field)" do + {:ok, flow_removed, ""} = + "test/packet_data/4-40-ofp_flow_removed.packet" + |> File.read! + |> Openflow.read + + assert flow_removed.version == 4 + assert flow_removed.xid == 0 + assert flow_removed.cookie == 0 + assert flow_removed.priority == 0xffff + assert flow_removed.reason == :idle_timeout + assert flow_removed.table_id == 0 + assert flow_removed.duration_sec == 3 + assert flow_removed.duration_nsec == 48_825_000 + assert flow_removed.idle_timeout == 3 + assert flow_removed.hard_timeout == 0 + assert flow_removed.packet_count == 1 + assert flow_removed.byte_count == 86 + assert flow_removed.match == [eth_dst: "f20ba47df8ea"] + end + + test "with OFP_FLOW_REMOVED packet(with match fields)" do + {:ok, flow_removed, ""} = + "test/packet_data/libofproto-OFP13-flow_removed.packet" + |> File.read! + |> Openflow.read + + assert flow_removed.version == 4 + assert flow_removed.xid == 0 + assert flow_removed.cookie == 0x123456789abcdef0 + assert flow_removed.priority == 100 + assert flow_removed.reason == :idle_timeout + assert flow_removed.table_id == 1 + assert flow_removed.duration_sec == 600 + assert flow_removed.duration_nsec == 500 + assert flow_removed.idle_timeout == 400 + assert flow_removed.hard_timeout == 300 + assert flow_removed.packet_count == 200 + assert flow_removed.byte_count == 100 + assert flow_removed.match == [ + in_port: 43_981, + eth_dst: "aabbcc998877", + eth_type: 2048, + vlan_vid: 5095, + ipv4_dst: {192, 168, 2, 1}, + tunnel_id: 50_000, + tun_src: {192, 168, 2, 3}, + tun_dst: {192, 168, 2, 4} + ] + end + end +end diff --git a/test/ofp_get_config_test.exs b/test/ofp_get_config_test.exs new file mode 100644 index 0000000..524e99f --- /dev/null +++ b/test/ofp_get_config_test.exs @@ -0,0 +1,52 @@ +defmodule OfpGetConfigTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_GET_CONFIG_REQUEST packet" do + {:ok, %Openflow.GetConfig.Request{} = config, ""} = + "test/packet_data/ofp_get_config_request.raw" + |> File.read! + |> Openflow.read + assert config.version == 4 + assert config.xid == 0 + end + + test "with OFP_GET_CONFIG_REPLY packet" do + {:ok, %Openflow.GetConfig.Reply{} = config, ""} = + "test/packet_data/ofp_get_config_reply.raw" + |> File.read! + |> Openflow.read + assert config.version == 4 + assert config.xid == 0 + assert config.flags == [] + assert config.miss_send_len == 128 + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.GetConfig.Request{}" do + config = %Openflow.GetConfig.Request{ + version: 4, + xid: 0 + } + expect = + "test/packet_data/ofp_get_config_request.raw" + |> File.read! + assert Openflow.to_binary(config) == expect + end + + test "with %Openflow.GetConfig.Reply{}" do + config = %Openflow.GetConfig.Reply{ + version: 4, + xid: 0, + flags: [], + miss_send_len: 128 + } + expect = + "test/packet_data/ofp_get_config_reply.raw" + |> File.read! + assert Openflow.to_binary(config) == expect + end + end +end diff --git a/test/ofp_group_mod_test.exs b/test/ofp_group_mod_test.exs new file mode 100644 index 0000000..cdf7e04 --- /dev/null +++ b/test/ofp_group_mod_test.exs @@ -0,0 +1,26 @@ +defmodule OfpGroupModTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_GROUP_MOD packet" do + binary = File.read!("test/packet_data/4-21-ofp_group_mod.packet") + {:ok, group_mod, ""} = Openflow.read(binary) + + assert group_mod.version == 4 + assert group_mod.xid == 0 + assert group_mod.command == :add + assert group_mod.type == :all + assert group_mod.group_id == 1 + assert group_mod.buckets == [ + Openflow.Bucket.new( + weight: 1, + watch_port: 1, + watch_group: 1, + actions: [Openflow.Action.Output.new(2)] + ) + ] + assert Openflow.to_binary(group_mod) == binary + end + end +end diff --git a/test/ofp_hello_test.exs b/test/ofp_hello_test.exs new file mode 100644 index 0000000..0fd4b1e --- /dev/null +++ b/test/ofp_hello_test.exs @@ -0,0 +1,26 @@ +defmodule OfpHelloTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_HELLO packet" do + {:ok, hello, ""} = + "test/packet_data/ofp_hello.raw" + |> File.read! + |> Openflow.read + assert hello.version == 4 + assert hello.xid == 0 + assert hello.elements == [versionbitmap: [30, 10, 9, 3, 2, 1]] + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.Hello{}" do + hello = Openflow.Hello.new([30, 10, 9, 3, 2, 1]) + expect = + "test/packet_data/ofp_hello.raw" + |> File.read! + assert Openflow.to_binary(hello) == expect + end + end +end diff --git a/test/ofp_packet_in_test.exs b/test/ofp_packet_in_test.exs new file mode 100644 index 0000000..a59e22e --- /dev/null +++ b/test/ofp_packet_in_test.exs @@ -0,0 +1,87 @@ +defmodule OfpPacketInTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_PACKET_IN packet(with simple matches)" do + {:ok, pktin, ""} = + "test/packet_data/4-4-ofp_packet_in.packet" + |> File.read! + |> Openflow.read + + assert pktin.version == 4 + assert pktin.xid == 0 + assert pktin.total_len == 42 + assert pktin.table_id == 1 + assert pktin.reason == :action + assert pktin.in_port == 6 + assert pktin.match == [ + eth_type: 2054, + eth_dst: "ffffffffffff", + eth_src: "f20ba47df8ea", + arp_op: 1, + arp_spa: {10, 0, 0, 1}, + arp_tpa: {10, 0, 0, 3}, + arp_sha: "f20ba47df8ea", + arp_tha: "000000000000" + ] + end + + test "with OFP_PACKET_IN packet(with complex matches)" do + {:ok, pktin, ""} = + "test/packet_data/4-59-ofp_packet_in.packet" + |> File.read! + |> Openflow.read + + assert pktin.version == 4 + assert pktin.xid == 0 + assert pktin.total_len == 0 + assert pktin.table_id == 200 + assert pktin.reason == :no_match + assert pktin.in_port == 84281096 + assert pktin.match == [ + in_phy_port: 16909060, + metadata: 283686952306183, + eth_type: 2054, + eth_dst: "ffffffffffff", + eth_src: "f20ba47df8ea", + vlan_vid: 999, + ip_dscp: 9, + ip_ecn: 3, + ip_proto: 99, + ipv4_src: {1, 2, 3, 4}, + ipv4_dst: {1, 2, 3, 4}, + tcp_src: 8080, + tcp_dst: 18080, + udp_src: 28080, + udp_dst: 55936, + sctp_src: 48080, + sctp_dst: 59328, + icmpv4_type: 100, + icmpv4_code: 101, + arp_op: 1, + arp_spa: {10, 0, 0, 1}, + arp_tpa: {10, 0, 0, 3}, + arp_sha: "f20ba47df8ea", + arp_tha: "000000000000", + ipv6_src: {65152, 0, 0, 0, 61451, 42239, 65096, 10405}, + ipv6_dst: {65152, 0, 0, 0, 61451, 42239, 65029, 47068}, + ipv6_flabel: 541473, + icmpv6_type: 200, + icmpv6_code: 201, + ipv6_nd_target: {65152, 0, 0, 0, 2656, 28415, 65151, 29927}, + ipv6_nd_sll: "00000000029a", + ipv6_nd_tll: "00000000022b", + mpls_label: 624485, + mpls_tc: 5, + mpls_bos: 1, + pbb_isid: 11259375, + tunnel_id: 651061555542690057, + ipv6_exthdr: [:auth, :frag, :router, :hop, :unrep, :unseq], + onf_pbb_uca: 1, + tun_src: {1, 2, 3, 4}, + tun_dst: {1, 2, 3, 4} + ] + end + end +end diff --git a/test/ofp_packet_out_test.exs b/test/ofp_packet_out_test.exs new file mode 100644 index 0000000..332ba10 --- /dev/null +++ b/test/ofp_packet_out_test.exs @@ -0,0 +1,49 @@ +defmodule OfpPacketOutTest do + use ExUnit.Case + doctest Openflow + + @packet << + 0xf2, 0x0b, 0xa4, 0xd0, 0x3f, 0x70, 0xf2, 0x0b, + 0xa4, 0x7d, 0xf8, 0xea, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x54, 0xf8, 0x1a, 0x00, 0x00, 0xff, 0x01, + 0xaf, 0x8b, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, + 0x00, 0x02, 0x08, 0x00, 0x02, 0x08, 0xf7, 0x60, + 0x00, 0x00, 0x31, 0xd6, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xab, 0x8d, 0x2d, 0x31, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, + 0x2e, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + >> + + describe "Openflow.read/1" do + test "with OFP_PACKET_OUT packet" do + {:ok, pktout, ""} = + "test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet" + |> File.read! + |> Openflow.read + + assert pktout.version == 4 + assert pktout.xid == 0 + assert pktout.buffer_id == :no_buffer + assert pktout.in_port == :controller + assert pktout.actions == [%Openflow.Action.Output{max_len: :no_buffer, port_number: :all}] + assert pktout.data == @packet + end + end + + describe "Openflow.to_binary/1" do + pktout = %Openflow.PacketOut{ + actions: [Openflow.Action.Output.new(port_number: :all)], + data: @packet + } + + expect = + "test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet" + |> File.read! + + assert Openflow.to_binary(pktout) == expect + end +end diff --git a/test/ofp_port_mod_test.exs b/test/ofp_port_mod_test.exs new file mode 100644 index 0000000..e9725d3 --- /dev/null +++ b/test/ofp_port_mod_test.exs @@ -0,0 +1,34 @@ +defmodule OfpPortModTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_PORT_MOD packet(1)" do + binary = File.read!("test/packet_data/4-22-ofp_port_mod.packet") + {:ok, port_mod, ""} = Openflow.read(binary) + + assert port_mod.version == 4 + assert port_mod.xid == 0 + assert port_mod.number == 1 + assert port_mod.hw_addr == "001100001111" + assert port_mod.config == [] + assert port_mod.mask == [] + assert port_mod.advertise == [:fiber] + assert Openflow.to_binary(port_mod) == binary + end + end + + test "with OFP_PORT_MOD packet(2)" do + binary = File.read!("test/packet_data/libofproto-OFP13-port_mod.packet") + {:ok, port_mod, ""} = Openflow.read(binary) + + assert port_mod.version == 4 + assert port_mod.xid == 0 + assert port_mod.number == 1 + assert port_mod.hw_addr == "aabbcc998877" + assert port_mod.config == [:port_down] + assert port_mod.mask == [:port_down] + assert port_mod.advertise == [:"100mb_fd", :copper, :autoneg] + assert Openflow.to_binary(port_mod) == binary + end +end diff --git a/test/ofp_port_status_test.exs b/test/ofp_port_status_test.exs new file mode 100644 index 0000000..fa6b5ea --- /dev/null +++ b/test/ofp_port_status_test.exs @@ -0,0 +1,48 @@ +defmodule OfpPortStatusTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_PORT_STATUS packet" do + {:ok, port_status, ""} = + "test/packet_data/libofproto-OFP13-port_status.packet" + |> File.read! + |> Openflow.read + + assert port_status.version == 4 + assert port_status.xid == 0 + assert port_status.reason == :modify + assert port_status.port.number == 1 + assert port_status.port.hw_addr == "ffffffffffff" + assert port_status.port.name == "eth0" + assert port_status.port.config == [] + assert port_status.port.state == [:live] + assert port_status.port.current_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.advertised_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.peer_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.current_speed == 50_000 + assert port_status.port.max_speed == 100_000 + end + + test "with OFP_PORT_STATUS packet(with kanji port name)" do + {:ok, port_status, ""} = + "test/packet_data/4-39-ofp_port_status.packet" + |> File.read! + |> Openflow.read + + assert port_status.version == 4 + assert port_status.xid == 0 + assert port_status.reason == :add + assert port_status.port.number == 7 + assert port_status.port.hw_addr == "f20ba4d03f70" + assert port_status.port.name == "私のポート" + assert port_status.port.config == [] + assert port_status.port.state == [:live] + assert port_status.port.current_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.advertised_features == [:copper, :autoneg] + assert port_status.port.peer_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.current_speed == 5_000 + assert port_status.port.max_speed == 5_000 + end + end +end diff --git a/test/ofp_set_config_test.exs b/test/ofp_set_config_test.exs new file mode 100644 index 0000000..7266c6f --- /dev/null +++ b/test/ofp_set_config_test.exs @@ -0,0 +1,32 @@ +defmodule OfpSetConfigTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_SET_CONFIG packet" do + {:ok, %Openflow.SetConfig{} = config, ""} = + "test/packet_data/ofp_set_config.raw" + |> File.read! + |> Openflow.read + assert config.version == 4 + assert config.xid == 0 + assert config.flags == [] + assert config.miss_send_len == 128 + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.SetConfig{}" do + config = %Openflow.SetConfig{ + version: 4, + xid: 0, + flags: [], + miss_send_len: 128 + } + expect = + "test/packet_data/ofp_set_config.raw" + |> File.read! + assert Openflow.to_binary(config) == expect + end + end +end diff --git a/test/ofp_table_mod_test.exs b/test/ofp_table_mod_test.exs new file mode 100644 index 0000000..198e6fc --- /dev/null +++ b/test/ofp_table_mod_test.exs @@ -0,0 +1,28 @@ +defmodule OfpTableModTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_TABLE_MOD packet(1)" do + binary = File.read!("test/packet_data/libofproto-OFP13-table_mod.packet") + {:ok, table_mod, ""} = Openflow.read(binary) + + assert table_mod.version == 4 + assert table_mod.xid == 0 + assert table_mod.table_id == :all + assert table_mod.config == 0 + assert Openflow.to_binary(table_mod) == binary + end + end + + test "with OFP_TABLE_MOD packet(2)" do + binary = File.read!("test/packet_data/4-23-ofp_table_mod.packet") + {:ok, table_mod, ""} = Openflow.read(binary) + + assert table_mod.version == 4 + assert table_mod.xid == 0 + assert table_mod.table_id == :all + assert table_mod.config == 0 + assert Openflow.to_binary(table_mod) == binary + end +end diff --git a/test/packet_data/.#nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw b/test/packet_data/.#nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw new file mode 120000 index 0000000..07c9d43 --- /dev/null +++ b/test/packet_data/.#nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw @@ -0,0 +1 @@ +shun159@shun159.19402:1510242034 \ No newline at end of file diff --git a/test/packet_data/4-0-ofp_desc_reply.packet b/test/packet_data/4-0-ofp_desc_reply.packet new file mode 100644 index 0000000..edfaa37 Binary files /dev/null and b/test/packet_data/4-0-ofp_desc_reply.packet differ diff --git a/test/packet_data/4-1-ofp_packet_out.packet b/test/packet_data/4-1-ofp_packet_out.packet new file mode 100644 index 0000000..cf6689f Binary files /dev/null and b/test/packet_data/4-1-ofp_packet_out.packet differ diff --git a/test/packet_data/4-10-ofp_hello.packet b/test/packet_data/4-10-ofp_hello.packet new file mode 100644 index 0000000..644b413 Binary files /dev/null and b/test/packet_data/4-10-ofp_hello.packet differ diff --git a/test/packet_data/4-11-ofp_flow_stats_request.packet b/test/packet_data/4-11-ofp_flow_stats_request.packet new file mode 100644 index 0000000..6b9e0a9 Binary files /dev/null and b/test/packet_data/4-11-ofp_flow_stats_request.packet differ diff --git a/test/packet_data/4-12-ofp_flow_stats_reply.packet b/test/packet_data/4-12-ofp_flow_stats_reply.packet new file mode 100644 index 0000000..c7c1ac8 Binary files /dev/null and b/test/packet_data/4-12-ofp_flow_stats_reply.packet differ diff --git a/test/packet_data/4-13-ofp_echo_request.packet b/test/packet_data/4-13-ofp_echo_request.packet new file mode 100644 index 0000000..d8d8bc4 Binary files /dev/null and b/test/packet_data/4-13-ofp_echo_request.packet differ diff --git a/test/packet_data/4-14-ofp_echo_reply.packet b/test/packet_data/4-14-ofp_echo_reply.packet new file mode 100644 index 0000000..2ac9c0d Binary files /dev/null and b/test/packet_data/4-14-ofp_echo_reply.packet differ diff --git a/test/packet_data/4-15-ofp_error_msg.packet b/test/packet_data/4-15-ofp_error_msg.packet new file mode 100644 index 0000000..196dad1 Binary files /dev/null and b/test/packet_data/4-15-ofp_error_msg.packet differ diff --git a/test/packet_data/4-16-ofp_experimenter.packet b/test/packet_data/4-16-ofp_experimenter.packet new file mode 100644 index 0000000..0b6e2dd Binary files /dev/null and b/test/packet_data/4-16-ofp_experimenter.packet differ diff --git a/test/packet_data/4-17-ofp_barrier_request.packet b/test/packet_data/4-17-ofp_barrier_request.packet new file mode 100644 index 0000000..4acb209 Binary files /dev/null and b/test/packet_data/4-17-ofp_barrier_request.packet differ diff --git a/test/packet_data/4-18-ofp_barrier_reply.packet b/test/packet_data/4-18-ofp_barrier_reply.packet new file mode 100644 index 0000000..04376c0 Binary files /dev/null and b/test/packet_data/4-18-ofp_barrier_reply.packet differ diff --git a/test/packet_data/4-19-ofp_role_request.packet b/test/packet_data/4-19-ofp_role_request.packet new file mode 100644 index 0000000..e2f3b38 Binary files /dev/null and b/test/packet_data/4-19-ofp_role_request.packet differ diff --git a/test/packet_data/4-2-ofp_flow_mod.packet b/test/packet_data/4-2-ofp_flow_mod.packet new file mode 100644 index 0000000..0c2029b Binary files /dev/null and b/test/packet_data/4-2-ofp_flow_mod.packet differ diff --git a/test/packet_data/4-20-ofp_role_reply.packet b/test/packet_data/4-20-ofp_role_reply.packet new file mode 100644 index 0000000..3f5ba39 Binary files /dev/null and b/test/packet_data/4-20-ofp_role_reply.packet differ diff --git a/test/packet_data/4-21-ofp_group_mod.packet b/test/packet_data/4-21-ofp_group_mod.packet new file mode 100644 index 0000000..3955477 Binary files /dev/null and b/test/packet_data/4-21-ofp_group_mod.packet differ diff --git a/test/packet_data/4-22-ofp_port_mod.packet b/test/packet_data/4-22-ofp_port_mod.packet new file mode 100644 index 0000000..be7044e Binary files /dev/null and b/test/packet_data/4-22-ofp_port_mod.packet differ diff --git a/test/packet_data/4-23-ofp_table_mod.packet b/test/packet_data/4-23-ofp_table_mod.packet new file mode 100644 index 0000000..02b8b02 Binary files /dev/null and b/test/packet_data/4-23-ofp_table_mod.packet differ diff --git a/test/packet_data/4-24-ofp_desc_request.packet b/test/packet_data/4-24-ofp_desc_request.packet new file mode 100644 index 0000000..b6de057 Binary files /dev/null and b/test/packet_data/4-24-ofp_desc_request.packet differ diff --git a/test/packet_data/4-25-ofp_aggregate_stats_request.packet b/test/packet_data/4-25-ofp_aggregate_stats_request.packet new file mode 100644 index 0000000..645ddac Binary files /dev/null and b/test/packet_data/4-25-ofp_aggregate_stats_request.packet differ diff --git a/test/packet_data/4-26-ofp_aggregate_stats_reply.packet b/test/packet_data/4-26-ofp_aggregate_stats_reply.packet new file mode 100644 index 0000000..3d5cd80 Binary files /dev/null and b/test/packet_data/4-26-ofp_aggregate_stats_reply.packet differ diff --git a/test/packet_data/4-27-ofp_table_stats_request.packet b/test/packet_data/4-27-ofp_table_stats_request.packet new file mode 100644 index 0000000..6fec06a Binary files /dev/null and b/test/packet_data/4-27-ofp_table_stats_request.packet differ diff --git a/test/packet_data/4-28-ofp_table_stats_reply.packet b/test/packet_data/4-28-ofp_table_stats_reply.packet new file mode 100644 index 0000000..1da72ab Binary files /dev/null and b/test/packet_data/4-28-ofp_table_stats_reply.packet differ diff --git a/test/packet_data/4-29-ofp_port_stats_request.packet b/test/packet_data/4-29-ofp_port_stats_request.packet new file mode 100644 index 0000000..d111640 Binary files /dev/null and b/test/packet_data/4-29-ofp_port_stats_request.packet differ diff --git a/test/packet_data/4-3-ofp_flow_mod.packet b/test/packet_data/4-3-ofp_flow_mod.packet new file mode 100644 index 0000000..8952a5b Binary files /dev/null and b/test/packet_data/4-3-ofp_flow_mod.packet differ diff --git a/test/packet_data/4-30-ofp_port_stats_reply.packet b/test/packet_data/4-30-ofp_port_stats_reply.packet new file mode 100644 index 0000000..8b8c182 Binary files /dev/null and b/test/packet_data/4-30-ofp_port_stats_reply.packet differ diff --git a/test/packet_data/4-31-ofp_group_features_request.packet b/test/packet_data/4-31-ofp_group_features_request.packet new file mode 100644 index 0000000..2175bb5 Binary files /dev/null and b/test/packet_data/4-31-ofp_group_features_request.packet differ diff --git a/test/packet_data/4-32-ofp_group_features_reply.packet b/test/packet_data/4-32-ofp_group_features_reply.packet new file mode 100644 index 0000000..6f7715c Binary files /dev/null and b/test/packet_data/4-32-ofp_group_features_reply.packet differ diff --git a/test/packet_data/4-33-ofp_group_desc_request.packet b/test/packet_data/4-33-ofp_group_desc_request.packet new file mode 100644 index 0000000..35add41 Binary files /dev/null and b/test/packet_data/4-33-ofp_group_desc_request.packet differ diff --git a/test/packet_data/4-34-ofp_group_desc_reply.packet b/test/packet_data/4-34-ofp_group_desc_reply.packet new file mode 100644 index 0000000..f3796cc Binary files /dev/null and b/test/packet_data/4-34-ofp_group_desc_reply.packet differ diff --git a/test/packet_data/4-35-ofp_queue_get_config_request.packet b/test/packet_data/4-35-ofp_queue_get_config_request.packet new file mode 100644 index 0000000..13072c8 Binary files /dev/null and b/test/packet_data/4-35-ofp_queue_get_config_request.packet differ diff --git a/test/packet_data/4-36-ofp_queue_get_config_reply.packet b/test/packet_data/4-36-ofp_queue_get_config_reply.packet new file mode 100644 index 0000000..898f0b3 Binary files /dev/null and b/test/packet_data/4-36-ofp_queue_get_config_reply.packet differ diff --git a/test/packet_data/4-37-ofp_queue_stats_request.packet b/test/packet_data/4-37-ofp_queue_stats_request.packet new file mode 100644 index 0000000..4baf6d7 Binary files /dev/null and b/test/packet_data/4-37-ofp_queue_stats_request.packet differ diff --git a/test/packet_data/4-38-ofp_queue_stats_reply.packet b/test/packet_data/4-38-ofp_queue_stats_reply.packet new file mode 100644 index 0000000..ad6d197 Binary files /dev/null and b/test/packet_data/4-38-ofp_queue_stats_reply.packet differ diff --git a/test/packet_data/4-39-ofp_port_status.packet b/test/packet_data/4-39-ofp_port_status.packet new file mode 100644 index 0000000..63280c7 Binary files /dev/null and b/test/packet_data/4-39-ofp_port_status.packet differ diff --git a/test/packet_data/4-4-ofp_packet_in.packet b/test/packet_data/4-4-ofp_packet_in.packet new file mode 100644 index 0000000..ebd12b5 Binary files /dev/null and b/test/packet_data/4-4-ofp_packet_in.packet differ diff --git a/test/packet_data/4-40-ofp_flow_removed.packet b/test/packet_data/4-40-ofp_flow_removed.packet new file mode 100644 index 0000000..677c2c4 Binary files /dev/null and b/test/packet_data/4-40-ofp_flow_removed.packet differ diff --git a/test/packet_data/4-41-ofp_error_msg_experimenter.packet b/test/packet_data/4-41-ofp_error_msg_experimenter.packet new file mode 100644 index 0000000..6d866b9 Binary files /dev/null and b/test/packet_data/4-41-ofp_error_msg_experimenter.packet differ diff --git a/test/packet_data/4-42-ofp_get_async_request.packet b/test/packet_data/4-42-ofp_get_async_request.packet new file mode 100644 index 0000000..4bffb26 Binary files /dev/null and b/test/packet_data/4-42-ofp_get_async_request.packet differ diff --git a/test/packet_data/4-43-ofp_get_async_reply.packet b/test/packet_data/4-43-ofp_get_async_reply.packet new file mode 100644 index 0000000..39897a9 Binary files /dev/null and b/test/packet_data/4-43-ofp_get_async_reply.packet differ diff --git a/test/packet_data/4-44-ofp_set_async.packet b/test/packet_data/4-44-ofp_set_async.packet new file mode 100644 index 0000000..de0aa1a Binary files /dev/null and b/test/packet_data/4-44-ofp_set_async.packet differ diff --git a/test/packet_data/4-45-ofp_meter_mod.packet b/test/packet_data/4-45-ofp_meter_mod.packet new file mode 100644 index 0000000..1ffbcf7 Binary files /dev/null and b/test/packet_data/4-45-ofp_meter_mod.packet differ diff --git a/test/packet_data/4-46-ofp_flow_mod.packet b/test/packet_data/4-46-ofp_flow_mod.packet new file mode 100644 index 0000000..496f1da Binary files /dev/null and b/test/packet_data/4-46-ofp_flow_mod.packet differ diff --git a/test/packet_data/4-47-ofp_meter_config_request.packet b/test/packet_data/4-47-ofp_meter_config_request.packet new file mode 100644 index 0000000..21a11e1 Binary files /dev/null and b/test/packet_data/4-47-ofp_meter_config_request.packet differ diff --git a/test/packet_data/4-48-ofp_meter_config_reply.packet b/test/packet_data/4-48-ofp_meter_config_reply.packet new file mode 100644 index 0000000..3f44326 Binary files /dev/null and b/test/packet_data/4-48-ofp_meter_config_reply.packet differ diff --git a/test/packet_data/4-49-ofp_meter_stats_request.packet b/test/packet_data/4-49-ofp_meter_stats_request.packet new file mode 100644 index 0000000..3b321af Binary files /dev/null and b/test/packet_data/4-49-ofp_meter_stats_request.packet differ diff --git a/test/packet_data/4-5-ofp_features_request.packet b/test/packet_data/4-5-ofp_features_request.packet new file mode 100644 index 0000000..7bb2775 Binary files /dev/null and b/test/packet_data/4-5-ofp_features_request.packet differ diff --git a/test/packet_data/4-50-ofp_meter_stats_reply.packet b/test/packet_data/4-50-ofp_meter_stats_reply.packet new file mode 100644 index 0000000..2317fc5 Binary files /dev/null and b/test/packet_data/4-50-ofp_meter_stats_reply.packet differ diff --git a/test/packet_data/4-51-ofp_meter_features_request.packet b/test/packet_data/4-51-ofp_meter_features_request.packet new file mode 100644 index 0000000..c4007bf Binary files /dev/null and b/test/packet_data/4-51-ofp_meter_features_request.packet differ diff --git a/test/packet_data/4-52-ofp_meter_features_reply.packet b/test/packet_data/4-52-ofp_meter_features_reply.packet new file mode 100644 index 0000000..fd9e380 Binary files /dev/null and b/test/packet_data/4-52-ofp_meter_features_reply.packet differ diff --git a/test/packet_data/4-53-ofp_port_desc_request.packet b/test/packet_data/4-53-ofp_port_desc_request.packet new file mode 100644 index 0000000..0d859f1 Binary files /dev/null and b/test/packet_data/4-53-ofp_port_desc_request.packet differ diff --git a/test/packet_data/4-54-ofp_port_desc_reply.packet b/test/packet_data/4-54-ofp_port_desc_reply.packet new file mode 100644 index 0000000..e30e9ae Binary files /dev/null and b/test/packet_data/4-54-ofp_port_desc_reply.packet differ diff --git a/test/packet_data/4-55-ofp_table_features_request.packet b/test/packet_data/4-55-ofp_table_features_request.packet new file mode 100644 index 0000000..52b9d3b Binary files /dev/null and b/test/packet_data/4-55-ofp_table_features_request.packet differ diff --git a/test/packet_data/4-56-ofp_table_features_reply.packet b/test/packet_data/4-56-ofp_table_features_reply.packet new file mode 100644 index 0000000..3a467f4 Binary files /dev/null and b/test/packet_data/4-56-ofp_table_features_reply.packet differ diff --git a/test/packet_data/4-57-ofp_group_stats_request.packet b/test/packet_data/4-57-ofp_group_stats_request.packet new file mode 100644 index 0000000..f59329e Binary files /dev/null and b/test/packet_data/4-57-ofp_group_stats_request.packet differ diff --git a/test/packet_data/4-58-ofp_group_stats_reply.packet b/test/packet_data/4-58-ofp_group_stats_reply.packet new file mode 100644 index 0000000..fe5a549 Binary files /dev/null and b/test/packet_data/4-58-ofp_group_stats_reply.packet differ diff --git a/test/packet_data/4-59-ofp_packet_in.packet b/test/packet_data/4-59-ofp_packet_in.packet new file mode 100644 index 0000000..f0879a3 Binary files /dev/null and b/test/packet_data/4-59-ofp_packet_in.packet differ diff --git a/test/packet_data/4-6-ofp_features_reply.packet b/test/packet_data/4-6-ofp_features_reply.packet new file mode 100644 index 0000000..a953633 Binary files /dev/null and b/test/packet_data/4-6-ofp_features_reply.packet differ diff --git a/test/packet_data/4-60-ofp_flow_mod.packet b/test/packet_data/4-60-ofp_flow_mod.packet new file mode 100644 index 0000000..0ca9835 Binary files /dev/null and b/test/packet_data/4-60-ofp_flow_mod.packet differ diff --git a/test/packet_data/4-61-ofp_experimenter_request.packet b/test/packet_data/4-61-ofp_experimenter_request.packet new file mode 100644 index 0000000..2ee1764 Binary files /dev/null and b/test/packet_data/4-61-ofp_experimenter_request.packet differ diff --git a/test/packet_data/4-62-ofp_experimenter_reply.packet b/test/packet_data/4-62-ofp_experimenter_reply.packet new file mode 100644 index 0000000..1f82682 Binary files /dev/null and b/test/packet_data/4-62-ofp_experimenter_reply.packet differ diff --git a/test/packet_data/4-63-onf_flow_monitor_request.packet b/test/packet_data/4-63-onf_flow_monitor_request.packet new file mode 100644 index 0000000..bf6d50e Binary files /dev/null and b/test/packet_data/4-63-onf_flow_monitor_request.packet differ diff --git a/test/packet_data/4-7-ofp_set_config.packet b/test/packet_data/4-7-ofp_set_config.packet new file mode 100644 index 0000000..43602b3 Binary files /dev/null and b/test/packet_data/4-7-ofp_set_config.packet differ diff --git a/test/packet_data/4-8-ofp_get_config_request.packet b/test/packet_data/4-8-ofp_get_config_request.packet new file mode 100644 index 0000000..78ad501 Binary files /dev/null and b/test/packet_data/4-8-ofp_get_config_request.packet differ diff --git a/test/packet_data/4-9-ofp_get_config_reply.packet b/test/packet_data/4-9-ofp_get_config_reply.packet new file mode 100644 index 0000000..b23f7bf Binary files /dev/null and b/test/packet_data/4-9-ofp_get_config_reply.packet differ diff --git a/test/packet_data/libofproto-OFP13-echo_reply.packet b/test/packet_data/libofproto-OFP13-echo_reply.packet new file mode 100644 index 0000000..e54d380 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-echo_reply.packet differ diff --git a/test/packet_data/libofproto-OFP13-echo_request.packet b/test/packet_data/libofproto-OFP13-echo_request.packet new file mode 100644 index 0000000..a13c95d Binary files /dev/null and b/test/packet_data/libofproto-OFP13-echo_request.packet differ diff --git a/test/packet_data/libofproto-OFP13-error_msg.packet b/test/packet_data/libofproto-OFP13-error_msg.packet new file mode 100644 index 0000000..33b43ca Binary files /dev/null and b/test/packet_data/libofproto-OFP13-error_msg.packet differ diff --git a/test/packet_data/libofproto-OFP13-features_reply.packet b/test/packet_data/libofproto-OFP13-features_reply.packet new file mode 100644 index 0000000..1e5dee8 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-features_reply.packet differ diff --git a/test/packet_data/libofproto-OFP13-flow_mod.packet b/test/packet_data/libofproto-OFP13-flow_mod.packet new file mode 100644 index 0000000..8c86982 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-flow_mod.packet differ diff --git a/test/packet_data/libofproto-OFP13-flow_mod.truncated64 b/test/packet_data/libofproto-OFP13-flow_mod.truncated64 new file mode 100644 index 0000000..e69de29 diff --git a/test/packet_data/libofproto-OFP13-flow_mod_conjunction.packet b/test/packet_data/libofproto-OFP13-flow_mod_conjunction.packet new file mode 100644 index 0000000..e7e0154 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-flow_mod_conjunction.packet differ diff --git a/test/packet_data/libofproto-OFP13-flow_mod_match_conj.packet b/test/packet_data/libofproto-OFP13-flow_mod_match_conj.packet new file mode 100644 index 0000000..c377092 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-flow_mod_match_conj.packet differ diff --git a/test/packet_data/libofproto-OFP13-flow_removed.packet b/test/packet_data/libofproto-OFP13-flow_removed.packet new file mode 100644 index 0000000..c98a8f2 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-flow_removed.packet differ diff --git a/test/packet_data/libofproto-OFP13-get_config_reply.packet b/test/packet_data/libofproto-OFP13-get_config_reply.packet new file mode 100644 index 0000000..b23f7bf Binary files /dev/null and b/test/packet_data/libofproto-OFP13-get_config_reply.packet differ diff --git a/test/packet_data/libofproto-OFP13-hello.packet b/test/packet_data/libofproto-OFP13-hello.packet new file mode 100644 index 0000000..60b3118 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-hello.packet differ diff --git a/test/packet_data/libofproto-OFP13-meter_mod.packet b/test/packet_data/libofproto-OFP13-meter_mod.packet new file mode 100644 index 0000000..60870dd Binary files /dev/null and b/test/packet_data/libofproto-OFP13-meter_mod.packet differ diff --git a/test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet b/test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet new file mode 100644 index 0000000..cf6689f Binary files /dev/null and b/test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet differ diff --git a/test/packet_data/libofproto-OFP13-packet_in.packet b/test/packet_data/libofproto-OFP13-packet_in.packet new file mode 100644 index 0000000..817b51c Binary files /dev/null and b/test/packet_data/libofproto-OFP13-packet_in.packet differ diff --git a/test/packet_data/libofproto-OFP13-port_mod.packet b/test/packet_data/libofproto-OFP13-port_mod.packet new file mode 100644 index 0000000..105a27e Binary files /dev/null and b/test/packet_data/libofproto-OFP13-port_mod.packet differ diff --git a/test/packet_data/libofproto-OFP13-port_status.packet b/test/packet_data/libofproto-OFP13-port_status.packet new file mode 100644 index 0000000..7a2c14c Binary files /dev/null and b/test/packet_data/libofproto-OFP13-port_status.packet differ diff --git a/test/packet_data/libofproto-OFP13-set_config.packet b/test/packet_data/libofproto-OFP13-set_config.packet new file mode 100644 index 0000000..43602b3 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-set_config.packet differ diff --git a/test/packet_data/libofproto-OFP13-table_mod.packet b/test/packet_data/libofproto-OFP13-table_mod.packet new file mode 100644 index 0000000..02b8b02 Binary files /dev/null and b/test/packet_data/libofproto-OFP13-table_mod.packet differ diff --git a/test/packet_data/nx_bundle.raw b/test/packet_data/nx_bundle.raw new file mode 100644 index 0000000..5599f37 Binary files /dev/null and b/test/packet_data/nx_bundle.raw differ diff --git a/test/packet_data/nx_bundle_load.raw b/test/packet_data/nx_bundle_load.raw new file mode 100644 index 0000000..8fc3404 Binary files /dev/null and b/test/packet_data/nx_bundle_load.raw differ diff --git a/test/packet_data/nx_clone.raw b/test/packet_data/nx_clone.raw new file mode 100644 index 0000000..47e62dd Binary files /dev/null and b/test/packet_data/nx_clone.raw differ diff --git a/test/packet_data/nx_controller.raw b/test/packet_data/nx_controller.raw new file mode 100644 index 0000000..a66c407 Binary files /dev/null and b/test/packet_data/nx_controller.raw differ diff --git a/test/packet_data/nx_controller2.raw b/test/packet_data/nx_controller2.raw new file mode 100644 index 0000000..0921fa8 Binary files /dev/null and b/test/packet_data/nx_controller2.raw differ diff --git a/test/packet_data/nx_ct(alg=ftp).raw b/test/packet_data/nx_ct(alg=ftp).raw new file mode 100644 index 0000000..3549456 Binary files /dev/null and b/test/packet_data/nx_ct(alg=ftp).raw differ diff --git a/test/packet_data/nx_ct(alg=tftp).raw b/test/packet_data/nx_ct(alg=tftp).raw new file mode 100644 index 0000000..c6b5a90 Binary files /dev/null and b/test/packet_data/nx_ct(alg=tftp).raw differ diff --git a/test/packet_data/nx_ct(commit).raw b/test/packet_data/nx_ct(commit).raw new file mode 100644 index 0000000..e9e8956 Binary files /dev/null and b/test/packet_data/nx_ct(commit).raw differ diff --git a/test/packet_data/nx_ct(commit, force).raw b/test/packet_data/nx_ct(commit, force).raw new file mode 100644 index 0000000..1df60e4 Binary files /dev/null and b/test/packet_data/nx_ct(commit, force).raw differ diff --git a/test/packet_data/nx_ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw b/test/packet_data/nx_ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw new file mode 100644 index 0000000..02667c5 Binary files /dev/null and b/test/packet_data/nx_ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw differ diff --git a/test/packet_data/nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw b/test/packet_data/nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw new file mode 100644 index 0000000..a780a64 Binary files /dev/null and b/test/packet_data/nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw differ diff --git a/test/packet_data/nx_ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])).raw b/test/packet_data/nx_ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])).raw new file mode 100644 index 0000000..46a0644 Binary files /dev/null and b/test/packet_data/nx_ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(dst)).raw b/test/packet_data/nx_ct(commit,nat(dst)).raw new file mode 100644 index 0000000..160d237 Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(dst)).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)).raw b/test/packet_data/nx_ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)).raw new file mode 100644 index 0000000..2dc561d Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(src)).raw b/test/packet_data/nx_ct(commit,nat(src)).raw new file mode 100644 index 0000000..028206b Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(src)).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(src=10.0.0.240,random)).raw b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240,random)).raw new file mode 100644 index 0000000..f201bb0 Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240,random)).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)).raw b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)).raw new file mode 100644 index 0000000..9176b4a Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(src=10.0.0.240:32768-65535,random)).raw b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240:32768-65535,random)).raw new file mode 100644 index 0000000..ab1b13a Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240:32768-65535,random)).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)).raw b/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)).raw new file mode 100644 index 0000000..fd1eb9c Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))fd.raw b/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))fd.raw new file mode 100644 index 0000000..fd1eb9c Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))fd.raw differ diff --git a/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)).raw b/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)).raw new file mode 100644 index 0000000..7f10898 Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)).raw differ diff --git a/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)).raw b/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)).raw new file mode 100644 index 0000000..52c9c6c Binary files /dev/null and b/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)).raw differ diff --git a/test/packet_data/nx_ct(nat).raw b/test/packet_data/nx_ct(nat).raw new file mode 100644 index 0000000..86595c3 Binary files /dev/null and b/test/packet_data/nx_ct(nat).raw differ diff --git a/test/packet_data/nx_ct(table=10).raw b/test/packet_data/nx_ct(table=10).raw new file mode 100644 index 0000000..72c42ec Binary files /dev/null and b/test/packet_data/nx_ct(table=10).raw differ diff --git a/test/packet_data/nx_ct(zone=10).raw b/test/packet_data/nx_ct(zone=10).raw new file mode 100644 index 0000000..5f43a0c Binary files /dev/null and b/test/packet_data/nx_ct(zone=10).raw differ diff --git a/test/packet_data/nx_ct(zone=NXM_NX_REG0[0..15]).raw b/test/packet_data/nx_ct(zone=NXM_NX_REG0[0..15]).raw new file mode 100644 index 0000000..d91f148 Binary files /dev/null and b/test/packet_data/nx_ct(zone=NXM_NX_REG0[0..15]).raw differ diff --git a/test/packet_data/nx_ct.raw b/test/packet_data/nx_ct.raw new file mode 100644 index 0000000..96358a5 Binary files /dev/null and b/test/packet_data/nx_ct.raw differ diff --git a/test/packet_data/nx_ct_clear.raw b/test/packet_data/nx_ct_clear.raw new file mode 100644 index 0000000..8cfac67 Binary files /dev/null and b/test/packet_data/nx_ct_clear.raw differ diff --git a/test/packet_data/nx_dec_ttl.raw b/test/packet_data/nx_dec_ttl.raw new file mode 100644 index 0000000..6b77e1b Binary files /dev/null and b/test/packet_data/nx_dec_ttl.raw differ diff --git a/test/packet_data/nx_dec_ttl_cnt_ids.raw b/test/packet_data/nx_dec_ttl_cnt_ids.raw new file mode 100644 index 0000000..5338bdf Binary files /dev/null and b/test/packet_data/nx_dec_ttl_cnt_ids.raw differ diff --git a/test/packet_data/nx_exit.raw b/test/packet_data/nx_exit.raw new file mode 100644 index 0000000..f6accc1 Binary files /dev/null and b/test/packet_data/nx_exit.raw differ diff --git a/test/packet_data/nx_fin_timeout.raw b/test/packet_data/nx_fin_timeout.raw new file mode 100644 index 0000000..a4cf557 Binary files /dev/null and b/test/packet_data/nx_fin_timeout.raw differ diff --git a/test/packet_data/nx_learn.raw b/test/packet_data/nx_learn.raw new file mode 100644 index 0000000..eb372f4 Binary files /dev/null and b/test/packet_data/nx_learn.raw differ diff --git a/test/packet_data/nx_learn2.raw b/test/packet_data/nx_learn2.raw new file mode 100644 index 0000000..55e900e Binary files /dev/null and b/test/packet_data/nx_learn2.raw differ diff --git a/test/packet_data/nx_multipath.raw b/test/packet_data/nx_multipath.raw new file mode 100644 index 0000000..2dcc917 Binary files /dev/null and b/test/packet_data/nx_multipath.raw differ diff --git a/test/packet_data/nx_note.raw b/test/packet_data/nx_note.raw new file mode 100644 index 0000000..19be9f7 Binary files /dev/null and b/test/packet_data/nx_note.raw differ diff --git a/test/packet_data/nx_output_reg.raw b/test/packet_data/nx_output_reg.raw new file mode 100644 index 0000000..0dd869e Binary files /dev/null and b/test/packet_data/nx_output_reg.raw differ diff --git a/test/packet_data/nx_output_trunc.raw b/test/packet_data/nx_output_trunc.raw new file mode 100644 index 0000000..29631eb Binary files /dev/null and b/test/packet_data/nx_output_trunc.raw differ diff --git a/test/packet_data/nx_packet_in2.raw b/test/packet_data/nx_packet_in2.raw new file mode 100644 index 0000000..d293c64 Binary files /dev/null and b/test/packet_data/nx_packet_in2.raw differ diff --git a/test/packet_data/nx_pop_queue.raw b/test/packet_data/nx_pop_queue.raw new file mode 100644 index 0000000..b1dd644 Binary files /dev/null and b/test/packet_data/nx_pop_queue.raw differ diff --git a/test/packet_data/nx_reg_load.raw b/test/packet_data/nx_reg_load.raw new file mode 100644 index 0000000..c5f0b4e Binary files /dev/null and b/test/packet_data/nx_reg_load.raw differ diff --git a/test/packet_data/nx_reg_move.raw b/test/packet_data/nx_reg_move.raw new file mode 100644 index 0000000..863a204 Binary files /dev/null and b/test/packet_data/nx_reg_move.raw differ diff --git a/test/packet_data/nx_resubmit.raw b/test/packet_data/nx_resubmit.raw new file mode 100644 index 0000000..5036884 Binary files /dev/null and b/test/packet_data/nx_resubmit.raw differ diff --git a/test/packet_data/nx_resubmit_table.raw b/test/packet_data/nx_resubmit_table.raw new file mode 100644 index 0000000..a115cfd Binary files /dev/null and b/test/packet_data/nx_resubmit_table.raw differ diff --git a/test/packet_data/nx_resubmit_table_ct.raw b/test/packet_data/nx_resubmit_table_ct.raw new file mode 100644 index 0000000..ff974d5 Binary files /dev/null and b/test/packet_data/nx_resubmit_table_ct.raw differ diff --git a/test/packet_data/nx_sample.raw b/test/packet_data/nx_sample.raw new file mode 100644 index 0000000..44c2437 Binary files /dev/null and b/test/packet_data/nx_sample.raw differ diff --git a/test/packet_data/nx_sample2.raw b/test/packet_data/nx_sample2.raw new file mode 100644 index 0000000..0c3dcdd Binary files /dev/null and b/test/packet_data/nx_sample2.raw differ diff --git a/test/packet_data/nx_sample3.raw b/test/packet_data/nx_sample3.raw new file mode 100644 index 0000000..8e7c421 Binary files /dev/null and b/test/packet_data/nx_sample3.raw differ diff --git a/test/packet_data/nx_set_controller_id.raw b/test/packet_data/nx_set_controller_id.raw new file mode 100644 index 0000000..6e82765 Binary files /dev/null and b/test/packet_data/nx_set_controller_id.raw differ diff --git a/test/packet_data/nx_set_packet_in_format.raw b/test/packet_data/nx_set_packet_in_format.raw new file mode 100644 index 0000000..ae11499 Binary files /dev/null and b/test/packet_data/nx_set_packet_in_format.raw differ diff --git a/test/packet_data/nx_set_queue.raw b/test/packet_data/nx_set_queue.raw new file mode 100644 index 0000000..f7da696 Binary files /dev/null and b/test/packet_data/nx_set_queue.raw differ diff --git a/test/packet_data/nx_set_tunnel.raw b/test/packet_data/nx_set_tunnel.raw new file mode 100644 index 0000000..baa9a5c Binary files /dev/null and b/test/packet_data/nx_set_tunnel.raw differ diff --git a/test/packet_data/nx_set_tunnel64.raw b/test/packet_data/nx_set_tunnel64.raw new file mode 100644 index 0000000..0106874 Binary files /dev/null and b/test/packet_data/nx_set_tunnel64.raw differ diff --git a/test/packet_data/ofp_echo_reply.raw b/test/packet_data/ofp_echo_reply.raw new file mode 100644 index 0000000..e54d380 Binary files /dev/null and b/test/packet_data/ofp_echo_reply.raw differ diff --git a/test/packet_data/ofp_echo_request.raw b/test/packet_data/ofp_echo_request.raw new file mode 100644 index 0000000..a13c95d Binary files /dev/null and b/test/packet_data/ofp_echo_request.raw differ diff --git a/test/packet_data/ofp_error.raw b/test/packet_data/ofp_error.raw new file mode 100644 index 0000000..196dad1 Binary files /dev/null and b/test/packet_data/ofp_error.raw differ diff --git a/test/packet_data/ofp_features_reply.raw b/test/packet_data/ofp_features_reply.raw new file mode 100644 index 0000000..1e5dee8 Binary files /dev/null and b/test/packet_data/ofp_features_reply.raw differ diff --git a/test/packet_data/ofp_features_request.raw b/test/packet_data/ofp_features_request.raw new file mode 100644 index 0000000..7bb2775 Binary files /dev/null and b/test/packet_data/ofp_features_request.raw differ diff --git a/test/packet_data/ofp_get_config_reply.raw b/test/packet_data/ofp_get_config_reply.raw new file mode 100644 index 0000000..b23f7bf Binary files /dev/null and b/test/packet_data/ofp_get_config_reply.raw differ diff --git a/test/packet_data/ofp_get_config_request.raw b/test/packet_data/ofp_get_config_request.raw new file mode 100644 index 0000000..78ad501 Binary files /dev/null and b/test/packet_data/ofp_get_config_request.raw differ diff --git a/test/packet_data/ofp_hello.raw b/test/packet_data/ofp_hello.raw new file mode 100644 index 0000000..644b413 Binary files /dev/null and b/test/packet_data/ofp_hello.raw differ diff --git a/test/packet_data/ofp_set_config.raw b/test/packet_data/ofp_set_config.raw new file mode 100644 index 0000000..43602b3 Binary files /dev/null and b/test/packet_data/ofp_set_config.raw differ diff --git a/test/packet_data/ovs-ofctl-of13-action_conjunction.packet b/test/packet_data/ovs-ofctl-of13-action_conjunction.packet new file mode 100644 index 0000000..ba029b2 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_conjunction.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_controller.packet b/test/packet_data/ovs-ofctl-of13-action_controller.packet new file mode 100644 index 0000000..8fba904 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_controller.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_controller2.packet b/test/packet_data/ovs-ofctl-of13-action_controller2.packet new file mode 100644 index 0000000..7e6b046 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_controller2.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_ct.packet b/test/packet_data/ovs-ofctl-of13-action_ct.packet new file mode 100644 index 0000000..5eb733f Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_ct.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_ct_exec.packet b/test/packet_data/ovs-ofctl-of13-action_ct_exec.packet new file mode 100644 index 0000000..3f0b343 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_ct_exec.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_ct_nat.packet b/test/packet_data/ovs-ofctl-of13-action_ct_nat.packet new file mode 100644 index 0000000..e210a38 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_ct_nat.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_ct_nat_v6.packet b/test/packet_data/ovs-ofctl-of13-action_ct_nat_v6.packet new file mode 100644 index 0000000..94b2aaa Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_ct_nat_v6.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_dec_ttl_cnt_ids.packet b/test/packet_data/ovs-ofctl-of13-action_dec_ttl_cnt_ids.packet new file mode 100644 index 0000000..c4308cc Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_dec_ttl_cnt_ids.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_fintimeout.packet b/test/packet_data/ovs-ofctl-of13-action_fintimeout.packet new file mode 100644 index 0000000..78c3eab Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_fintimeout.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_learn.packet b/test/packet_data/ovs-ofctl-of13-action_learn.packet new file mode 100644 index 0000000..39c71e2 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_learn.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_note.packet b/test/packet_data/ovs-ofctl-of13-action_note.packet new file mode 100644 index 0000000..9a1491c Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_note.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_output_trunc.packet b/test/packet_data/ovs-ofctl-of13-action_output_trunc.packet new file mode 100644 index 0000000..491cf4c Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_output_trunc.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_resubmit.packet b/test/packet_data/ovs-ofctl-of13-action_resubmit.packet new file mode 100644 index 0000000..690b978 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_resubmit.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_sample.packet b/test/packet_data/ovs-ofctl-of13-action_sample.packet new file mode 100644 index 0000000..c5167ba Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_sample.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_sample2.packet b/test/packet_data/ovs-ofctl-of13-action_sample2.packet new file mode 100644 index 0000000..444f094 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_sample2.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_stack_pop.packet b/test/packet_data/ovs-ofctl-of13-action_stack_pop.packet new file mode 100644 index 0000000..d6f99e2 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_stack_pop.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-action_stack_push.packet b/test/packet_data/ovs-ofctl-of13-action_stack_push.packet new file mode 100644 index 0000000..e5d38a3 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-action_stack_push.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-match_conj.packet b/test/packet_data/ovs-ofctl-of13-match_conj.packet new file mode 100644 index 0000000..1f9c57c Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-match_conj.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-match_load_nx_register.packet b/test/packet_data/ovs-ofctl-of13-match_load_nx_register.packet new file mode 100644 index 0000000..03ce737 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-match_load_nx_register.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-match_move_nx_register.packet b/test/packet_data/ovs-ofctl-of13-match_move_nx_register.packet new file mode 100644 index 0000000..eb9cf8a Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-match_move_nx_register.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-match_pkt_mark.packet b/test/packet_data/ovs-ofctl-of13-match_pkt_mark.packet new file mode 100644 index 0000000..46ea0dc Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-match_pkt_mark.packet differ diff --git a/test/packet_data/ovs-ofctl-of13-match_pkt_mark_masked.packet b/test/packet_data/ovs-ofctl-of13-match_pkt_mark_masked.packet new file mode 100644 index 0000000..26db6b9 Binary files /dev/null and b/test/packet_data/ovs-ofctl-of13-match_pkt_mark_masked.packet differ diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/test/tres_test.exs b/test/tres_test.exs new file mode 100644 index 0000000..24ae030 --- /dev/null +++ b/test/tres_test.exs @@ -0,0 +1,4 @@ +defmodule TresTest do + use ExUnit.Case + doctest Tres +end