diff --git a/bin/enum_gen b/bin/enum_gen index bf59491..7390ec7 100755 Binary files a/bin/enum_gen and b/bin/enum_gen differ diff --git a/config/config.exs b/config/config.exs index 40896b3..6dc6fa7 100644 --- a/config/config.exs +++ b/config/config.exs @@ -4,7 +4,7 @@ use Mix.Config config :tres, protocol: :tcp, - port: 6653, + port: 6633, max_connections: 10, num_acceptors: 10, callback_module: Tres.ExampleHandler, diff --git a/lib/openflow.ex b/lib/openflow.ex index 49b98db..84fb124 100644 --- a/lib/openflow.ex +++ b/lib/openflow.ex @@ -15,22 +15,23 @@ defmodule Openflow do end def read(<>) do - try do - body_len = len - @ofp_header_size - <> = binary2 + try do + body_len = len - @ofp_header_size + <> = binary2 - result = type + 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 - catch - _c, _e -> - {:error, :malformed_packet} + case result do + {:ok, struct} -> {:ok, %{struct | version: ver, xid: xid}, rest} + {:error, reason} -> {:error, reason} end + catch + _c, reason -> + {:error, {:malformed_packet, {reason, __STACKTRACE__}}} + end end def to_binary(messages) when is_list(messages) do diff --git a/lib/openflow/hello.ex b/lib/openflow/hello.ex index aa3da17..415ccc9 100644 --- a/lib/openflow/hello.ex +++ b/lib/openflow/hello.ex @@ -24,8 +24,8 @@ defmodule Openflow.Hello do def supported_version?(%Hello{elements: elements}) do elements - |> Enum.reduce([], fn({:versionbitmap, versions}, acc) -> acc ++ versions end) - |> Enum.any?(fn(version) -> version == 4 end) + |> Enum.reduce([], fn {:versionbitmap, versions}, acc -> acc ++ versions end) + |> Enum.any?(fn version -> version == 4 end) end def read(binary), do: %Hello{elements: decode([], binary)} diff --git a/lib/openflow/nx_packet_in2.ex b/lib/openflow/nx_packet_in2.ex index 77955be..02e2572 100644 --- a/lib/openflow/nx_packet_in2.ex +++ b/lib/openflow/nx_packet_in2.ex @@ -14,17 +14,40 @@ defmodule Openflow.NxPacketIn2 do metadata: nil, userdata: nil, # continuation properties: - continuation_bridge: "", - continuation_stack: [], - continuation_conntracked: false, + continuation_bridge: nil, + continuation_stack: nil, + continuation_conntracked: nil, + continuation_mirrors: nil, continuation_table_id: nil, continuation_cookie: nil, - continuation_actions: [], + continuation_actions: nil, continuation_action_set: nil ) alias __MODULE__ + @encode_keys ~w( + packet + full_len + buffer_id + table_id + cookie + reason + metadata + userdata + )a + + @continuation_keys ~w( + continuation_bridge + continuation_stack + continuation_conntracked + continuation_table_id + continuation_cookie + continuation_actions + continuation_mirrors + continuation_action_set + )a + @experimenter 0x00002320 @nx_type 30 @@ -51,6 +74,12 @@ defmodule Openflow.NxPacketIn2 do def ofp_type, do: 4 + def to_binary(%NxPacketIn2{} = pin) do + props_bin = encode_props("", pin, @encode_keys) + continuations_bin = encode_continuations("", pin, @continuation_keys) + <<@experimenter::32, @nx_type::32, props_bin::bytes, continuations_bin::bytes>> + end + def read(<<@experimenter::32, @nx_type::32, props_bin::bytes>>) do %NxPacketIn2{} |> decode_props(props_bin) @@ -58,6 +87,189 @@ defmodule Openflow.NxPacketIn2 do ## private functions + defp encode_props(acc, _pin, []), do: acc + + defp encode_props(acc, %NxPacketIn2{packet: packet} = pin, [:packet | rest]) + when not is_nil(packet) and is_binary(packet) do + length = @prop_header_length + byte_size(packet) + pad_length = Openflow.Utils.pad_length(length, 8) + binary = <<@packet::16, length::16, packet::bytes, 0::size(pad_length)-unit(8)>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxPacketIn2{full_len: full_len} = pin, [:full_len | rest]) + when not is_nil(full_len) and is_integer(full_len) do + length = @prop_header_length + 4 + binary = <<@full_len::16, length::16, full_len::32>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxPacketIn2{buffer_id: buffer_id} = pin, [:buffer_id | rest]) + when not is_nil(buffer_id) and is_integer(buffer_id) do + length = @prop_header_length + 4 + binary = <<@buffer_id::16, length::16, buffer_id::32>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxPacketIn2{table_id: table_id} = pin, [:table_id | rest]) + when not is_nil(table_id) and is_integer(table_id) do + length = @prop_header_length + 4 + binary = <<@table_id::16, length::16, table_id::8, 0::24>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxPacketIn2{cookie: cookie} = pin, [:cookie | rest]) + when not is_nil(cookie) and is_integer(cookie) do + length = @prop_header_length + 4 + binary = <<@cookie::16, length::16, 0::32, cookie::64>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxPacketIn2{metadata: metadata} = pin, [:metadata | rest]) + when not is_nil(metadata) and is_list(metadata) do + pad_match_bin = + metadata + |> Openflow.Match.new() + |> Openflow.Match.to_binary() + + <<1::16, match_len_with_header::16, padded_match_bin::bytes>> = pad_match_bin + match_len = match_len_with_header - 4 + <> = padded_match_bin + length = @prop_header_length + match_len + pad_length = Openflow.Utils.pad_length(length, 8) + binary = <<@metadata::16, length::16, match_bin::bytes, 0::size(pad_length)-unit(8)>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxPacketIn2{userdata: userdata} = pin, [:userdata | rest]) + when not is_nil(userdata) and is_binary(userdata) do + length = @prop_header_length + byte_size(userdata) + pad_length = Openflow.Utils.pad_length(length, 8) + binary = <<@userdata::16, length::16, userdata::bytes, 0::size(pad_length)-unit(8)>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, pin, [_ | rest]) do + encode_props(acc, pin, rest) + end + + defp encode_continuations(acc, _pin, []) do + if byte_size(acc) > 0 do + length = @prop_header_length + byte_size(acc) + pad_length = Openflow.Utils.pad_length(length, 8) + <<@continuation::16, acc::bytes, 0::size(pad_length)-unit(8)>> + else + <<>> + end + end + + defp encode_continuations( + acc, + %NxPacketIn2{continuation_bridge: br} = pin, + [:continuation_bridge | rest] + ) + when not is_nil(br) do + length = @prop_header_length + byte_size(br) + pad_length = Openflow.Utils.pad_length(length, 8) + bridge_bin = <> + binary = <<@nxcpt_bridge::16, length::16, bridge_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxPacketIn2{continuation_stack: stack} = pin, + [:continuation_stack | rest] + ) + when not is_nil(stack) do + length = @prop_header_length + byte_size(stack) + pad_length = Openflow.Utils.pad_length(length, 8) + stack_bin = <> + binary = <<@nxcpt_stack::16, length::16, stack_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxPacketIn2{continuation_mirrors: mirrors} = pin, + [:continuation_mirrors | rest] + ) + when not is_nil(mirrors) do + length = @prop_header_length + byte_size(mirrors) + pad_length = Openflow.Utils.pad_length(length, 8) + mirrors_bin = <> + binary = <<@nxcpt_mirrors::16, length::16, mirrors_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxPacketIn2{continuation_conntracked: true} = pin, + [:continuation_conntracked | rest] + ) do + length = @prop_header_length + 1 + pad_length = Openflow.Utils.pad_length(length, 8) + conntracked_bin = <<1::size(1)-unit(8), 0::size(pad_length)-unit(8), acc::bytes>> + binary = <<@nxcpt_conntracked::16, length::16, conntracked_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxPacketIn2{continuation_table_id: table_id} = pin, + [:continuation_table_id | rest] + ) + when not is_nil(table_id) do + length = @prop_header_length + 1 + pad_length = Openflow.Utils.pad_length(length, 8) + table_id_bin = <> + binary = <<@nxcpt_table_id::16, length::16, table_id_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxPacketIn2{continuation_cookie: cookie} = pin, + [:continuation_cookie | rest] + ) + when not is_nil(cookie) do + length = @prop_header_length + 8 + pad_length = Openflow.Utils.pad_length(length, 8) + cookie_bin = <> + binary = <<@nxcpt_cookie::16, length::16, cookie_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxPacketIn2{continuation_actions: actions} = pin, + [:continuation_actions | rest] + ) do + actions_bin = Openflow.Action.to_binary(actions) + length = @prop_header_length + byte_size(actions_bin) + pad_length = Openflow.Utils.pad_length(length, 8) + actions_bin = <> + binary = <<@nxcpt_actions::16, length::16, actions_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxPacketIn2{continuation_action_set: action_set} = pin, + [:continuation_action_set | rest] + ) + when not is_nil(action_set) do + length = @prop_header_length + byte_size(action_set) + pad_length = Openflow.Utils.pad_length(length, 8) + action_set_bin = <> + binary = <<@nxcpt_action_set::16, length::16, action_set_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations(acc, pin, [_ | rest]) do + encode_continuations(acc, pin, rest) + end + defp decode_props(pktin, ""), do: pktin defp decode_props(pktin, <<@packet::16, length::16, tail::bytes>>) do diff --git a/lib/openflow/nx_resume.ex b/lib/openflow/nx_resume.ex new file mode 100644 index 0000000..46e4530 --- /dev/null +++ b/lib/openflow/nx_resume.ex @@ -0,0 +1,431 @@ +defmodule Openflow.NxResume 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 properties: + continuation_bridge: nil, + continuation_stack: nil, + continuation_conntracked: nil, + continuation_mirrors: nil, + continuation_table_id: nil, + continuation_cookie: nil, + continuation_actions: nil, + continuation_action_set: nil + ) + + alias __MODULE__ + + @encode_keys ~w( + packet + full_len + buffer_id + table_id + cookie + reason + metadata + userdata + )a + + @continuation_keys ~w( + continuation_bridge + continuation_stack + continuation_conntracked + continuation_table_id + continuation_cookie + continuation_actions + continuation_mirrors + continuation_action_set + )a + + @experimenter 0x00002320 + @nx_type 28 + + @packet 0 + @full_len 1 + @buffer_id 2 + @table_id 3 + @cookie 4 + @reason 5 + @metadata 6 + @userdata 7 + @continuation 8 + + @nxcpt_bridge 0x8000 + @nxcpt_stack 0x8001 + @nxcpt_mirrors 0x8002 + @nxcpt_conntracked 0x8003 + @nxcpt_table_id 0x8004 + @nxcpt_cookie 0x8005 + @nxcpt_actions 0x8006 + @nxcpt_action_set 0x8007 + + @prop_header_length 4 + + def ofp_type, do: 4 + + def new(options \\ []) do + packet_in = options[:packet_in] || %Openflow.NxPacketIn2{} + packet = options[:packet] || packet_in.packet + + %NxResume{ + packet: packet, + table_id: packet_in.table_id, + cookie: packet_in.cookie, + metadata: options[:metadata] || packet_in.metadata, + userdata: packet_in.userdata, + continuation_bridge: packet_in.continuation_bridge, + continuation_stack: packet_in.continuation_stack, + continuation_conntracked: packet_in.continuation_conntracked, + continuation_table_id: packet_in.continuation_table_id, + continuation_cookie: packet_in.continuation_cookie, + continuation_actions: packet_in.continuation_actions, + continuation_action_set: packet_in.continuation_action_set, + continuation_mirrors: packet_in.continuation_mirrors + } + end + + def to_binary(%NxResume{} = pin) do + props_bin = encode_props("", pin, @encode_keys) + continuations_bin = encode_continuations("", pin, @continuation_keys) + <<@experimenter::32, @nx_type::32, props_bin::bytes, continuations_bin::bytes>> + end + + def read(<<@experimenter::32, @nx_type::32, props_bin::bytes>>) do + %NxResume{} + |> decode_props(props_bin) + end + + ## private functions + + defp encode_props(acc, _pin, []), do: acc + + defp encode_props(acc, %NxResume{packet: packet} = pin, [:packet | rest]) + when not is_nil(packet) and is_binary(packet) do + length = @prop_header_length + byte_size(packet) + pad_length = Openflow.Utils.pad_length(length, 8) + binary = <<@packet::16, length::16, packet::bytes, 0::size(pad_length)-unit(8)>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxResume{full_len: full_len} = pin, [:full_len | rest]) + when not is_nil(full_len) and is_integer(full_len) do + length = @prop_header_length + 4 + binary = <<@full_len::16, length::16, full_len::32>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxResume{buffer_id: buffer_id} = pin, [:buffer_id | rest]) + when not is_nil(buffer_id) do + length = @prop_header_length + 4 + buffer_id_int = Openflow.Utils.get_enum(buffer_id, :buffer_id) + binary = <<@buffer_id::16, length::16, buffer_id_int::32>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxResume{table_id: table_id} = pin, [:table_id | rest]) + when not is_nil(table_id) and is_integer(table_id) do + length = @prop_header_length + 4 + binary = <<@table_id::16, length::16, table_id::8, 0::24>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxResume{cookie: cookie} = pin, [:cookie | rest]) + when not is_nil(cookie) and is_integer(cookie) do + length = @prop_header_length + 4 + 8 + pad_length = Openflow.Utils.pad_length(length, 8) + binary = <<@cookie::16, length::16, 0::32, cookie::64, 0::size(pad_length)-unit(8)>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxResume{metadata: metadata} = pin, [:metadata | rest]) + when not is_nil(metadata) and is_list(metadata) do + pad_match_bin = + metadata + |> Openflow.Match.new() + |> Openflow.Match.to_binary() + + <<1::16, match_len_with_header::16, padded_match_bin::bytes>> = pad_match_bin + match_len = match_len_with_header - 4 + <> = padded_match_bin + length = @prop_header_length + match_len + pad_length = Openflow.Utils.pad_length(length, 8) + binary = <<@metadata::16, length::16, match_bin::bytes, 0::size(pad_length)-unit(8)>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, %NxResume{userdata: userdata} = pin, [:userdata | rest]) + when not is_nil(userdata) and is_binary(userdata) do + length = @prop_header_length + byte_size(userdata) + pad_length = Openflow.Utils.pad_length(length, 8) + binary = <<@userdata::16, length::16, userdata::bytes, 0::size(pad_length)-unit(8)>> + encode_props(<>, pin, rest) + end + + defp encode_props(acc, pin, [_ | rest]) do + encode_props(acc, pin, rest) + end + + defp encode_continuations(acc, _pin, []) do + if byte_size(acc) > 0 do + length = @prop_header_length + byte_size(acc) + 4 + pad_length = Openflow.Utils.pad_length(length, 8) + <<@continuation::16, length::16, 0::32, acc::bytes, 0::size(pad_length)-unit(8)>> + else + <<>> + end + end + + defp encode_continuations( + acc, + %NxResume{continuation_bridge: br} = pin, + [:continuation_bridge | rest] + ) + when not is_nil(br) do + length = byte_size(br) + @prop_header_length + pad_length = Openflow.Utils.pad_length(length, 8) + br_bin = <> + binary = <<@nxcpt_bridge::16, length::16, br_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxResume{continuation_stack: stack} = pin, + [:continuation_stack | rest] + ) + when not is_nil(stack) do + length = @prop_header_length + byte_size(stack) + pad_length = Openflow.Utils.pad_length(length, 8) + stack_bin = <> + binary = <<@nxcpt_stack::16, length::16, stack_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxResume{continuation_mirrors: mirrors} = pin, + [:continuation_mirrors | rest] + ) + when not is_nil(mirrors) do + length = @prop_header_length + byte_size(mirrors) + pad_length = Openflow.Utils.pad_length(length, 8) + mirrors_bin = <> + binary = <<@nxcpt_mirrors::16, length::16, mirrors_bin::bytes, acc::bytes>> + + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxResume{continuation_conntracked: true} = pin, + [:continuation_conntracked | rest] + ) do + length = @prop_header_length + 1 + pad_length = Openflow.Utils.pad_length(length, 8) + conntracked_bin = <<1::size(1)-unit(8), 0::size(pad_length)-unit(8), acc::bytes>> + binary = <<@nxcpt_conntracked::16, length::16, conntracked_bin::bytes, acc::bytes>> + + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxResume{continuation_table_id: table_id} = pin, + [:continuation_table_id | rest] + ) + when not is_nil(table_id) do + length = @prop_header_length + 1 + pad_length = Openflow.Utils.pad_length(length, 8) + table_id_bin = <> + binary = <<@nxcpt_table_id::16, length::16, table_id_bin::bytes, acc::bytes>> + + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxResume{continuation_cookie: cookie} = pin, + [:continuation_cookie | rest] + ) + when not is_nil(cookie) do + length = @prop_header_length + 8 + pad_length = Openflow.Utils.pad_length(length, 8) + cookie_bin = <> + binary = <<@nxcpt_cookie::16, length::16, cookie_bin::bytes, acc::bytes>> + + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxResume{continuation_actions: actions} = pin, + [:continuation_actions | rest] + ) + when not is_nil(actions) do + actions_bin = Openflow.Action.to_binary(actions) + length = @prop_header_length + byte_size(actions_bin) + 4 + pad_length = Openflow.Utils.pad_length(length, 8) + padded_actions_bin = <<0::32, actions_bin::bytes, 0::size(pad_length)-unit(8)>> + binary = <<@nxcpt_actions::16, length::16, padded_actions_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations( + acc, + %NxResume{continuation_action_set: action_set} = pin, + [:continuation_action_set | rest] + ) + when not is_nil(action_set) do + length = @prop_header_length + byte_size(action_set) + pad_length = Openflow.Utils.pad_length(length, 8) + action_set_bin = <> + binary = <<@nxcpt_action_set::16, length::16, action_set_bin::bytes, acc::bytes>> + encode_continuations(binary, pin, rest) + end + + defp encode_continuations(acc, pin, [_ | rest]) do + encode_continuations(acc, pin, rest) + end + + defp decode_props(pktin, ""), do: pktin + + defp decode_props(pktin, <<@packet::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + packet_length = length - @prop_header_length + <> = tail + decode_props(%{pktin | packet: packet}, rest) + end + + defp decode_props(pktin, <<@full_len::16, _length::16, full_len::32, rest::bytes>>) do + decode_props(%{pktin | full_len: full_len}, rest) + end + + defp decode_props(pktin, <<@buffer_id::16, _length::16, buffer_id::32, rest::bytes>>) do + decode_props(%{pktin | buffer_id: buffer_id}, rest) + end + + defp decode_props(pktin, <<@table_id::16, _length::16, table_id::8, _::24, rest::bytes>>) do + decode_props(%{pktin | table_id: table_id}, rest) + end + + defp decode_props(pktin, <<@cookie::16, _length::16, _::32, cookie::64, rest::bytes>>) do + decode_props(%{pktin | cookie: cookie}, rest) + end + + defp decode_props(pktin, <<@reason::16, _length::16, reason_int::8, _::24, rest::bytes>>) do + reason = Openflow.Enums.to_atom(reason_int, :packet_in_reason) + decode_props(%{pktin | reason: reason}, rest) + end + + defp decode_props(pktin, <<@metadata::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + match_field_length = length - @prop_header_length + + <> = + tail + + match_len = 4 + byte_size(match_fields_bin) + padding = Openflow.Utils.pad_length(match_len, 8) + match_bin = <<1::16, match_len::16, match_fields_bin::bytes, 0::size(padding)-unit(8)>> + {fields, _rest} = Openflow.Match.read(match_bin) + decode_props(%{pktin | metadata: fields}, rest) + end + + defp decode_props(pktin, <<@userdata::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + userdata_length = length - @prop_header_length + <> = tail + decode_props(%{pktin | userdata: userdata}, rest) + end + + defp decode_props(pktin, <<@continuation::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length - 4 + <<_pad::32, data::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + + pktin + |> decode_continuations(data) + |> decode_props(rest) + end + + defp decode_props(pktin, <<_::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <<_data::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + decode_props(pktin, rest) + end + + defp decode_continuations(pktin, ""), do: pktin + + defp decode_continuations(pktin, <<@nxcpt_bridge::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <> = tail + decode_continuations(%{pktin | continuation_bridge: bridge}, rest) + end + + defp decode_continuations(pktin, <<@nxcpt_stack::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = (length - @prop_header_length) * 8 + <> = tail + decode_continuations(%{pktin | continuation_stack: pktin.continuation_stack ++ [stack]}, rest) + end + + defp decode_continuations(pktin, <<@nxcpt_mirrors::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <> = tail + decode_continuations(%{pktin | continuation_mirrors: mirrors}, rest) + end + + defp decode_continuations(pktin, <<@nxcpt_conntracked::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <<_::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + decode_continuations(%{pktin | continuation_conntracked: true}, rest) + end + + defp decode_continuations(pktin, <<@nxcpt_table_id::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + <> = tail + decode_continuations(%{pktin | continuation_table_id: table_id}, rest) + end + + defp decode_continuations(pktin, <<@nxcpt_cookie::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + <> = tail + decode_continuations(%{pktin | continuation_cookie: cookie}, rest) + end + + defp decode_continuations(pktin, <<@nxcpt_actions::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length - 4 + + <<_pad::32, actions::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = + tail + + decode_continuations(%{pktin | continuation_actions: Openflow.Action.read(actions)}, rest) + end + + defp decode_continuations(pktin, <<@nxcpt_action_set::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <> = tail + decode_continuations(%{pktin | continuation_action_set: action_set}, rest) + end + + defp decode_continuations(pktin, _) do + decode_continuations(pktin, "") + end +end diff --git a/lib/ovsdb/open_vswitch.ex b/lib/ovsdb/open_vswitch.ex index 3c7dbdf..530c670 100644 --- a/lib/ovsdb/open_vswitch.ex +++ b/lib/ovsdb/open_vswitch.ex @@ -34,12 +34,12 @@ defmodule OVSDB.OpenvSwitch do disable_in_band: "true" ) """ - def add_br(pid, [_|_] = options) do + def add_br(pid, [_ | _] = options) do br_uuids = GenServer.call(pid, {:sync_get, @open_vswitch, "bridges"}) add_br_options = [ bridge: options[:name], - fail_mode: options[:fail_mode] || "standalone", + fail_mode: options[:fail_mode] || "standalone", datapath_id: options[:datapath_id], disable_in_band: options[:disable_in_band], br_uuids: br_uuids @@ -83,10 +83,11 @@ defmodule OVSDB.OpenvSwitch do protocol: "OpenFlow13" ) """ - def set_controller(pid, [_|_] = options) do + def set_controller(pid, [_ | _] = options) do case find_by_name(pid, @bridge, options[:bridge]) do :not_found -> {:error, :not_found} + %{"_uuid" => _uuid} -> set_ctl_opts = [ bridge: options[:bridge], @@ -94,7 +95,7 @@ defmodule OVSDB.OpenvSwitch do connection_mode: options[:connection_mode] || "out-of-band", controller_rate_limit: options[:controller_rate_limit] || 1000, controller_burst_limit: options[:controller_burst_limit] || 100, - protocol: options[:protocol] || "OpenFlow13", + protocol: options[:protocol] || "OpenFlow13" ] GenServer.call(pid, {:set_controller, set_ctl_opts}) @@ -111,10 +112,11 @@ defmodule OVSDB.OpenvSwitch do ofport_request: 99 ) """ - def add_port(pid, [_|_] = options) do + def add_port(pid, [_ | _] = options) do case find_by_name(pid, @bridge, options[:bridge]) do :not_found -> {:error, :not_found} + %{"_uuid" => _uuid, "ports" => ports} -> port_opts = [ bridge: options[:bridge], @@ -146,14 +148,16 @@ defmodule OVSDB.OpenvSwitch do @doc """ iex> OpenvSwitch.del_port(client_pid, bridge: "br0", port: "vxlan5") """ - def del_port(pid, [_|_] = options) do + def del_port(pid, [_ | _] = options) do case find_by_name(pid, @bridge, options[:bridge]) do :not_found -> {:error, :not_found} + %{"_uuid" => _uuid, "ports" => ports} -> case find_by_name(pid, @port, options[:port]) do :not_found -> {:error, :not_found} + %{"_uuid" => port_uuid} -> new_ports = del_elem_from_set(ports, port_uuid) @@ -257,34 +261,41 @@ defmodule OVSDB.OpenvSwitch do bridge = %{ports: options[:ports]} replies = - xact([ - :eovsdb_op.update(@bridge, eq_br_name, bridge), - :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) - ], pid) + xact( + [ + :eovsdb_op.update(@bridge, eq_br_name, bridge), + :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) + ], + pid + ) {:reply, replies, state} end def handle_call({:set_controller, options}, _from, state) do %State{client_pid: pid, ovs_uuid: ovs} = state + controller = %{ target: options[:target], connection_mode: options[:connection_mode], controller_rate_limit: options[:controller_rate_limit], - controller_burst_limit: options[:controller_burst_limit], + controller_burst_limit: options[:controller_burst_limit] } bridge = %{ protocols: options[:protocol], - controller: ["named-uuid", "controller"], + controller: ["named-uuid", "controller"] } replies = - xact([ - :eovsdb_op.insert(@controller, controller, "controller"), - :eovsdb_op.update(@bridge, [{"name", "==", options[:bridge]}], bridge), - :eovsdb_op.mutate(@open_vswitch, [{"_uuid", "==", ovs}], [{"next_cfg", "+=", 1}]) - ], pid) + xact( + [ + :eovsdb_op.insert(@controller, controller, "controller"), + :eovsdb_op.update(@bridge, [{"name", "==", options[:bridge]}], bridge), + :eovsdb_op.mutate(@open_vswitch, [{"_uuid", "==", ovs}], [{"next_cfg", "+=", 1}]) + ], + pid + ) {:reply, replies, state} end @@ -294,7 +305,7 @@ defmodule OVSDB.OpenvSwitch do port = %{ name: options[:name], - interfaces: ["named-uuid", "interface"], + interfaces: ["named-uuid", "interface"] } iface_options = [ @@ -377,6 +388,7 @@ defmodule OVSDB.OpenvSwitch do defp do_find_by_name(pid, table, name) do query = :eovsdb_op.select('*', table, [{"name", "==", name}]) + case xact(query, pid) do [%{"rows" => []}] -> :not_found [%{"rows" => [row]}] -> row @@ -386,11 +398,11 @@ defmodule OVSDB.OpenvSwitch do defp make_ovsdb_map(map), do: make_ovsdb_map([], map) defp make_ovsdb_map(acc, []), do: ["map", Enum.reverse(acc)] - defp make_ovsdb_map(acc, [[_key, nil]|rest]), do: make_ovsdb_map(acc, rest) - defp make_ovsdb_map(acc, [attr|rest]), do: make_ovsdb_map([attr|acc], rest) + defp make_ovsdb_map(acc, [[_key, nil] | rest]), do: make_ovsdb_map(acc, rest) + defp make_ovsdb_map(acc, [attr | rest]), do: make_ovsdb_map([attr | acc], rest) defp make_ovsdb_set(nil), do: ["set", []] - defp make_ovsdb_set([_|_] = list), do: ["set", list] + defp make_ovsdb_set([_ | _] = list), do: ["set", list] defp make_ovsdb_set(value), do: value defp add_elem_to_set(["set", []], value), do: value diff --git a/lib/tres/controller.ex b/lib/tres/controller.ex index 62cd4ed..19abd7f 100644 --- a/lib/tres/controller.ex +++ b/lib/tres/controller.ex @@ -14,6 +14,7 @@ defmodule Tres.Controller do def handler_spec(dpid) do {cb_mod, cb_args} = Tres.Utils.get_callback_module() + %{ id: {__MODULE__, dpid}, start: {cb_mod, :start_link, [[dpid, cb_args]]}, diff --git a/lib/tres/message_handler_sup.ex b/lib/tres/message_handler_sup.ex index 562b476..24bb2f2 100644 --- a/lib/tres/message_handler_sup.ex +++ b/lib/tres/message_handler_sup.ex @@ -7,6 +7,7 @@ defmodule Tres.MessageHandlerSup do def init(_init_args) do children = [] + Supervisor.init( children, strategy: :one_for_one, diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index 70d911b..f9f8786 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -147,11 +147,16 @@ defmodule Tres.MessageHelper do Openflow.MeterMod.new( xid: options[:xid] || 0, command: :delete, - meter_id: options[:meter_id] || 0, + meter_id: options[:meter_id] || 0 ) send_message(meter_mod, datapath_id, options[:blocking] || false) end + + defp send_resume(datapath_id, options) do + resume = Openflow.NxResume.new(options) + send_message(resume, datapath_id, options[:blocking] || false) + end end end end diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 05e723d..69a3cd3 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -50,7 +50,7 @@ defmodule Tres.SecureChannel do {:tcp, socket, packet}, state, %State{socket: socket, transport: transport} = state_data - ) do + ) do transport.setopts(socket, active: :once) handle_packet(packet, state_data, state, []) end @@ -137,9 +137,7 @@ defmodule Tres.SecureChannel do # INIT state defp handle_INIT(:enter, _old_state, state_data) do - debug( - "Initiate HELLO handshake: " <> "#{state_data.ip_addr}:#{state_data.port}" - ) + debug("Initiate HELLO handshake: " <> "#{state_data.ip_addr}:#{state_data.port}") initiate_hello_handshake(state_data) end @@ -153,19 +151,14 @@ defmodule Tres.SecureChannel do end defp handle_INIT(:internal, message, _state_data) do - debug( - "Hello handshake in progress, " <> "dropping message: #{inspect(message)}" - ) + debug("Hello handshake in progress, " <> "dropping message: #{inspect(message)}") :keep_state_and_data end # CONNECTING state defp handle_CONNECTING(:enter, :INIT, state_data) do - debug( - "Initiate FEATURES handshake:" <> - " #{state_data.ip_addr}:#{state_data.port}" - ) + debug("Initiate FEATURES handshake:" <> " #{state_data.ip_addr}:#{state_data.port}") initiate_features_handshake(state_data) end @@ -195,8 +188,7 @@ defmodule Tres.SecureChannel do defp handle_CONNECTING(:internal, {:openflow, message}, _state_data) do debug( - "Features handshake in progress," <> - " dropping message: #{inspect(message.__struct__)}" + "Features handshake in progress," <> " dropping message: #{inspect(message.__struct__)}" ) :keep_state_and_data @@ -209,6 +201,7 @@ defmodule Tres.SecureChannel do # CONNECTED state defp handle_CONNECTED(:enter, :CONNECTING, state_data) do _tref = schedule_xactdb_ageout() + case init_handler(state_data) do %State{} = new_state_data -> start_periodic_idle_check() @@ -340,10 +333,12 @@ defmodule Tres.SecureChannel do 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, :malformed_packet} -> - :ok = debug("malformed packet received from #{state_data.datapath_id}") + + {:error, {:malformed_packet, {_reason, st}}} -> + :ok = debug("malformed packet received from #{state_data.datapath_id} stack_trace: #{st}") handle_packet("", %{state_data | buffer: ""}, state, actions) end end @@ -375,8 +370,9 @@ defmodule Tres.SecureChannel do unless is_nil(message), do: send(state_data.handler_pid, message) XACT_KV.delete(state_data.xact_kv_ref, xid) end + defp process_xact_entry({:xact_entry, xid, message, _orig, from, _inserted_at}, state_data) - when is_tuple(from) do + when is_tuple(from) do reply = if is_nil(message), do: :noreply, else: message :ok = :gen_statem.reply(from, {:ok, reply}) XACT_KV.delete(state_data.xact_kv_ref, xid) @@ -558,8 +554,7 @@ defmodule Tres.SecureChannel do defp send_message(message, %State{socket: socket, transport: transport}) do if is_list(message) do for message <- message, - do: - debug("Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + do: debug("Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") else debug("Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") end diff --git a/lib/tres/switch_registry.ex b/lib/tres/switch_registry.ex index cad2612..30fb857 100644 --- a/lib/tres/switch_registry.ex +++ b/lib/tres/switch_registry.ex @@ -25,6 +25,7 @@ defmodule Tres.SwitchRegistry do def send_message(message, dpid, _blocking = true) do blocking_send_message(message, dpid) end + def send_message(message, dpid, _blocking) do send_message(message, dpid) end diff --git a/lib/tres/utils.ex b/lib/tres/utils.ex index 21ed74e..75af3b9 100644 --- a/lib/tres/utils.ex +++ b/lib/tres/utils.ex @@ -14,11 +14,11 @@ defmodule Tres.Utils do def start_openflow_listener do :ranch.start_listener( - _ref = Tres, - _trasport = :ranch_tcp, + _ref = Tres, + _trasport = :ranch_tcp, _transport_opts = transport_options(), - _protocol = @connection_manager, - _protocol_opts = [] + _protocol = @connection_manager, + _protocol_opts = [] ) end diff --git a/test/ofp_action_test.exs b/test/ofp_action_test.exs index 616cfc8..5580700 100644 --- a/test/ofp_action_test.exs +++ b/test/ofp_action_test.exs @@ -631,14 +631,14 @@ defmodule OfpActionTest do test_file = "test/packet_data/nx_clone.raw" packet = File.read!(test_file) actions = Openflow.Action.read(packet) + clone = - Openflow.Action.NxClone.new( - [ - Openflow.Action.PushVlan.new(), - Openflow.Action.SetField.new({:vlan_vid, 5}), - Openflow.Action.Output.new(10) - ] - ) + Openflow.Action.NxClone.new([ + Openflow.Action.PushVlan.new(), + Openflow.Action.SetField.new({:vlan_vid, 5}), + Openflow.Action.Output.new(10) + ]) + actions_bin = Openflow.Action.to_binary(clone) assert actions_bin == packet assert actions == [clone] diff --git a/test/ofp_packet_in2_test.exs b/test/ofp_packet_in2_test.exs index e9b7ef7..45eff9c 100644 --- a/test/ofp_packet_in2_test.exs +++ b/test/ofp_packet_in2_test.exs @@ -14,9 +14,9 @@ defmodule OfpPacketIn2Test do assert pktin.full_len == 64 assert pktin.table_id == 7 assert pktin.buffer_id == 0x114 - assert pktin.cookie == 0xfedcba9876543210 + assert pktin.cookie == 0xFEDCBA9876543210 assert pktin.reason == :action - assert pktin.metadata == [metadata: 0x5a5a5a5a5a5a5a5a] + assert pktin.metadata == [metadata: 0x5A5A5A5A5A5A5A5A] assert pktin.userdata == <<1, 2, 3, 4, 5>> end end