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__ alias Openflow.Action.Experimenter 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) Experimenter.pack_exp_header(<< @experimenter::32, @nxast::16, 0::size(2)-unit(8), flags_int::16, range_flags_int::16, ranges_bin::bytes >>) 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