From 1cb687433c9d2adae1f523ca904f59f524212d15 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Thu, 28 Mar 2019 18:17:01 +0900 Subject: [PATCH] add documents --- lib/openflow/actions/dec_nw_ttl.ex | 14 +++ lib/openflow/actions/group.ex | 20 ++++ lib/openflow/actions/nx_reg_load.ex | 28 ++++++ lib/openflow/actions/nx_reg_move.ex | 133 ++++++++++++++++++++++++++ lib/openflow/actions/nx_resubmit.ex | 49 ++++++++++ lib/openflow/actions/nx_set_tunnel.ex | 18 ++++ lib/openflow/actions/output.ex | 4 +- lib/openflow/actions/pop_mpls.ex | 14 +-- lib/openflow/actions/pop_vlan.ex | 11 +-- lib/openflow/actions/push_mpls.ex | 6 -- lib/openflow/actions/push_vlan.ex | 5 - lib/openflow/actions/set_field.ex | 62 ++++++++++++ lib/openflow/actions/set_nw_ttl.ex | 20 ++++ lib/openflow/actions/set_queue.ex | 20 ++++ 14 files changed, 377 insertions(+), 27 deletions(-) diff --git a/lib/openflow/actions/dec_nw_ttl.ex b/lib/openflow/actions/dec_nw_ttl.ex index 30c6b0d..2b86304 100644 --- a/lib/openflow/actions/dec_nw_ttl.ex +++ b/lib/openflow/actions/dec_nw_ttl.ex @@ -1,21 +1,35 @@ defmodule Openflow.Action.DecNwTtl do @moduledoc """ + Decrement IP TTL """ defstruct([]) alias __MODULE__ + @type t :: %DecNwTtl{} + + @spec ofpat() :: 24 def ofpat, do: 24 + @doc """ + Create a new dec_nw_ttl action struct + + ```elixir + iex> %DecNwTtl{} = DecNwTtl.new() + ``` + """ + @spec new() :: t() def new do %DecNwTtl{} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%DecNwTtl{}) do <<24::16, 8::16, 0::size(4)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<24::16, 8::16, _::size(4)-unit(8)>>) do %DecNwTtl{} end diff --git a/lib/openflow/actions/group.ex b/lib/openflow/actions/group.ex index ff2d756..2aeef63 100644 --- a/lib/openflow/actions/group.ex +++ b/lib/openflow/actions/group.ex @@ -1,18 +1,38 @@ defmodule Openflow.Action.Group do + @moduledoc """ + Apply Group + """ + defstruct(id: 0) alias __MODULE__ + @type t :: %Group{id: 0..0xFFFFFFFF} + + @spec ofpat() :: 22 def ofpat, do: 22 + @doc """ + Create a new group action struct + + ## Options + - id: Group identifier + + ```elixir + iex> %Group{id: 10} = Group.new(10) + ``` + """ + @spec new(0..0xFFFFFFFF) :: t() def new(id) do %Group{id: id} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%Group{id: id}) do <<22::16, 8::16, id::32>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<22::16, 8::16, id::32>>) do %Group{id: id} end diff --git a/lib/openflow/actions/nx_reg_load.ex b/lib/openflow/actions/nx_reg_load.ex index fbd4b22..8a9ccd7 100644 --- a/lib/openflow/actions/nx_reg_load.ex +++ b/lib/openflow/actions/nx_reg_load.ex @@ -1,4 +1,32 @@ defmodule Openflow.Action.NxRegLoad do + @moduledoc """ + Copies value[0:n_bits] to dst[ofs:ofs+n_bits], where a[b:c] denotes the bits + within 'a' numbered 'b' through 'c' (not including bit 'c'). Bit numbering + starts at 0 for the least-significant bit, 1 for the next most significant + bit, and so on. + + 'dst' is an nxm_header with nxm_hasmask=0. See the documentation for + NXAST_REG_MOVE, above, for the permitted fields and for the side effects of + loading them. + + The 'ofs' and 'n_bits' fields are combined into a single 'ofs_nbits' field + to avoid enlarging the structure by another 8 bytes. To allow 'n_bits' to + take a value between 1 and 64 (inclusive) while taking up only 6 bits, it is + also stored as one less than its true value: + + ``` + 15 6 5 0 + +------------------------------+------------------+ + | ofs | n_bits - 1 | + +------------------------------+------------------+ + ``` + + The switch will reject actions for which ofs+n_bits is greater than the + width of 'dst', or in which any bits in 'value' with value 2n_bits or + greater are set to 1, with error type OFPET_BAD_ACTION, code + OFPBAC_BAD_ARGUMENT. + """ + import Bitwise defstruct( diff --git a/lib/openflow/actions/nx_reg_move.ex b/lib/openflow/actions/nx_reg_move.ex index fc8071f..defcb65 100644 --- a/lib/openflow/actions/nx_reg_move.ex +++ b/lib/openflow/actions/nx_reg_move.ex @@ -1,4 +1,94 @@ defmodule Openflow.Action.NxRegMove do + @moduledoc """ + Copies src[src_ofs:src_ofs+n_bits] to dst[dst_ofs:dst_ofs+n_bits], where + a[b:c] denotes the bits within 'a' numbered 'b' through 'c' (not including + bit 'c'). Bit numbering starts at 0 for the least-significant bit, 1 for + the next most significant bit, and so on. + + The following nxm_header values are potentially acceptable as 'src': + - `:in_port` + - `:eth_dst` + - `:eth_src` + - `:eth_type` + - `:vlan_tci` + - `:ip_tos` + - `:ip_proto` + - `:ip_src` + - `:ip_dst` + - `:tcp_src` + - `:tcp_dst` + - `:udp_src` + - `:udp_dst` + - `:icmp_type` + - `:icmp_code` + - `:arp_op` + - `:arp_spa` + - `:arp_tpa` + - `:tun_id` + - `:arp_sha` + - `:arp_tha` + - `:icmpv6_type` + - `:icmpv6_code` + - `:nd_sll` + - `:nd_tll` + - `:reg(idx)` for idx in the switch's accepted range. + - `:pkt_mark` + - `:tun_ipv4_src` + - `:tun_ipv4_dst` + + The following nxm_header values are potentially acceptable as 'dst': + - `:eth_dst` + - `:eth_src` + - `:ip_tos` + - `:ip_src` + - `:ip_dst` + - `:tcp_src` + - `:tcp_dst` + - `:udp_src` + - `:udp_dst` + - `:icmp_type` + - `:icmp_code` + - `:icmpv6_type` + - `:icmpv6_code` + - `:arp_sha` + - `:arp_tha` + - `:arp_op` + - `:arp_spa` + - `:arp_tpa` + + Modifying any of the above fields changes the corresponding packet header. + + - `:in_port` + + - `:reg(idx)` for idx in the switch's accepted range. + + - `:pkt_mark` + + - `:vlan_tci`. Modifying this field's value has side effects on the + packet's 802.1Q header. Setting a value with CFI=0 removes the 802.1Q + header (if any), ignoring the other bits. Setting a value with CFI=1 + adds or modifies the 802.1Q header appropriately, setting the TCI field + to the field's new value (with the CFI bit masked out). + + - `:tun_id`, `:tun_ipv4_src`, `:tun_ipv4_dst`. Modifying + any of these values modifies the corresponding tunnel header field used + for the packet's next tunnel encapsulation, if allowed by the + configuration of the output tunnel port. + + A given nxm_header value may be used as 'src' or 'dst' only on a flow whose + nx_match satisfies its prerequisites. For example, NXM_OF_IP_TOS may be + used only if the flow's nx_match includes an nxm_entry that specifies + nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, and nxm_value=0x0800. + + The switch will reject actions for which src_ofs+n_bits is greater than the + width of 'src' or dst_ofs+n_bits is greater than the width of 'dst' with + error type OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT. + + This action behaves properly when 'src' overlaps with 'dst', that is, it + behaves as if 'src' were copied out to a temporary buffer, then the + temporary buffer copied to 'dst'. + """ + defstruct( n_bits: 0, src_offset: 0, @@ -13,6 +103,47 @@ defmodule Openflow.Action.NxRegMove do alias __MODULE__ alias Openflow.Action.Experimenter + @type t :: %NxRegMove{ + n_bits: 0..0xFFFF, + src_offset: 0..0xFFFF, + dst_offset: 0..0xFFFF, + src_field: atom(), + dst_field: atom() + } + + @doc """ + Creates a new reg_move action struct + + ## Options: + - n_bits: Number of bits + - src_offset: Starting bit offset in source + - dst_offset: Starting bit offset in destination + - src_field: oxm/nxm field name for source field + - dst_field: oxm/nxm field name for destination field + + ## Example + + ```elixir + # 1. move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[] + iex> NxRegMove.new(src_field: :nx_in_port, :nx_vlan_tci) + + # 2. move:NXM_NX_TUN_ID[40..57]->NXM_NX_REG1[0..17] + iex> NxRegMove.new( + iex> src_field: :tun_id, + iex> src_offset: 40, + iex> dst_field: :reg1 + iex> dst_offset: 0, + iex> n_bits: 18 + iex> ) + ``` + """ + @spec new( + n_bits: 0..0xFFFF, + src_offset: 0..0xFFFF, + dst_offset: 0..0xFFFF, + src_field: atom(), + dst_field: atom() + ) :: t() def new(options \\ []) do src_field = options[:src_field] || raise "src_field must be specified" dst_field = options[:dst_field] || raise "dst_field must be specified" @@ -27,6 +158,7 @@ defmodule Openflow.Action.NxRegMove do } end + @spec to_binary(t()) :: binary() def to_binary(%NxRegMove{} = move) do src_field_bin = Openflow.Match.codec_header(move.src_field) dst_field_bin = Openflow.Match.codec_header(move.dst_field) @@ -42,6 +174,7 @@ defmodule Openflow.Action.NxRegMove do >>) end + @spec read(binary()) :: t() def read(<<@experimenter::32, @nxast::16, body::bytes>>) do <> = body diff --git a/lib/openflow/actions/nx_resubmit.ex b/lib/openflow/actions/nx_resubmit.ex index d0dbd11..4d07b39 100644 --- a/lib/openflow/actions/nx_resubmit.ex +++ b/lib/openflow/actions/nx_resubmit.ex @@ -1,4 +1,16 @@ defmodule Openflow.Action.NxResubmit do + @moduledoc """ + Searches the flow table again, using a flow that is slightly modified from the original lookup: + + Following the lookup, the original in_port is restored. + + If the modified flow matched in the flow table, then the corresponding + actions are executed. Afterward, actions following NXAST_RESUBMIT in + the original set of actions, if any, are executed; any changes made to + the packet (e.g. changes to VLAN) by secondary actions persist when + those actions are executed, although the original in_port is restored. + """ + defstruct(in_port: :in_port) @experimenter 0x00002320 @@ -7,10 +19,46 @@ defmodule Openflow.Action.NxResubmit do alias __MODULE__ alias Openflow.Action.Experimenter + @type t :: %{in_port: port_no()} + + @type port_no :: + :max + | :in_port + | :table + | :normal + | :flood + | :all + | :controller + | :local + | :none + | 1..0xFFFF + + @doc """ + Creates a new nx_resubmit action struct + + ## Options: + - in_port: New in_port for checking flow table in the one of the `port_no()` type + + ## Note: + If the modified flow matchd in the flow table, then the corresponding actions are executed,\\ + Afterward, actions following the resubmit in the original set of actions, if any, are executed;\\ + any changes made to the packet by secondary actions persist when those actions are executed, + although the original in_port is restored + + ## Example: + + ```elixir + iex> %NxResubmit{in_port: :in_port} = NxResubmit.new() + iex> %NxResubmit{in_port: 1} = NxResubmit.new(1) + ``` + + """ + @spec new(port_no()) :: t() def new(in_port \\ :in_port) do %NxResubmit{in_port: in_port} end + @spec to_binary(t()) :: binary() def to_binary(%NxResubmit{in_port: in_port}) do in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no) @@ -22,6 +70,7 @@ defmodule Openflow.Action.NxResubmit do >>) end + @spec read(binary()) :: t() 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} diff --git a/lib/openflow/actions/nx_set_tunnel.ex b/lib/openflow/actions/nx_set_tunnel.ex index c7c6b59..b0f836e 100644 --- a/lib/openflow/actions/nx_set_tunnel.ex +++ b/lib/openflow/actions/nx_set_tunnel.ex @@ -1,4 +1,8 @@ defmodule Openflow.Action.NxSetTunnel do + @moduledoc """ + Set encapsulating tunnel ID. + """ + defstruct(tunnel_id: 0) @experimenter 0x00002320 @@ -7,10 +11,23 @@ defmodule Openflow.Action.NxSetTunnel do alias __MODULE__ alias Openflow.Action.Experimenter + @type tunnel_id :: 0..0xFFFFFFFF + @type t :: %NxSetTunnel{tunnel_id: tunnel_id()} + + @doc """ + Creates a new set_tunnel action struct + + ## Example: + ```elixir + iex> %NxSetTunnel{tunnel_id: 1} = NxSetTunnel.new(1) + ``` + """ + @spec new(tunnel_id()) :: t() def new(tunnel_id) do %NxSetTunnel{tunnel_id: tunnel_id} end + @spec to_binary(t()) :: binary() def to_binary(%NxSetTunnel{tunnel_id: tunnel_id}) do Experimenter.pack_exp_header(<< @experimenter::32, @@ -20,6 +37,7 @@ defmodule Openflow.Action.NxSetTunnel do >>) end + @spec read(binary()) :: t() def read(<<@experimenter::32, @nxast::16, _::16, tunnel_id::32>>) do %NxSetTunnel{tunnel_id: tunnel_id} end diff --git a/lib/openflow/actions/output.ex b/lib/openflow/actions/output.ex index e0c0bfd..2efe8bc 100644 --- a/lib/openflow/actions/output.ex +++ b/lib/openflow/actions/output.ex @@ -31,8 +31,8 @@ defmodule Openflow.Action.Output do Create a new output action structure ## Options: - - `port_number`: port number that sends a packet from - - `max_len`: byte length of the packet that sends to the controller + - `port_number`: Output port + - `max_len`: Max length to send to controller ## Example diff --git a/lib/openflow/actions/pop_mpls.ex b/lib/openflow/actions/pop_mpls.ex index 3d82f6e..3695991 100644 --- a/lib/openflow/actions/pop_mpls.ex +++ b/lib/openflow/actions/pop_mpls.ex @@ -3,12 +3,6 @@ defmodule Openflow.Action.PopMpls do Pop the out MPLS label note: The one of ETH__P_MPLS_* is needed to be specified to eth_type field - - send_flow_mod_add( - datapath_id, - match: Match.new(0x8847), - instructions: ApplyActions.new(PopMpls.new()) - ) """ defstruct(ethertype: 0x8847) @@ -25,7 +19,11 @@ defmodule Openflow.Action.PopMpls do @doc """ Create a new pop_mpls action struct - 0x8847(ETH_P_MPLS_UC) as default value. + note: 0x8847(ETH_P_MPLS_UC) as default value. + + ```elixir + iex> %PopMpls{ethertype: 0x8847} = PopMpls.new() + ``` """ @spec new() :: t() @spec new(ethertype :: 0..0xFFFF) :: t() @@ -33,10 +31,12 @@ defmodule Openflow.Action.PopMpls do %PopMpls{ethertype: ethertype} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%PopMpls{ethertype: ethertype}) do <<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do %PopMpls{ethertype: ethertype} end diff --git a/lib/openflow/actions/pop_vlan.ex b/lib/openflow/actions/pop_vlan.ex index 5ad96c9..87a8e60 100644 --- a/lib/openflow/actions/pop_vlan.ex +++ b/lib/openflow/actions/pop_vlan.ex @@ -1,13 +1,6 @@ defmodule Openflow.Action.PopVlan do @moduledoc """ Pop the outer VLAN tag. - - note: The one of ETH__P_802_* is needed to be specified to eth_type field - send_flow_mod_add( - datapath_id, - match: Match.new(eth_type: 0x8100), - instructions: ApplyActions.new(PopVlan.new()) - ) """ defstruct([]) @@ -21,6 +14,10 @@ defmodule Openflow.Action.PopVlan do @doc """ Create a new pop_vlan action struct + + ````elixir + iex> %PopVlan{} = PopVlan.new() + ```` """ @spec new() :: t() def new do diff --git a/lib/openflow/actions/push_mpls.ex b/lib/openflow/actions/push_mpls.ex index caa2141..4f3383f 100644 --- a/lib/openflow/actions/push_mpls.ex +++ b/lib/openflow/actions/push_mpls.ex @@ -1,12 +1,6 @@ defmodule Openflow.Action.PushMpls do @moduledoc """ Push a new MPLS label - - send_flow_mod_add( - datapath_id, - match: Match.new(), - instruction: ApplyActions.new(PushMpls.new()) - ) """ defstruct(ethertype: 0x8847) diff --git a/lib/openflow/actions/push_vlan.ex b/lib/openflow/actions/push_vlan.ex index 25221ac..e922adc 100644 --- a/lib/openflow/actions/push_vlan.ex +++ b/lib/openflow/actions/push_vlan.ex @@ -1,11 +1,6 @@ defmodule Openflow.Action.PushVlan do @moduledoc """ Push a new VLAN tag - - send_flow_mod_add( - datapath_id, - instructions: ApplyActions.new(PushVlan.new()) - ) """ defstruct(ethertype: 0x8100) diff --git a/lib/openflow/actions/set_field.ex b/lib/openflow/actions/set_field.ex index 8884e8f..a7c9699 100644 --- a/lib/openflow/actions/set_field.ex +++ b/lib/openflow/actions/set_field.ex @@ -1,12 +1,74 @@ defmodule Openflow.Action.SetField do + @moduledoc """ + Set a header field using OXM TLV format. + """ + defstruct(field: nil) alias __MODULE__ + @type t :: %SetField{field: Keyword.t()} + @set_field_size 8 def ofpat, do: 25 + @doc """ + Create a new set_field action struct + + note: The following oxm(nxm)_header values are potentially acceptable as `field`: + + - :tun_id + - :tun_ipv4_src + - :tun_ipv4_dst + - :tun_ipv6_src + - :tun_ipv6_dst + - :tun_flags + - :tun_gbp_id + - :tun_gbp_flags + - :tun_metadata{0..63} + - :in_port + - :pkt_mark + - :ct_mark + - :ct_label + - :reg{0..15} + - :xreg{0..8} + - :xxreg{0..4} + - :eth_src + - :eth_dst + - :vlan_tci + - :mpls_ttl + - :ip_src + - :ip_dst + - :ipv6_src + - :ipv6_dst + - :ipv6_label + - :ip_tos + - :ip_ecn + - :ip_ttl + - :arp_op + - :arp_spa + - :arp_tpa + - :arp_sha + - :arp_tha + - :tcp_src + - :tcp_dst + - :udp_src + - :udp_dst + - :icmp_type + - :icmp_code + - :icmpv6_type + - :icmpv6_code + - :nd_target + - :nd_sll + - :nd_tll + - :metadata + + ```elixir + iex> %SetField{field: [reg1: 10]} = SetField.new(reg1: 10) + ``` + """ + @spec new(Keyword.t()) :: t() def new([{_field, _value}] = oxm_field) do %SetField{field: oxm_field} end diff --git a/lib/openflow/actions/set_nw_ttl.ex b/lib/openflow/actions/set_nw_ttl.ex index 48a296a..1c6e667 100644 --- a/lib/openflow/actions/set_nw_ttl.ex +++ b/lib/openflow/actions/set_nw_ttl.ex @@ -1,18 +1,38 @@ defmodule Openflow.Action.SetNwTtl do + @moduledoc """ + Set IP TTL + """ + defstruct(ttl: 0) alias __MODULE__ + @type t :: %SetNwTtl{ttl: 0..0xFF} + + @spec ofpat() :: 23 def ofpat, do: 23 + @doc """ + Create a new set_nw_ttl action struct + + ## Options: + - IP TTL + + ```elixir + iex> %SetNwTtl{ttl: 64} = SetNwTtl.new(_ttl = 64) + ``` + """ + @spec new(ttl :: 0..0xFF) :: t() def new(ttl) do %SetNwTtl{ttl: ttl} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%SetNwTtl{ttl: ttl}) do <<23::16, 8::16, ttl::8, 0::size(3)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<23::16, 8::16, ttl::8, _::size(3)-unit(8)>>) do %SetNwTtl{ttl: ttl} end diff --git a/lib/openflow/actions/set_queue.ex b/lib/openflow/actions/set_queue.ex index 001f4ff..2e96d80 100644 --- a/lib/openflow/actions/set_queue.ex +++ b/lib/openflow/actions/set_queue.ex @@ -1,18 +1,38 @@ defmodule Openflow.Action.SetQueue do + @moduledoc """ + Set queue id when outputting to a port + """ + defstruct(id: 0) alias __MODULE__ + @type t :: %SetQueue{id: 0..0xFFFFFFFF} + + @spec ofpat() :: 21 def ofpat, do: 21 + @doc """ + Create a new set_queue action struct + + ## Options: + - Queue id for the packets + + ```elixir + iex> %SetQueue{id: 1} = SetQueue.new(_id = 1) + ``` + """ + @spec new(id :: 0..0xFFFFFFFF) :: t() def new(id) do %SetQueue{id: id} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%SetQueue{id: id}) do <<21::16, 8::16, id::32>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<21::16, 8::16, id::32>>) do %SetQueue{id: id} end