diff --git a/.formatter.exs b/.formatter.exs index b874ba3..aafb2f5 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,5 +1,5 @@ [ - inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"], + inputs: ["mix.exs", "{config,lib,test,priv}/**/*.{ex,exs}"], locals_without_parens: [ # Formatter tests diff --git a/.gitignore b/.gitignore index 674fc49..aeed094 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ erl_crash.dump # Examples */polaris/* + +/bin/enum_gen diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..097035a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +sudo: true +dist: xenial +language: elixir +elixir: + - "1.8.1" +otp_release: + - "21.3.6" + +before_install: + - sudo apt-get install -y wget curl gdebi debhelper unbound + - sudo apt-get install build-essential fakeroot autoconf automake libssl-dev bzip2 openssl graphviz python-all procps python-qt4 python-zopeinterface python-twisted-conch libtool dh-autoreconf libunbound-dev + - wget https://github.com/openvswitch/ovs/archive/v2.11.0.tar.gz + - tar xvzf v2.11.0.tar.gz + - cd ovs-2.11.0/ + - sudo DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary + - sudo dpkg -i ../python-openvswitch_2.11.0-1_all.deb + - sudo dpkg -i ../libopenvswitch_2.11.0-1_amd64.deb + - sudo dpkg -i ../libopenvswitch-dev_2.11.0-1_amd64.deb + - sudo dpkg -i ../openvswitch-common_2.11.0-1_amd64.deb + - sudo dpkg -i ../openvswitch-switch_2.11.0-1_amd64.deb + - sudo ovs-vsctl add-br br0 + - sudo ovs-vsctl set bridge br0 datapath_type=netdev + - sudo ovs-vsctl set bridge br0 protocols=OpenFlow13 + - sudo ovs-vsctl set bridge br0 other-config:datapath-id=0000000000000001 + - sudo ovs-vsctl set-controller br0 tcp:127.0.0.1:6653 + - sudo ovs-vsctl set-manager ptcp:6640 + - sudo ovs-vsctl show + - cd - + +script: + - MIX_ENV=test mix do compile, coveralls.json + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..114ed07 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +## develop (unreleased) + +### New Features +* tres/secure_channel: Add a new function for blocking send + +### Bugs Fixed +* tres/secure_channel: Fix to interpolate terms when logging +* tres/message_handler: Fix to use `DynamicSupervisor` instead of `:simple_one_for_one` diff --git a/LICENSE b/LICENSE index ea5a6eb..e3f63b1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,13 +1,12 @@ -Copyright [2018] [Eishun Kondoh] +# "THE SUSHI-WARE LICENSE" -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + wrote this file. - http://www.apache.org/licenses/LICENSE-2.0 +As long as you retain this notice you can do whatever you want +with this stuff. If we meet some day, and you think this stuff +is worth it, you can buy me a **sushi 🍣** in return. -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +(This license is based on ["THE BEER-WARE LICENSE" (Revision 42)]. + Thanks a lot, Poul-Henning Kamp ;) + +​["THE BEER-WARE LICENSE" (Revision 42)]: https://people.freebsd.org/~phk/ diff --git a/README.md b/README.md index 69efd9b..c391143 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # Tres - an Elixir OpenFlow development platform +[![Build Status](https://img.shields.io/travis/shun159/tres.svg?style=flat-square)](https://travis-ci.org/shun159/tres) +[![codecov](https://img.shields.io/codecov/c/github/shun159/tres/develop.svg?style=flat-square)](https://codecov.io/gh/shun159/tres) +[![Supported OTP version](https://img.shields.io/badge/erlang-22.x-blue.svg?style=flat-square)](http://erlang.org/) +[![LICENSE](https://img.shields.io/badge/license-SUSHI--WARE%F0%9F%8D%A3-blue.svg?style=flat-square)](https://github.com/MakeNowJust/sushi-ware) +[![hex version](https://img.shields.io/badge/hex-0.1.0-yellow.svg?style=flat-square)](https://hex.pm/packages/tres/0.1.0) + ## Overview Tres is a framework and set of helper libraries to develop OpenFlow controllers in Elixir. @@ -11,7 +17,7 @@ Tres is a framework and set of helper libraries to develop OpenFlow controllers ```elixir def deps do [ - {:tres, github: "shun159/tres", branch: "develop"} + {:tres, "~> 0.1.0"} ] end ``` @@ -25,7 +31,7 @@ config :tres, ``` To use `Tres.Controller` with your code, set the handler callback_module to your callback module. -This module must implement the `Module.start_link/2` that returns `on_start`. +This module must implement the `Module.start_link/1` that returns `on_start`. Set the callback_args to the terms you want pass to the `start_link/2` callback function. @@ -35,7 +41,7 @@ defmodule Sample do use GenServer use Tres.Controller - def start_link(datapath_id, _start_args) do + def start_link([datapath_id, _start_args]) do GenServer.start_link(__MODULE__, [datapath_id]) end @@ -70,9 +76,11 @@ $ iex -S mix - learning-switch: Simple Layer2 switch - leader-example: Simple election based multiple controller using Ulf Wiger's Locks Leader - patch\_panel: inteligent patch\_panel example +- simple\_router: An OpenFlow controller that emulates layer 3 switch (router). +- nx\_learning\_switch: Example for very simple-minded L2 switch using NXAST_LEARN action. License ------- -Tres is released under the Apache license Version 2.0: +Tres is released under the __SUSHI-WARE LICENSE__. -* https://www.apache.org/licenses/LICENSE-2.0 +__η§γ«ε―ΏεΈγ‚’γŠγ”γ£γ¦γγ γ•γ„__ diff --git a/bin/enum_gen b/bin/enum_gen deleted file mode 100755 index 6390ace..0000000 Binary files a/bin/enum_gen and /dev/null differ diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..0cf380b --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +codecov: + token: db65a412-7a83-4c38-ad5b-9c68677c6680 diff --git a/config/config.exs b/config/config.exs index 40896b3..c04ed23 100644 --- a/config/config.exs +++ b/config/config.exs @@ -11,7 +11,7 @@ config :tres, callback_args: [] config :logger, - level: :info, - format: "$date $time [$level] $metadata$message\n", - metadata: [:application], + level: :debug, + format: "$date $time [$level] $message\n", + metadata: [], handle_otp_reports: true diff --git a/coveralls.json b/coveralls.json new file mode 100644 index 0000000..4143d55 --- /dev/null +++ b/coveralls.json @@ -0,0 +1,11 @@ +{ + "skip_files": [ + "lib/openflow/enums.ex" + ], + + "coverage_options": { + "output_dir": "cover/", + "template_path": "lib/templates/html/htmlcov/", + "minimum_coverage": 0 + } +} diff --git a/examples/learning_switch/lib/learning_switch/ofctl.ex b/examples/learning_switch/lib/learning_switch/ofctl.ex index 21b123f..8c42401 100644 --- a/examples/learning_switch/lib/learning_switch/ofctl.ex +++ b/examples/learning_switch/lib/learning_switch/ofctl.ex @@ -63,14 +63,18 @@ defmodule LearningSwitch.Ofctl do end defp init_flow_tables(datapath_id) do + :ok = onf_bundle_open(datapath_id, bundle_id: 1, flags: [:atomic, :ordered]) + for flow_options <- [ add_default_broadcast_flow_entry(), add_default_flooding_flow_entry(), add_multicast_mac_drop_flow_entry(), add_ipv6_multicast_mac_drop_flow_entry(), add_default_forwarding_flow_entry()] do - send_flow_mod_add(datapath_id, flow_options) + send_flow_mod_add(datapath_id, Keyword.merge(flow_options, [bundle_id: 1, bundle_flags: [:atomic, :ordered]])) end + + :ok = onf_bundle_commit(datapath_id, bundle_id: 1, flags: [:atomic, :ordered]) end defp add_forwarding_flow_and_packet_out(packet_in, state) do @@ -81,12 +85,8 @@ defmodule LearningSwitch.Ofctl do packet_out(packet_in, port_no || :flood) end - defp packet_out(%PacketIn{datapath_id: datapath_id, data: data}, port_no) do - send_packet_out( - datapath_id, - data: data, - actions: [Output.new(port_no)] - ) + defp packet_out(%PacketIn{} = pin, port_no) do + send_packet_out(packet_in: pin, actions: [Output.new(port_no)]) end defp add_forwarding_flow_entry(_packet_in, nil), do: :noop diff --git a/examples/learning_switch/mix.lock b/examples/learning_switch/mix.lock index cbc80d1..d1071b3 100644 --- a/examples/learning_switch/mix.lock +++ b/examples/learning_switch/mix.lock @@ -1,7 +1,7 @@ %{ "binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, - "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, - "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"}, + "jsone": {:git, "https://github.com/sile/jsone.git", "b23d312a5ed051ea7ad0989a9f2cb1a9c3f9a502", [tag: "1.4.6"]}, + "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}, } diff --git a/examples/nx_learning_switch/.formatter.exs b/examples/nx_learning_switch/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/examples/nx_learning_switch/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/examples/nx_learning_switch/.gitignore b/examples/nx_learning_switch/.gitignore new file mode 100644 index 0000000..3db6a2c --- /dev/null +++ b/examples/nx_learning_switch/.gitignore @@ -0,0 +1,24 @@ +# 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 third-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 + +# Ignore package tarball (built via "mix hex.build"). +nx_learning_switch-*.tar + diff --git a/examples/nx_learning_switch/README.md b/examples/nx_learning_switch/README.md new file mode 100644 index 0000000..927c08c --- /dev/null +++ b/examples/nx_learning_switch/README.md @@ -0,0 +1,3 @@ +# NxLearningSwitch + +Example for a very simple-minded MAC learning switch diff --git a/examples/nx_learning_switch/config/config.exs b/examples/nx_learning_switch/config/config.exs new file mode 100644 index 0000000..b8b6603 --- /dev/null +++ b/examples/nx_learning_switch/config/config.exs @@ -0,0 +1,17 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +config :tres, + protocol: :tcp, + port: 6653, + max_connections: 10, + num_acceptors: 10, + callback_module: NxLearningSwitch, + callback_args: [] + +config :logger, + level: :debug, + format: "$date $time [$level] $metadata$message\n", + metadata: [:application], + handle_otp_reports: true diff --git a/examples/nx_learning_switch/lib/nx_learning_switch.ex b/examples/nx_learning_switch/lib/nx_learning_switch.ex new file mode 100644 index 0000000..08214a1 --- /dev/null +++ b/examples/nx_learning_switch/lib/nx_learning_switch.ex @@ -0,0 +1,93 @@ +defmodule NxLearningSwitch do + @moduledoc """ + Emulates Layer2 switch + """ + + use GenServer + use Tres.Controller + + import Logger + + # API functions + + def start_link(datapath_id, _args) do + GenServer.start_link(__MODULE__, [datapath_id]) + end + + # GenServer callback functions + + @impl GenServer + def init([{datapath_id, _aux_id}]) do + :ok = info("Connected: datapath_id: #{datapath_id}") + {:ok, datapath_id, {:continue, :init}} + end + + @impl GenServer + def handle_continue(:init, datapath_id) do + :ok = l2_learning_flow(datapath_id) + :ok = l2_flooding_flows(datapath_id) + {:noreply, datapath_id} + end + + @impl GenServer + def handle_call(_request, _from, state) do + {:reply, :ok, state} + end + + @impl GenServer + def handle_cast(_request, state) do + {:noreply, state} + end + + @impl GenServer + def handle_info({:switch_disconnected, reason}, datapath_id) do + :ok = warn("Disconnected: datapath_id: #{datapath_id} by #{inspect(reason)}") + {:stop, :normal, datapath_id} + end + + @impl GenServer + def handle_info(_info, state) do + {:noreply, state} + end + + ## private functions + + defp l2_learning_flow(datapath_id) do + send_flow_mod_add( + datapath_id, + table_id: 0, + instructions: ApplyActions.new([ + NxLearn.new( + table_id: 1, + priority: 2, + hard_timeout: 10, + flow_specs: [ + NxFlowSpecMatch.new( + src: :nx_eth_src, + dst: :nx_eth_dst + ), + NxFlowSpecMatch.new( + src: :nx_vlan_tci, + dst: :nx_vlan_tci, + offset: 0, + n_bits: 12 + ), + NxFlowSpecOutput.new( + src: :nx_in_port + ) + ] + ), + NxResubmitTable.new(1) + ]) + ) + end + + defp l2_flooding_flows(datapath_id) do + send_flow_mod_add( + datapath_id, + table_id: 1, + priority: 0, + instructions: ApplyActions.new(Output.new(:flood)) + ) + end +end diff --git a/examples/nx_learning_switch/mix.exs b/examples/nx_learning_switch/mix.exs new file mode 100644 index 0000000..0d6c717 --- /dev/null +++ b/examples/nx_learning_switch/mix.exs @@ -0,0 +1,27 @@ +defmodule NxLearningSwitch.MixProject do + use Mix.Project + + def project do + [ + app: :nx_learning_switch, + version: "0.1.0", + elixir: "~> 1.8", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:tres, path: "../../../tres"} + ] + end +end diff --git a/examples/nx_learning_switch/mix.lock b/examples/nx_learning_switch/mix.lock new file mode 100644 index 0000000..becf0a3 --- /dev/null +++ b/examples/nx_learning_switch/mix.lock @@ -0,0 +1,3 @@ +%{ + "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, +} diff --git a/examples/nx_learning_switch/test/nx_learning_switch_test.exs b/examples/nx_learning_switch/test/nx_learning_switch_test.exs new file mode 100644 index 0000000..a9e8311 --- /dev/null +++ b/examples/nx_learning_switch/test/nx_learning_switch_test.exs @@ -0,0 +1,8 @@ +defmodule NxLearningSwitchTest do + use ExUnit.Case + doctest NxLearningSwitch + + test "greets the world" do + assert NxLearningSwitch.hello() == :world + end +end diff --git a/examples/nx_learning_switch/test/test_helper.exs b/examples/nx_learning_switch/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/examples/nx_learning_switch/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/examples/simple_router/.formatter.exs b/examples/simple_router/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/examples/simple_router/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/examples/simple_router/.gitignore b/examples/simple_router/.gitignore new file mode 100644 index 0000000..3075870 --- /dev/null +++ b/examples/simple_router/.gitignore @@ -0,0 +1,24 @@ +# 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 + +# Ignore package tarball (built via "mix hex.build"). +simple_router-*.tar + diff --git a/examples/simple_router/README.md b/examples/simple_router/README.md new file mode 100644 index 0000000..878110f --- /dev/null +++ b/examples/simple_router/README.md @@ -0,0 +1,47 @@ +# SimpleRouter + +#### An OpenFlow controller that emulates layer 3 switch (router). + +## Configuration + +```elixir +config :simple_router, + interfaces: %{ + "veth1" => %{mac_address: "02:00:00:01:00:01", ip_address: "192.168.1.1/24"}, + "veth3" => %{mac_address: "02:00:00:01:00:02", ip_address: "192.168.2.1/24"}, + }, + routes: %{ + "0.0.0.0/0" => "192.168.1.2" + } +``` + +## Flow Tables + +``` +> $ sudo ovs-ofctl dump-flows br0 -OOpenFlow13 --color=auto + cookie=0x0, duration=10.217s, table=0, n_packets=3, n_bytes=126, priority=1,arp actions=goto_table:1 + cookie=0x0, duration=10.217s, table=0, n_packets=19, n_bytes=1862, priority=1,ip actions=goto_table:2 + cookie=0x0, duration=10.208s, table=1, n_packets=1, n_bytes=42, priority=0,arp,in_port=veth1,arp_tpa=192.168.1.1,arp_op=1 actions=learn(table=4,idle_timeout=300,priority=1,NXM_NX_REG0[]=NXM_OF_ARP_SPA[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[]),push:NXM_OF_ARP_TPA[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],push:NXM_OF_ETH_SRC[],pop:NXM_OF_ETH_DST[],pop:NXM_OF_ARP_TPA[],pop:NXM_NX_ARP_THA[],pop:NXM_OF_ARP_SPA[],set_field:2->arp_op,set_field:02:00:00:01:00:01->arp_sha,set_field:02:00:00:01:00:01->eth_src,IN_PORT + cookie=0x0, duration=10.207s, table=1, n_packets=1, n_bytes=42, priority=0,arp,in_port=veth1,arp_tpa=192.168.1.1,arp_op=2 actions=learn(table=4,idle_timeout=300,priority=1,NXM_NX_REG0[]=NXM_OF_ARP_SPA[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[]) + cookie=0x0, duration=10.206s, table=1, n_packets=1, n_bytes=42, priority=0,arp,in_port=veth3,arp_tpa=192.168.2.1,arp_op=1 actions=learn(table=4,idle_timeout=300,priority=1,NXM_NX_REG0[]=NXM_OF_ARP_SPA[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[]),push:NXM_OF_ARP_TPA[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],push:NXM_OF_ETH_SRC[],pop:NXM_OF_ETH_DST[],pop:NXM_OF_ARP_TPA[],pop:NXM_NX_ARP_THA[],pop:NXM_OF_ARP_SPA[],set_field:2->arp_op,set_field:02:00:00:01:00:02->arp_sha,set_field:02:00:00:01:00:02->eth_src,IN_PORT + cookie=0x0, duration=10.206s, table=1, n_packets=0, n_bytes=0, priority=0,arp,in_port=veth3,arp_tpa=192.168.2.1,arp_op=2 actions=learn(table=4,idle_timeout=300,priority=1,NXM_NX_REG0[]=NXM_OF_ARP_SPA[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[]) + cookie=0x0, duration=10.205s, table=2, n_packets=0, n_bytes=0, priority=124,ip,nw_dst=192.168.1.0/24 actions=move:NXM_OF_IP_DST[]->NXM_NX_REG0[],goto_table:3 + cookie=0x0, duration=10.205s, table=2, n_packets=9, n_bytes=882, priority=124,ip,nw_dst=192.168.2.0/24 actions=move:NXM_OF_IP_DST[]->NXM_NX_REG0[],goto_table:3 + cookie=0x0, duration=10.205s, table=2, n_packets=10, n_bytes=980, priority=0,ip actions=set_field:0xc0a80102->reg0,goto_table:3 + cookie=0x0, duration=10.212s, table=3, n_packets=10, n_bytes=980, priority=24,reg0=0xc0a80100/0xffffff00 actions=set_field:0x1->reg1,resubmit(,4),resubmit(,5) + cookie=0x0, duration=10.212s, table=3, n_packets=9, n_bytes=882, priority=24,reg0=0xc0a80200/0xffffff00 actions=set_field:0x2->reg1,resubmit(,4),resubmit(,5) + cookie=0x0, duration=9.891s, table=4, n_packets=9, n_bytes=882, idle_timeout=300, priority=1,reg0=0xc0a80202 actions=load:0x9a9114dd25d8->NXM_OF_ETH_DST[] + cookie=0x0, duration=9.855s, table=4, n_packets=9, n_bytes=882, idle_timeout=300, priority=1,reg0=0xc0a80102 actions=load:0x7a69d574651c->NXM_OF_ETH_DST[] + cookie=0x0, duration=10.207s, table=4, n_packets=1, n_bytes=98, priority=0,reg1=0x1 actions=controller(userdata=41.52.50.5f.6d.69.73.73.69.6e.67.ff.ff.ff.ff.ff.ff.02.00.00.01.00.01.08.06.00.01.08.00.06.04.00.01.02.00.00.01.00.01.c0.a8.01.01.00.00.00.00.00.00,pause) + cookie=0x0, duration=10.206s, table=4, n_packets=0, n_bytes=0, priority=0,reg1=0x2 actions=controller(userdata=41.52.50.5f.6d.69.73.73.69.6e.67.ff.ff.ff.ff.ff.ff.02.00.00.01.00.02.08.06.00.01.08.00.06.04.00.01.02.00.00.01.00.02.c0.a8.02.01.00.00.00.00.00.00,pause) + cookie=0x0, duration=10.206s, table=5, n_packets=9, n_bytes=882, priority=1,reg1=0x1 actions=group:1 + cookie=0x0, duration=10.205s, table=5, n_packets=9, n_bytes=882, priority=1,reg1=0x2 actions=group:2 + ``` + +## Group Tables + ``` +> $ sudo ovs-ofctl dump-groups br0 -OOpenFlow13 --color=auto +OFPST_GROUP_DESC reply (OF1.3) (xid=0x2): + group_id=1,type=indirect,bucket=actions=set_field:02:00:00:01:00:01->eth_src,output:veth1 + group_id=2,type=indirect,bucket=actions=set_field:02:00:00:01:00:02->eth_src,output:veth3 +``` diff --git a/examples/simple_router/config/config.exs b/examples/simple_router/config/config.exs new file mode 100644 index 0000000..899635d --- /dev/null +++ b/examples/simple_router/config/config.exs @@ -0,0 +1,20 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +config :tres, + protocol: :tcp, + port: 6653, + max_connections: 10, + num_acceptors: 10, + callback_module: SimpleRouter.Openflow.Controller, + callback_args: [] + +config :simple_router, + interfaces: %{ + "veth1" => %{mac_address: "02:00:00:01:00:01", ip_address: "192.168.1.1/24"}, + "veth3" => %{mac_address: "02:00:00:01:00:02", ip_address: "192.168.2.1/24"}, + }, + routes: %{ + "0.0.0.0/0" => "192.168.1.2" + } diff --git a/examples/simple_router/lib/simple_router.ex b/examples/simple_router/lib/simple_router.ex new file mode 100644 index 0000000..7eccb34 --- /dev/null +++ b/examples/simple_router/lib/simple_router.ex @@ -0,0 +1,18 @@ +defmodule SimpleRouter do + @moduledoc """ + Documentation for SimpleRouter. + """ + + @doc """ + Hello world. + + ## Examples + + iex> SimpleRouter.hello() + :world + + """ + def hello do + :world + end +end diff --git a/examples/simple_router/lib/simple_router/application.ex b/examples/simple_router/lib/simple_router/application.ex new file mode 100644 index 0000000..6843961 --- /dev/null +++ b/examples/simple_router/lib/simple_router/application.ex @@ -0,0 +1,9 @@ +defmodule SimpleRouter.Application do + @moduledoc false + + use Application + + def start(_type, _args) do + SimpleRouter.Supervisor.start_link() + end +end diff --git a/examples/simple_router/lib/simple_router/config.ex b/examples/simple_router/lib/simple_router/config.ex new file mode 100644 index 0000000..cba4f9c --- /dev/null +++ b/examples/simple_router/lib/simple_router/config.ex @@ -0,0 +1,55 @@ +defmodule SimpleRouter.Config do + @moduledoc false + + alias Tres.IPv4Address + + @spec interfaces() :: %{String.t() => map()} + def interfaces do + :interfaces + |> get_env(%{}) + |> Enum.reduce(%{}, &interfaces_to_erl/2) + end + + @spec routes() :: [tuple()] + def routes do + :routes + |> get_env(%{}) + |> Enum.reduce([], &routes_to_erl/2) + end + + # private functions + + defp interfaces_to_erl({ifname, %{mac_address: mac, ip_address: ip}}, acc) do + {ipaddr, mask} = IPv4Address.parse(ip) + + entry = + %{ + mac_address: String.replace(mac, ~r/:/, ""), + ip_address: ipaddr, + subnet_mask: mask, + network_address: IPv4Address.to_network({ipaddr, mask}), + prefix_length: get_cidr(ip) + } + + Map.merge(acc, %{ifname => entry}) + end + + defp routes_to_erl({dst, nexthop}, acc) do + {dstaddr, mask} = IPv4Address.parse(dst) + {nexthop, _netmask} = IPv4Address.parse(nexthop) + entry = %{dst: dstaddr, mask: mask, prefix_length: get_cidr(dst), nexthop: nexthop} + [entry | acc] + end + + defp get_cidr(ip) do + case String.split(ip, ~r/\//) do + [^ip] -> + 32 + [_addr, cidr] -> + String.to_integer(cidr) + end + end + + defp get_env(key, default), + do: Application.get_env(:simple_router, key, default) +end diff --git a/examples/simple_router/lib/simple_router/openflow/controller.ex b/examples/simple_router/lib/simple_router/openflow/controller.ex new file mode 100644 index 0000000..7e71cf9 --- /dev/null +++ b/examples/simple_router/lib/simple_router/openflow/controller.ex @@ -0,0 +1,139 @@ +defmodule SimpleRouter.Openflow.Controller do + @moduledoc """ + Emulates Router + """ + + use GenServer + use Tres.Controller + + import Logger + + alias SimpleRouter.Config + alias SimpleRouter.Openflow.FlowTables + alias SimpleRouter.Openflow.GroupTables + + defmodule State do + @moduledoc false + + defstruct [ + datapath_id: nil, + ports: %{} + ] + end + + def start_link({datapath_id, _aux_id}, _start_args) do + GenServer.start_link(__MODULE__, [datapath_id]) + end + + def init([datapath_id]) do + :ok = debug("Switch Connected: datapath_id = #{datapath_id}") + {:ok, %State{datapath_id: datapath_id}, {:continue, :init_datapath}} + end + + def handle_continue(:init_datapath, state) do + :ok = init_datapath(state.datapath_id) + {:noreply, state, {:continue, :init_interface}} + end + + def handle_continue(:init_interface, state) do + port_desc = PortDesc.Request.new() + :ok = send_message(port_desc, state.datapath_id) + {:noreply, state} + end + + def handle_info(%PortDesc.Reply{datapath_id: dpid, ports: ports}, state) do + :ok = debug("port_reply from dpid: #{dpid}") + ports = handle_ports(ports) + routes = Config.routes() + :ok = onf_bundle_open(dpid, bundle_id: 1, flags: [:atomic, :ordered]) + :ok = FlowTables.classifier(dpid) + :ok = init_egress_groups(ports, dpid) + :ok = init_interface_lookup_flows(ports, dpid) + :ok = init_arp_handler_flows(ports, dpid) + :ok = init_egress_flows(ports, dpid) + :ok = init_connected_routes(ports, dpid) + :ok = init_static_routes(routes, dpid) + :ok = onf_bundle_commit(dpid, bundle_id: 1, flags: [:atomic, :ordered]) + {:noreply, %{state | ports: ports}} + end + + def handle_info(%NxPacketIn2{userdata: <<"ARP_missing", arp_req::bytes>>} = pin, state) do + :ok = debug("ARP Entry missing: #{inspect(pin)}") + nexthop_address = pin.metadata[:reg0] + + send_packet_out( + packet_in: pin, + metadata: Keyword.replace!(pin.metadata, :in_port, :controller), + data: <> + ) + + {:noreply, state} + end + + def handle_info(info, state) do + :ok = warn("Unhandled info received: #{inspect(info)}") + {:noreply, state} + end + + # private functions + + @spec handle_ports([%Openflow.Port{}]) :: [map()] + defp handle_ports(ports) do + ifaces = Config.interfaces() + handle_ports([], ports, ifaces) + end + + defp handle_ports(acc, [], _ifaces), do: acc + + defp handle_ports(acc0, [port|rest], ifaces) do + case Map.get(ifaces, port.name) do + iface0 when is_map(iface0) -> + iface = Map.merge(iface0, %{number: port.number}) + handle_ports([iface|acc0], rest, ifaces) + _ -> + handle_ports(acc0, rest, ifaces) + end + end + + @spec init_egress_groups([map()], String.t()) :: :ok + defp init_egress_groups(ifaces, datapath_id), + do: Enum.each(ifaces, &GroupTables.egress(&1, datapath_id)) + + @spec init_interface_lookup_flows([map()], String.t()) :: :ok + defp init_interface_lookup_flows(ifaces, datapath_id), + do: Enum.each(ifaces, &FlowTables.lookup_iface(&1, datapath_id)) + + @spec init_arp_handler_flows([map()], String.t()) :: :ok + defp init_arp_handler_flows(ifaces, datapath_id), + do: Enum.each(ifaces, &FlowTables.arp_handlers(&1, datapath_id)) + + @spec init_egress_flows([map()], String.t()) :: :ok + defp init_egress_flows(ifaces, datapath_id), + do: Enum.each(ifaces, &FlowTables.egress(&1, datapath_id)) + + @spec init_connected_routes([map()], String.t()) :: :ok + defp init_connected_routes(ifaces, datapath_id), + do: Enum.each(ifaces, &FlowTables.connected_route(&1, datapath_id)) + + @spec init_static_routes([map()], String.t()) :: :ok + defp init_static_routes(routes, datapath_id), + do: Enum.each(routes, &FlowTables.static_route(&1, datapath_id)) + + @spec init_datapath(String.t()) :: :ok + defp init_datapath(datapath_id) do + :ok = set_config(datapath_id) + :ok = set_packet_in_format(datapath_id) + end + + @spec set_config(String.t()) :: :ok + defp set_config(datapath_id) do + set_config = SetConfig.new(miss_send_len: :no_buffer) + :ok = send_message(set_config, datapath_id) + end + + @spec set_packet_in_format(String.t()) :: :ok + defp set_packet_in_format(datapath_id) do + pin_format = NxSetPacketInFormat.new(:nxt_packet_in2) + :ok = send_message(pin_format, datapath_id) + end +end diff --git a/examples/simple_router/lib/simple_router/openflow/flow_tables.ex b/examples/simple_router/lib/simple_router/openflow/flow_tables.ex new file mode 100644 index 0000000..db4c88b --- /dev/null +++ b/examples/simple_router/lib/simple_router/openflow/flow_tables.ex @@ -0,0 +1,240 @@ +defmodule SimpleRouter.Openflow.FlowTables do + @moduledoc """ + Flow Table Definitions + """ + + use Tres.Controller + + alias Tres.IPv4Address + alias Tres.MacAddress + + @classifier_table_id 0 + @arp_handler_table_id 1 + @routing_table_id 2 + @interface_lookup_table_id 3 + @arp_lookup_table_id 4 + @egress_table_id 5 + + def classifier(datapath_id) do + # ARP + send_flow_mod_add( + datapath_id, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + table_id: @classifier_table_id, + priority: 1, + match: Match.new(eth_type: 0x0806), + instructions: [GotoTable.new(@arp_handler_table_id)] + ) + + # IPv4 + send_flow_mod_add( + datapath_id, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + table_id: @classifier_table_id, + priority: 1, + match: Match.new(eth_type: 0x0800), + instructions: [GotoTable.new(@routing_table_id)] + ) + end + + def connected_route(iface, datapath_id) do + send_flow_mod_add( + datapath_id, + table_id: @routing_table_id, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + priority: 100 + iface.prefix_length, + match: Match.new( + eth_type: 0x0800, + ipv4_dst: {iface.network_address, iface.subnet_mask} + ), + instructions: [ + ApplyActions.new(NxRegMove.new(src_field: :ipv4_dst, dst_field: :reg0)), + GotoTable.new(@interface_lookup_table_id) + ] + ) + end + + def static_route(route, datapath_id) do + send_flow_mod_add( + datapath_id, + table_id: @routing_table_id, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + priority: route.prefix_length, + match: Match.new( + eth_type: 0x0800, + ipv4_dst: {route.dst, route.mask} + ), + instructions: [ + ApplyActions.new(SetField.new(reg0: IPv4Address.to_int(route.nexthop))), + GotoTable.new(@interface_lookup_table_id) + ] + ) + end + + def arp_handlers(iface, datapath_id) do + :ok = arp_request_handler(iface, datapath_id) + :ok = arp_reply_handler(iface, datapath_id) + :ok = arp_entry_is_missing(iface, datapath_id) + end + + def arp_entry_is_missing(iface, datapath_id) do + send_flow_mod_add( + datapath_id, + priority: 0, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + table_id: @arp_lookup_table_id, + match: Match.new(reg1: iface.number), + instructions: [ + ApplyActions.new( + NxController2.new(userdata: arp_packet(iface), pause: true) + ) + ] + ) + end + + def lookup_iface(iface, datapath_id) do + match = + Match.new( + reg0: { + IPv4Address.to_int(iface.network_address), + IPv4Address.to_int(iface.subnet_mask) + } + ) + + send_flow_mod_add( + datapath_id, + table_id: @interface_lookup_table_id, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + priority: iface.prefix_length, + match: match, + instructions: [ + ApplyActions.new([ + SetField.new(reg1: iface.number), + NxResubmitTable.new(@arp_lookup_table_id), + NxResubmitTable.new(@egress_table_id) + ]) + ] + ) + end + + def egress(iface, datapath_id) do + send_flow_mod_add( + datapath_id, + table_id: @egress_table_id, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + priority: 1, + match: Match.new(reg1: iface.number), + instructions: ApplyActions.new(Group.new(iface.number)) + ) + end + + # private functions + + defp arp_request_handler(iface, datapath_id) do + send_flow_mod_add( + datapath_id, + table_id: @arp_handler_table_id, + priority: 0, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + match: Match.new( + in_port: iface.number, + eth_type: 0x0806, + # Request + arp_op: 0x1, + arp_tpa: iface.ip_address + ), + instructions: ApplyActions.new(arp_respond_and_learn_actions(iface.mac_address)) + ) + end + + defp arp_reply_handler(iface, datapath_id) do + send_flow_mod_add( + datapath_id, + table_id: @arp_handler_table_id, + priority: 0, + bundle_id: 1, + bundle_flags: [:atomic, :ordered], + match: Match.new( + in_port: iface.number, + eth_type: 0x0806, + # Reply + arp_op: 0x2, + arp_tpa: iface.ip_address + ), + instructions: ApplyActions.new(learn_arp_reply_actions()) + ) + end + + defp arp_respond_and_learn_actions(mac) do + [ + NxLearn.new( + priority: 1, + table_id: @arp_lookup_table_id, + idle_timeout: 300, + flow_specs: [ + NxFlowSpecMatch.new(src: :arp_spa, dst: :reg0), + NxFlowSpecLoad.new(src: :eth_src, dst: :eth_dst) + ] + ), + NxStackPush.new(field: :arp_tpa), + NxStackPush.new(field: :arp_sha), + NxStackPush.new(field: :arp_spa), + NxStackPush.new(field: :eth_src), + NxStackPop.new(field: :eth_dst), + NxStackPop.new(field: :arp_tpa), + NxStackPop.new(field: :arp_tha), + NxStackPop.new(field: :arp_spa), + SetField.new(arp_op: 0x2), + SetField.new(arp_sha: mac), + SetField.new(eth_src: mac), + Output.new(:in_port) + ] + end + + defp learn_arp_reply_actions do + [ + NxLearn.new( + priority: 1, + table_id: @arp_lookup_table_id, + idle_timeout: 300, + flow_specs: [ + NxFlowSpecMatch.new(src: :arp_spa, dst: :reg0), + NxFlowSpecLoad.new(src: :eth_src, dst: :eth_dst) + ] + ) + ] + end + + @arphrd_ether 1 + @eth_p_ip 0x0800 + + defp arp_packet(iface) do + ether_header = << + 0xffffffffffff::48, # destination ethernet address + (MacAddress.to_i(iface.mac_address))::48, # source ethernet address + 0x0806::16 # ethernet type + >> + + # Target Protocol Address will append in NX_PACKET_IN2 + arp_header = << + @arphrd_ether::16, # hardware type + @eth_p_ip::16, # protocol type + 6::8, # hardware address length + 4::8, # protocol address length + 1::16, # ARPOP_REQUEST + (MacAddress.to_i(iface.mac_address))::48, # Source Hardware Address + (IPv4Address.to_int(iface.ip_address))::32, # Source Protocol Address + 0x000000000000::48 # Target Hardware Address + >> + + <<"ARP_missing", ether_header::bytes, arp_header::bytes>> + end +end diff --git a/examples/simple_router/lib/simple_router/openflow/group_tables.ex b/examples/simple_router/lib/simple_router/openflow/group_tables.ex new file mode 100644 index 0000000..cc8dd52 --- /dev/null +++ b/examples/simple_router/lib/simple_router/openflow/group_tables.ex @@ -0,0 +1,24 @@ +defmodule SimpleRouter.Openflow.GroupTables do + @moduledoc """ + Group Table Definitions + """ + + use Tres.Controller + + def egress(iface, datapath_id) do + bucket = + Openflow.Bucket.new( + actions: [ + SetField.new(eth_src: iface.mac_address), + Output.new(iface.number) + ] + ) + + send_group_mod_add( + datapath_id, + type: :indirect, + group_id: iface.number, + buckets: [bucket] + ) + end +end diff --git a/examples/simple_router/lib/simple_router/supervisor.ex b/examples/simple_router/lib/simple_router/supervisor.ex new file mode 100644 index 0000000..5f3b67a --- /dev/null +++ b/examples/simple_router/lib/simple_router/supervisor.ex @@ -0,0 +1,17 @@ +defmodule SimpleRouter.Supervisor do + @moduledoc """ + Top Level supervisor + """ + + use Supervisor + + @children [] + + def start_link do + Supervisor.start_link(__MODULE__, [], name: __MODULE__) + end + + def init(_args) do + Supervisor.init(@children, strategy: :one_for_one) + end +end diff --git a/examples/simple_router/mix.exs b/examples/simple_router/mix.exs new file mode 100644 index 0000000..4bf5407 --- /dev/null +++ b/examples/simple_router/mix.exs @@ -0,0 +1,27 @@ +defmodule SimpleRouter.MixProject do + use Mix.Project + + def project do + [ + app: :simple_router, + version: "0.1.0", + elixir: "~> 1.7", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + def application do + [ + extra_applications: [:logger, :tres, :pkt], + mod: {SimpleRouter.Application, []} + ] + end + + defp deps do + [ + {:tres, path: "../../../tres"}, + {:pkt, github: "msantos/pkt"} + ] + end +end diff --git a/examples/simple_router/mix.lock b/examples/simple_router/mix.lock new file mode 100644 index 0000000..64390d9 --- /dev/null +++ b/examples/simple_router/mix.lock @@ -0,0 +1,7 @@ +%{ + "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, + "jsone": {:git, "https://github.com/sile/jsone.git", "b23d312a5ed051ea7ad0989a9f2cb1a9c3f9a502", [tag: "1.4.6"]}, + "pkt": {:git, "https://github.com/msantos/pkt.git", "ff0e9a7d28cdae941bce935602cd252cad1ea296", []}, + "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, + "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}, +} diff --git a/examples/simple_router/test/simple_router_test.exs b/examples/simple_router/test/simple_router_test.exs new file mode 100644 index 0000000..a1513a2 --- /dev/null +++ b/examples/simple_router/test/simple_router_test.exs @@ -0,0 +1,8 @@ +defmodule SimpleRouterTest do + use ExUnit.Case + doctest SimpleRouter + + test "greets the world" do + assert SimpleRouter.hello() == :world + end +end diff --git a/examples/simple_router/test/test_helper.exs b/examples/simple_router/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/examples/simple_router/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/lib/openflow.ex b/lib/openflow.ex index ac30ab8..98d6a26 100644 --- a/lib/openflow.ex +++ b/lib/openflow.ex @@ -15,24 +15,27 @@ defmodule Openflow do end def read(<>) do - body_len = len - @ofp_header_size - <> = binary2 + try do + body_len = len - @ofp_header_size + <> = binary2 - result = - type - |> Openflow.Enums.to_atom(:openflow_codec) - |> do_read(body_bin) + 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} + 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 - binaries = for message <- messages, do: to_binary(message) - Enum.join(binaries, "") - end + def to_binary(messages) when is_list(messages), + do: Enum.reduce(messages, "", &(&2 <> to_binary(&1))) def to_binary(%{__struct__: encoder, version: version, xid: xid} = msg) do case encoder.to_binary(msg) do diff --git a/lib/openflow/action.ex b/lib/openflow/action.ex index 6ffbe57..5690467 100644 --- a/lib/openflow/action.ex +++ b/lib/openflow/action.ex @@ -1,4 +1,63 @@ defmodule Openflow.Action do + @moduledoc """ + Openflow parser handler + """ + + @type type :: + Openflow.Action.Output.t() + | Openflow.Action.CopyTtlOut.t() + | Openflow.Action.CopyTtlIn.t() + | Openflow.Action.SetMplsTtl.t() + | Openflow.Action.DecMplsTtl.t() + | Openflow.Action.PushVlan.t() + | Openflow.Action.PopVlan.t() + | Openflow.Action.PushMpls.t() + | Openflow.Action.PopMpls.t() + | Openflow.Action.SetQueue.t() + | Openflow.Action.Group.t() + | Openflow.Action.SetNwTtl.t() + | Openflow.Action.DecNwTtl.t() + | Openflow.Action.SetField.t() + | Openflow.Action.PushPbb.t() + | Openflow.Action.PopPbb.t() + | Openflow.Action.NxResubmit.t() + | Openflow.Action.NxSetTunnel.t() + | Openflow.Action.NxRegMove.t() + | Openflow.Action.NxRegLoad.t() + | Openflow.Action.NxNote.t() + | Openflow.Action.NxSetTunnel64.t() + | Openflow.Action.NxMultipath.t() + | Openflow.Action.NxBundle.t() + | Openflow.Action.NxBundleLoad.t() + | Openflow.Action.NxResubmitTable.t() + | Openflow.Action.NxOutputReg.t() + | Openflow.Action.NxLearn.t() + | Openflow.Action.NxExit.t() + | Openflow.Action.NxDecTtl.t() + | Openflow.Action.NxFinTimeout.t() + | Openflow.Action.NxController.t() + | Openflow.Action.NxDecTtlCntIds.t() + | Openflow.Action.NxWriteMetadata.t() + | Openflow.Action.NxPushMpls.t() + | Openflow.Action.NxPopMpls.t() + | Openflow.Action.NxStackPush.t() + | Openflow.Action.NxStackPop.t() + | Openflow.Action.NxSample.t() + | Openflow.Action.NxOutputReg2.t() + | Openflow.Action.NxRegLoad2.t() + | Openflow.Action.NxConjunction.t() + | Openflow.Action.NxConntrack.t() + | Openflow.Action.NxNat.t() + | Openflow.Action.NxController2.t() + | Openflow.Action.NxSample2.t() + | Openflow.Action.NxOutputTrunc.t() + | Openflow.Action.NxGroup.t() + | Openflow.Action.NxSample3.t() + | Openflow.Action.NxClone.t() + | Openflow.Action.NxCtClear.t() + | Openflow.Action.NxResubmitTableCt.t() + | Openflow.Action.NxLearn2.t() + def read(action_bin) do do_read([], action_bin) end diff --git a/lib/openflow/actions/copy_ttl_in.ex b/lib/openflow/actions/copy_ttl_in.ex index 83347b4..6b4fd2c 100644 --- a/lib/openflow/actions/copy_ttl_in.ex +++ b/lib/openflow/actions/copy_ttl_in.ex @@ -1,18 +1,37 @@ defmodule Openflow.Action.CopyTtlIn do + @moduledoc """ + Copy the TTL from next-to-outermost to outermost header with TTL.\\ + Copy can be IP-to-IP, MPLS-to-MPLS, or IP-to-MPLS. + """ + defstruct([]) alias __MODULE__ + @type t :: %CopyTtlIn{} + + @spec ofpat() :: 12 def ofpat, do: 12 + @doc """ + Create a copy_ttl_in action struct + + ## Example: + ```elixir + iex> %CopyTtlIn{} = Openflow.Action.CopyTtlIn.new() + ``` + """ + @spec new() :: CopyTtlIn.t() def new do %CopyTtlIn{} end + @spec to_binary(CopyTtlIn.t()) :: <<_::16, _::_*8>> def to_binary(%CopyTtlIn{}) do <<12::16, 8::16, 0::size(4)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: CopyTtlIn.t() def read(<<12::16, 8::16, _::size(4)-unit(8)>>) do %CopyTtlIn{} end diff --git a/lib/openflow/actions/copy_ttl_out.ex b/lib/openflow/actions/copy_ttl_out.ex index 759c0ad..7ce6dcf 100644 --- a/lib/openflow/actions/copy_ttl_out.ex +++ b/lib/openflow/actions/copy_ttl_out.ex @@ -1,18 +1,38 @@ defmodule Openflow.Action.CopyTtlOut do + @moduledoc """ + Copy the TTL from outermost to next-to-outermost header with TTL.\\ + Copy can be IP-to-IP, MPLS-to-MPLS, or MPLS-to-IP. + """ + defstruct([]) alias __MODULE__ + @type t :: %CopyTtlOut{} + + @spec ofpat() :: 11 def ofpat, do: 11 + @doc """ + Create a copy_ttl_out action structure + + ## Example + + ```elixir + iex> %CopyTtlOut{} = Openflow.Action.CopyTtlOut.new() + ``` + """ + @spec new() :: CopyTtlOut.t() def new do %CopyTtlOut{} end + @spec to_binary(CopyTtlIn.t()) :: <<_::16, _::_*8>> def to_binary(%CopyTtlOut{}) do <<11::16, 8::16, 0::size(4)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: CopyTtlOut.t() def read(<<11::16, 8::16, _::size(4)-unit(8)>>) do %CopyTtlOut{} end diff --git a/lib/openflow/actions/dec_mpls_ttl.ex b/lib/openflow/actions/dec_mpls_ttl.ex index 2931954..d3309cd 100644 --- a/lib/openflow/actions/dec_mpls_ttl.ex +++ b/lib/openflow/actions/dec_mpls_ttl.ex @@ -1,18 +1,39 @@ defmodule Openflow.Action.DecMplsTtl do + @moduledoc """ + Decrement MPLS TTL action + """ + defstruct([]) alias __MODULE__ + @type t :: %DecMplsTtl{} + + @spec ofpat() :: 16 def ofpat, do: 16 + @doc """ + Create a new dec_mpls_ttl action struct. + + Note: Need to be specified the one of `ETH_P_MPLS_*` on `eth_type` match field when using this action. + + ## Example: + + ````elixir + iex> %DecMplsTtl{} = DecMplsTtl.new() + ```` + """ + @spec new() :: t() def new do %DecMplsTtl{} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%DecMplsTtl{}) do <<16::16, 8::16, 0::size(4)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<16::16, 8::16, _::size(4)-unit(8)>>) do %DecMplsTtl{} end diff --git a/lib/openflow/actions/dec_nw_ttl.ex b/lib/openflow/actions/dec_nw_ttl.ex index 6d23b5d..2b86304 100644 --- a/lib/openflow/actions/dec_nw_ttl.ex +++ b/lib/openflow/actions/dec_nw_ttl.ex @@ -1,18 +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/experimenter.ex b/lib/openflow/actions/experimenter.ex index b2b7e99..16b3302 100644 --- a/lib/openflow/actions/experimenter.ex +++ b/lib/openflow/actions/experimenter.ex @@ -3,6 +3,8 @@ defmodule Openflow.Action.Experimenter do alias __MODULE__ + @type t :: %Experimenter{exp_id: 0..0xFFFFFFFF, data: binary()} + @experimter_size 8 def ofpat, do: 0xFFFF @@ -16,6 +18,23 @@ defmodule Openflow.Action.Experimenter do <<0xFFFF::16, length::16, exp_id::32, data::bytes>> end + @spec pack_exp_header(binary()) :: binary() + def pack_exp_header(exp_body) do + pad_length = + exp_body + |> Kernel.byte_size() + |> Kernel.+(4) + |> Openflow.Utils.padding(8) + + length = + exp_body + |> byte_size() + |> Kernel.+(4) + |> Kernel.+(pad_length) + + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(pad_length)-unit(8)>> + 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) -> 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_bundle.ex b/lib/openflow/actions/nx_bundle.ex index da16230..0b93371 100644 --- a/lib/openflow/actions/nx_bundle.ex +++ b/lib/openflow/actions/nx_bundle.ex @@ -12,8 +12,35 @@ defmodule Openflow.Action.NxBundle do @nxast 12 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do + @type algorithm :: :active_backup | :highest_random_weight + @type hash_field :: + :eth_src + | :symmetric_l4 + | :symmetric_l3l4 + | :symmetric_l3l4_udp + | :nw_src + | :nw_dst + + @type t :: %NxBundle{ + algorithm: algorithm(), + hash_field: hash_field(), + basis: non_neg_integer(), + slave_type: :nx_in_port, + n_slaves: non_neg_integer(), + slaves: [pos_integer()] + } + + @spec new( + algorithm: algorithm(), + hash_field: hash_field(), + basis: non_neg_integer(), + slave_type: :nx_in_port, + n_slaves: non_neg_integer(), + slaves: [pos_integer()] + ) :: t() + def new(options \\ []) do slaves = options[:slaves] || [] %NxBundle{ @@ -25,28 +52,25 @@ defmodule Openflow.Action.NxBundle do } 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) + def to_binary(%NxBundle{} = bundle) do + bundle_hash_field_int = Openflow.Enums.to_int(bundle.hash_field, :nx_hash_fields) + bundle_alg_int = Openflow.Enums.to_int(bundle.algorithm, :nx_bd_algorithm) + bundle_slave_type_bin = Openflow.Match.codec_header(bundle.slave_type) + bundle_slaves_bin = codec_slaves(bundle.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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + bundle_alg_int::16, + bundle_hash_field_int::16, + bundle.basis::16, + bundle_slave_type_bin::4-bytes, + bundle.n_slaves::16, + 0::size(2)-unit(8), + 0::size(4)-unit(8), + 0::size(4)-unit(8), + bundle_slaves_bin::bytes + >>) end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do diff --git a/lib/openflow/actions/nx_bundle_load.ex b/lib/openflow/actions/nx_bundle_load.ex index f213fe0..743689c 100644 --- a/lib/openflow/actions/nx_bundle_load.ex +++ b/lib/openflow/actions/nx_bundle_load.ex @@ -17,9 +17,42 @@ defmodule Openflow.Action.NxBundleLoad do @nxast 13 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do - dst_field = options[:dst_field] + @type algorithm :: :active_backup | :highest_random_weight + @type hash_field :: + :eth_src + | :symmetric_l4 + | :symmetric_l3l4 + | :symmetric_l3l4_udp + | :nw_src + | :nw_dst + + @type t :: %NxBundleLoad{ + algorithm: algorithm(), + hash_field: hash_field(), + basis: non_neg_integer(), + slave_type: :nx_in_port, + n_slaves: non_neg_integer(), + slaves: [pos_integer()], + offset: non_neg_integer(), + n_bits: pos_integer(), + dst_field: atom() + } + + @spec new( + algorithm: algorithm(), + hash_field: hash_field(), + basis: non_neg_integer(), + slave_type: :nx_in_port, + n_slaves: non_neg_integer(), + slaves: [pos_integer()], + offset: non_neg_integer(), + n_bits: pos_integer(), + dst_field: atom() + ) :: t() + def new(options \\ []) do + dst_field = options[:dst_field] || raise "dst_field must be specified" default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) slaves = options[:slaves] || [] @@ -31,37 +64,31 @@ defmodule Openflow.Action.NxBundleLoad do slaves: slaves, offset: options[:offset] || 0, n_bits: options[:n_bits] || default_n_bits, - dst_field: options[:dst_field] + 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) + def to_binary(%NxBundleLoad{} = bundle_load) do + hash_field_int = Openflow.Enums.to_int(bundle_load.hash_field, :nx_hash_fields) + alg_int = Openflow.Enums.to_int(bundle_load.algorithm, :nx_bd_algorithm) + slave_type_bin = Openflow.Match.codec_header(bundle_load.slave_type) + slaves_bin = codec_slaves(bundle_load.slaves) + ofs_nbits = bundle_load.offset <<< 6 ||| bundle_load.n_bits - 1 + dst_field_bin = Openflow.Match.codec_header(bundle_load.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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + alg_int::16, + hash_field_int::16, + bundle_load.basis::16, + slave_type_bin::4-bytes, + bundle_load.n_slaves::16, + ofs_nbits::16, + dst_field_bin::4-bytes, + 0::32, + slaves_bin::bytes + >>) end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do diff --git a/lib/openflow/actions/nx_clone.ex b/lib/openflow/actions/nx_clone.ex index 971721b..939a558 100644 --- a/lib/openflow/actions/nx_clone.ex +++ b/lib/openflow/actions/nx_clone.ex @@ -5,6 +5,7 @@ defmodule Openflow.Action.NxClone do @nxast 42 alias __MODULE__ + alias Openflow.Action.Experimenter def new(actions \\ []) do %NxClone{actions: actions} @@ -12,11 +13,10 @@ defmodule Openflow.Action.NxClone do 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)>> + + Experimenter.pack_exp_header( + <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>> + ) end def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>>) do diff --git a/lib/openflow/actions/nx_conjunction.ex b/lib/openflow/actions/nx_conjunction.ex index be33f2a..4e073b6 100644 --- a/lib/openflow/actions/nx_conjunction.ex +++ b/lib/openflow/actions/nx_conjunction.ex @@ -9,18 +9,32 @@ defmodule Openflow.Action.NxConjunction do @nxast 34 alias __MODULE__ + alias Openflow.Action.Experimenter + @spec new(Keyword.t()) :: %NxConjunction{} def new(options) do + clause = (options[:clause] || 0) + 1 + n_clauses = options[:n_clauses] || raise "n_clauses must be specified" + n_clauses >= 2 || raise "n_clauses must be greater than 1" + + (1 <= clause and clause <= n_clauses and 2 <= n_clauses and n_clauses <= 64) || + raise "conjunction(id, k/n) must satisfy 1 <= k <= n and 2 <= n <= 64" + %NxConjunction{ - clause: options[:clause] || 0, - n_clauses: options[:n_clauses] || 0, + clause: clause, + n_clauses: n_clauses, id: options[:id] || 0 } 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>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + clause::8, + n_clauses::8, + id::32 + >>) end def read(<<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>>) do diff --git a/lib/openflow/actions/nx_conntrack.ex b/lib/openflow/actions/nx_conntrack.ex index 7844221..bedee53 100644 --- a/lib/openflow/actions/nx_conntrack.ex +++ b/lib/openflow/actions/nx_conntrack.ex @@ -16,6 +16,7 @@ defmodule Openflow.Action.NxConntrack do @nxast 35 alias __MODULE__ + alias Openflow.Action.Experimenter def new(options \\ []) do %NxConntrack{ @@ -30,36 +31,21 @@ defmodule Openflow.Action.NxConntrack do } 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) + def to_binary(%NxConntrack{} = ct) do + flags_int = Openflow.Enums.flags_to_int(ct.flags, :nx_conntrack_flags) + ct_context_bin = ct_context_binary(ct) + exec_bin = Openflow.Action.to_binary(ct.exec) - {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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + flags_int::16, + ct_context_bin::bytes, + ct.recirc_table::8, + 0::size(3)-unit(8), + ct.alg::16, + exec_bin::bytes + >>) end def read( @@ -87,4 +73,15 @@ defmodule Openflow.Action.NxConntrack do %{ct | zone_src: zone_src, zone_offset: ofs, zone_n_bits: n_bits + 1} end end + + # private functions + + defp ct_context_binary(%NxConntrack{zone_src: nil} = ct), + do: <<0::32, ct.zone_imm::16>> + + defp ct_context_binary(%NxConntrack{} = ct) do + zone_src_bin = Openflow.Match.codec_header(ct.zone_src) + ofs_nbits = ct.zone_offset <<< 6 ||| ct.zone_n_bits - 1 + <> + end end diff --git a/lib/openflow/actions/nx_controller.ex b/lib/openflow/actions/nx_controller.ex index 2f2d1fc..a125abe 100644 --- a/lib/openflow/actions/nx_controller.ex +++ b/lib/openflow/actions/nx_controller.ex @@ -9,8 +9,23 @@ defmodule Openflow.Action.NxController do @nxast 20 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do + @type max_len :: :max | :no_buffer | non_neg_integer() + @type packet_in_reason :: + :no_match | :action | :invalid_ttl | :action_set | :group | :packet_out + @type t :: %NxController{ + max_len: max_len(), + id: non_neg_integer(), + reason: packet_in_reason() + } + + @spec new( + max_len: max_len(), + id: non_neg_integer(), + reason: packet_in_reason() + ) :: t() + def new(options \\ []) do %NxController{ max_len: options[:max_len] || :no_buffer, id: options[:id] || 0, @@ -18,14 +33,18 @@ defmodule Openflow.Action.NxController do } 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) + def to_binary(%NxController{} = controller) do + max_len_int = Openflow.Utils.get_enum(controller.max_len, :controller_max_len) + reason_int = Openflow.Enums.to_int(controller.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>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + max_len_int::16, + controller.id::16, + reason_int::8, + 0::8 + >>) end def read( diff --git a/lib/openflow/actions/nx_controller2.ex b/lib/openflow/actions/nx_controller2.ex index 78fb9d5..c9d5620 100644 --- a/lib/openflow/actions/nx_controller2.ex +++ b/lib/openflow/actions/nx_controller2.ex @@ -4,7 +4,8 @@ defmodule Openflow.Action.NxController2 do id: 0, reason: :action, userdata: "", - pause: false + pause: false, + meter_id: 0 ) @experimenter 0x00002320 @@ -17,28 +18,57 @@ defmodule Openflow.Action.NxController2 do @prop_reason 2 @prop_userdata 3 @prop_pause 4 + @prop_meter_id 5 + + @nx_ctlr_no_meter 0 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do + @type max_len :: pos_integer() | :max | :no_buffer + @type packet_in_reason :: + :no_match | :action | :invalid_ttl | :action_set | :group | :packet_out + @type meter_id :: pos_integer() | :max | :slowpath | :controller | :all + + @type t :: %NxController2{ + max_len: max_len(), + id: non_neg_integer(), + reason: packet_in_reason(), + userdata: binary(), + pause: boolean(), + meter_id: meter_id() + } + + @spec new( + max_len: max_len(), + id: non_neg_integer(), + reason: packet_in_reason(), + userdata: binary(), + pause: boolean(), + meter_id: meter_id() + ) :: t() + def new(options \\ []) + + def new(options) when is_list(options) do %NxController2{ max_len: options[:max_len] || :no_buffer, id: options[:id] || 0, reason: options[:reason] || :action, userdata: options[:userdata], - pause: options[:pause] || false + pause: options[:pause] || false, + meter_id: options[:meter_id] || @nx_ctlr_no_meter } 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>> + props_bin = encode_props(ctl) + + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + 0::size(6)-unit(8), + props_bin::bytes + >>) end def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), body::bytes>>) do @@ -48,93 +78,144 @@ defmodule Openflow.Action.NxController2 do # private functions - defp get_prop_key(ctl) do + defp encode_props(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) + |> Enum.reduce(<<>>, &encode_prop/2) end - defp encode_prop(acc, [], _ctl), do: acc + defp encode_prop({:max_len, value}, acc) + when value != :no_buffer or value < 0xFFFF do + pad_length = 2 + prop_length = @prop_header_size + 2 + pad_length + max_len_int = Openflow.Utils.get_enum(value, :controller_max_len) - defp encode_prop(acc, [prop | rest], ctl) do - value = Map.get(ctl, prop) + << + acc::bytes, + @prop_max_len::16, + prop_length::16, + max_len_int::16, + 0::size(pad_length)-unit(8) + >> + end - 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)>> + defp encode_prop({:id, value}, acc) do + pad_length = 2 + prop_length = @prop_header_size + 2 + pad_length - 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)>> + << + acc::bytes, + @prop_ctl_id::16, + prop_length::16, + value::16, + 0::size(pad_length)-unit(8) + >> + end - 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)>> + defp encode_prop({:reason, reason}, acc) + when reason != :action do + padding_length = 3 + prop_length = @prop_header_size + 1 + padding_length + reason_int = Openflow.Utils.get_enum(reason, :packet_in_reason) - 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)>> + << + acc::bytes, + @prop_reason::16, + prop_length::16, + reason_int::8, + 0::size(padding_length)-unit(8) + >> + end - 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)>> + defp encode_prop({:userdata, value}, acc) + when byte_size(value) > 0 do + prop_length = @prop_header_size + byte_size(value) + padding_length = Openflow.Utils.padding(prop_length, 8) - true -> - "" - end + << + acc::bytes, + @prop_userdata::16, + prop_length::16, + value::bytes, + 0::size(padding_length)-unit(8) + >> + end - encode_prop(<>, rest, ctl) + defp encode_prop({:pause, true}, acc) do + padding_length = 4 + prop_length = @prop_header_size + padding_length + + << + acc::bytes, + @prop_pause::16, + prop_length::16, + 0::size(padding_length)-unit(8) + >> + end + + defp encode_prop({:meter_id, value}, acc) + when value != @nx_ctlr_no_meter do + prop_length = @prop_header_size + 4 + + << + acc::bytes, + @prop_meter_id::16, + prop_length::16, + value::32 + >> + end + + defp encode_prop(_, acc) do + acc 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) + defp decode_prop( + ctl, + << + @prop_max_len::16, + _prop_length::16, + max_len_int::16, + _::size(2)-unit(8), + rest::bytes + >> + ) do + max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len) + decode_prop(%{ctl | max_len: max_len}, rest) + end - case prop_type do - :max_len -> - <<@prop_max_len::16, _prop_length::16, max_len_int::16, _::size(2)-unit(8), rest::bytes>> = - bin + defp decode_prop( + ctl, + <<@prop_reason::16, _prop_length::16, reason_int::8, _::size(3)-unit(8), rest::bytes>> + ) do + reason = Openflow.Utils.get_enum(reason_int, :packet_in_reason) + decode_prop(%{ctl | reason: reason}, rest) + end - max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len) - decode_prop(struct(ctl, %{max_len: max_len}), rest) + defp decode_prop( + ctl, + <<@prop_ctl_id::16, _prop_length::16, controller_id::16, _::size(2)-unit(8), + rest::bytes>> + ) do + decode_prop(%{ctl | id: controller_id}, rest) + end - :controller_id -> - <<@prop_ctl_id::16, _prop_length::16, controller_id::16, _::size(2)-unit(8), rest::bytes>> = - bin + defp decode_prop( + ctl, + <<@prop_userdata::16, prop_length::16, remains::bytes>> + ) do + userdata_len = prop_length - 4 + padding_length = Openflow.Utils.padding(prop_length, 8) + <> = remains + decode_prop(%{ctl | userdata: userdata}, rest) + end - decode_prop(struct(ctl, %{controller_id: controller_id}), rest) + defp decode_prop(ctl, <<@prop_pause::16, _::16, 0::size(4)-unit(8), rest::bytes>>) do + decode_prop(%{ctl | pause: true}, rest) + end - :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 + defp decode_prop(ctl, <<@prop_meter_id::16, _::16, meter_id::32, rest::bytes>>) do + decode_prop(%{ctl | meter_id: meter_id}, rest) end end diff --git a/lib/openflow/actions/nx_ct_clear.ex b/lib/openflow/actions/nx_ct_clear.ex index f58d3b3..84afeaa 100644 --- a/lib/openflow/actions/nx_ct_clear.ex +++ b/lib/openflow/actions/nx_ct_clear.ex @@ -5,14 +5,19 @@ defmodule Openflow.Action.NxCtClear do @nxast 43 alias __MODULE__ + alias Openflow.Action.Experimenter 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>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + 0::16, + 0::size(4)-unit(8) + >>) end def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do diff --git a/lib/openflow/actions/nx_dec_mpls_ttl.ex b/lib/openflow/actions/nx_dec_mpls_ttl.ex deleted file mode 100644 index 6b5744d..0000000 --- a/lib/openflow/actions/nx_dec_mpls_ttl.ex +++ /dev/null @@ -1,21 +0,0 @@ -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 index de6b088..f077f4c 100644 --- a/lib/openflow/actions/nx_dec_ttl.ex +++ b/lib/openflow/actions/nx_dec_ttl.ex @@ -5,14 +5,18 @@ defmodule Openflow.Action.NxDecTtl do @nxast 18 alias __MODULE__ + alias Openflow.Action.Experimenter 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>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + 0::size(6)-unit(8) + >>) end def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do diff --git a/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex b/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex index 729b705..d3303fa 100644 --- a/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex +++ b/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex @@ -5,24 +5,26 @@ defmodule Openflow.Action.NxDecTtlCntIds do @nxast 21 alias __MODULE__ + alias Openflow.Action.Experimenter + @type t :: %NxDecTtlCntIds{ids: [non_neg_integer()]} + + @spec new(ids :: [non_neg_integer()]) :: t() 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) + ids_bin = Enum.reduce(ids, <<>>, fn id, acc -> <> end) - 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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + n_controllers::16, + 0::size(4)-unit(8), + ids_bin::bytes + >>) end def read(<<@experimenter::32, @nxast::16, n_controllers::16, body::bitstring>>) do diff --git a/lib/openflow/actions/nx_exit.ex b/lib/openflow/actions/nx_exit.ex index cc9786b..3c880c1 100644 --- a/lib/openflow/actions/nx_exit.ex +++ b/lib/openflow/actions/nx_exit.ex @@ -5,17 +5,17 @@ defmodule Openflow.Action.NxExit do @nxast 17 alias __MODULE__ + alias Openflow.Action.Experimenter 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>> + Experimenter.pack_exp_header(<<@experimenter::32, @nxast::16, 0::48>>) end - def read(<<@experimenter::32, @nxast::16, 0::48>>) do + def read(<<@experimenter::32, @nxast::16, 0::48, _::bytes>>) do %NxExit{} end end diff --git a/lib/openflow/actions/nx_fin_timeout.ex b/lib/openflow/actions/nx_fin_timeout.ex index 8f8e005..e9db357 100644 --- a/lib/openflow/actions/nx_fin_timeout.ex +++ b/lib/openflow/actions/nx_fin_timeout.ex @@ -8,7 +8,9 @@ defmodule Openflow.Action.NxFinTimeout do @nxast 19 alias __MODULE__ + alias Openflow.Action.Experimenter + @spec new(Keyword.t()) :: %NxFinTimeout{} def new(options) do %NxFinTimeout{ idle_timeout: options[:idle_timeout] || 0, @@ -16,9 +18,13 @@ defmodule Openflow.Action.NxFinTimeout do } 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)>> + def to_binary(%NxFinTimeout{} = fin_timeout) do + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + fin_timeout.idle_timeout::16, + fin_timeout.hard_timeout::16 + >>) end def read(<<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16, _::size(2)-unit(8)>>) do diff --git a/lib/openflow/actions/nx_flow_spec_load.ex b/lib/openflow/actions/nx_flow_spec_load.ex index dcdbf86..bd7a849 100644 --- a/lib/openflow/actions/nx_flow_spec_load.ex +++ b/lib/openflow/actions/nx_flow_spec_load.ex @@ -13,12 +13,28 @@ defmodule Openflow.Action.NxFlowSpecLoad do alias __MODULE__ + @type t :: %NxFlowSpecLoad{ + src: atom(), + dst: atom(), + n_bits: non_neg_integer(), + src_offset: non_neg_integer(), + dst_offset: non_neg_integer() + } + + @spec new( + src: atom(), + dst: atom(), + n_bits: non_neg_integer(), + src_offset: non_neg_integer(), + dst_offset: non_neg_integer() + ) :: t() def new(options) do - dst = options[:dst] + dst = options[:dst] || raise(":dst must be specified") + src = options[:src] || raise(":src must be specified") n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst) %NxFlowSpecLoad{ - src: options[:src], + src: src, dst: dst, n_bits: n_bits, src_offset: options[:src_offset] || 0, diff --git a/lib/openflow/actions/nx_flow_spec_match.ex b/lib/openflow/actions/nx_flow_spec_match.ex index feae23d..22312a9 100644 --- a/lib/openflow/actions/nx_flow_spec_match.ex +++ b/lib/openflow/actions/nx_flow_spec_match.ex @@ -13,12 +13,28 @@ defmodule Openflow.Action.NxFlowSpecMatch do alias __MODULE__ - def new(options) do - dst = options[:dst] + @type t :: %NxFlowSpecMatch{ + src: atom(), + dst: atom(), + n_bits: non_neg_integer(), + src_offset: non_neg_integer(), + dst_offset: non_neg_integer() + } + + @spec new( + src: atom(), + dst: atom(), + n_bits: non_neg_integer(), + src_offset: non_neg_integer(), + dst_offset: non_neg_integer() + ) :: t() + def new(options \\ []) do + dst = options[:dst] || raise ":dst must be specified" + src = options[:src] || raise ":src must be specified" n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst) %NxFlowSpecMatch{ - src: options[:src], + src: src, dst: dst, n_bits: n_bits, src_offset: options[:src_offset] || 0, @@ -26,6 +42,7 @@ defmodule Openflow.Action.NxFlowSpecMatch do } end + @spec to_binary(t()) :: binary() def to_binary(%NxFlowSpecMatch{} = fsm) do %NxFlowSpecMatch{ dst: dst_field, @@ -48,6 +65,7 @@ defmodule Openflow.Action.NxFlowSpecMatch do end end + @spec read(binary()) :: {t(), binary()} 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>> diff --git a/lib/openflow/actions/nx_flow_spec_output.ex b/lib/openflow/actions/nx_flow_spec_output.ex index 7f0500b..87980f7 100644 --- a/lib/openflow/actions/nx_flow_spec_output.ex +++ b/lib/openflow/actions/nx_flow_spec_output.ex @@ -10,23 +10,42 @@ defmodule Openflow.Action.NxFlowSpecOutput do alias __MODULE__ - def new(options) do - src = options[:src] + @type t :: %NxFlowSpecOutput{ + n_bits: non_neg_integer(), + src: atom(), + src_offset: non_neg_integer() + } + + @spec new(src: atom(), n_bits: non_neg_integer(), src_offset: non_neg_integer()) :: t() + def new(options \\ []) do + src = options[:src] || raise ":src must be specified" n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(src) %NxFlowSpecOutput{n_bits: n_bits, src: src, src_offset: options[:src_offset] || 0} 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>> + @spec to_binary(t()) :: binary() + def to_binary(%NxFlowSpecOutput{} = flow_spec) do + << + 0::2, + @learn_src_field::1, + @learn_dst::2, + flow_spec.n_bits::11, + Openflow.Match.codec_header(flow_spec.src)::4-bytes, + flow_spec.src_offset::16 + >> end + @spec read(binary()) :: {t(), binary()} 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 = %NxFlowSpecOutput{ + n_bits: n_bits, + src: Openflow.Match.codec_header(src_bin), + 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 index 04b6cf3..5ac243c 100644 --- a/lib/openflow/actions/nx_learn.ex +++ b/lib/openflow/actions/nx_learn.ex @@ -15,6 +15,7 @@ defmodule Openflow.Action.NxLearn do @nxast 16 alias __MODULE__ + alias Openflow.Action.Experimenter def new(options) do %NxLearn{ @@ -30,28 +31,24 @@ defmodule Openflow.Action.NxLearn do } 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) + def to_binary(%NxLearn{} = learn) do + learn_flags_int = Openflow.Enums.flags_to_int(learn.flags, :nx_learn_flag) + flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(learn.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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + learn.idle_timeout::16, + learn.hard_timeout::16, + learn.priority::16, + learn.cookie::64, + learn_flags_int::16, + learn.table_id::8, + 0::8, + learn.fin_idle_timeout::16, + learn.fin_hard_timeout::16, + flow_specs_bin::bitstring + >>) end def read(<<@experimenter::32, @nxast::16, body::bitstring>>) do diff --git a/lib/openflow/actions/nx_learn2.ex b/lib/openflow/actions/nx_learn2.ex index ea3a49d..02fcd6f 100644 --- a/lib/openflow/actions/nx_learn2.ex +++ b/lib/openflow/actions/nx_learn2.ex @@ -18,8 +18,9 @@ defmodule Openflow.Action.NxLearn2 do @nxast 45 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do + def new(options \\ []) do %NxLearn2{ idle_timeout: options[:idle_timeout] || 0, hard_timeout: options[:hard_timeout] || 0, @@ -36,40 +37,33 @@ defmodule Openflow.Action.NxLearn2 do } 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) + def to_binary(%NxLearn2{} = learn) do + learn_flags_int = Openflow.Enums.flags_to_int(learn.flags, :nx_learn_flag) + learn_flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(learn.flow_specs) - result_dst_bin = - if :write_result in flags do - Openflow.Match.codec_header(result_dst) - else - "" - end + learn_result_dst_bin = + if :write_result in learn.flags, + do: Openflow.Match.codec_header(learn.result_dst), + else: <<>> - 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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + learn.idle_timeout::16, + learn.hard_timeout::16, + learn.priority::16, + learn.cookie::64, + learn_flags_int::16, + learn.table_id::8, + 0::size(1)-unit(8), + learn.fin_idle_timeout::16, + learn.fin_hard_timeout::16, + learn.limit::32, + learn.result_dst_offset::16, + 0::size(2)-unit(8), + learn_result_dst_bin::bytes, + learn_flow_specs_bin::bitstring + >>) end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do diff --git a/lib/openflow/actions/nx_multipath.ex b/lib/openflow/actions/nx_multipath.ex index 72975e0..1e7c693 100644 --- a/lib/openflow/actions/nx_multipath.ex +++ b/lib/openflow/actions/nx_multipath.ex @@ -16,54 +16,44 @@ defmodule Openflow.Action.NxMultipath do @nxast 10 alias __MODULE__ + alias Openflow.Action.Experimenter + @spec new(Keyword.t()) :: %NxMultipath{} 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) + dst_field = options[:dst_field] || raise "dst_field must be specified" 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, + hash_field: options[:hash_field] || :eth_src, + basis: options[:basis] || 0, + algorithm: options[:algorithm] || :modulo_n, + max_link: options[:max_link] || 0, + offset: options[:offset] || 0, + n_bits: options[:n_bits] || default_n_bits, + argument: options[:argument] || 0, 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 + def to_binary(%NxMultipath{} = multipath) do + hash_field_int = Openflow.Enums.to_int(multipath.hash_field, :nx_hash_fields) + alg_int = Openflow.Enums.to_int(multipath.algorithm, :nx_mp_algorithm) + dst_field_bin = Openflow.Match.codec_header(multipath.dst_field) + ofs_nbits = multipath.offset <<< 6 ||| multipath.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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + hash_field_int::16, + multipath.basis::16, + 0::size(2)-unit(8), + alg_int::16, + multipath.max_link::16, + multipath.argument::32, + 0::size(2)-unit(8), + ofs_nbits::16, + dst_field_bin::4-bytes + >>) end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do diff --git a/lib/openflow/actions/nx_nat.ex b/lib/openflow/actions/nx_nat.ex index 921d5f1..f6694ee 100644 --- a/lib/openflow/actions/nx_nat.ex +++ b/lib/openflow/actions/nx_nat.ex @@ -13,49 +13,62 @@ defmodule Openflow.Action.NxNat do @nxast 36 alias __MODULE__ + alias Openflow.Action.Experimenter + @type in_addr :: :inet.ip4_address() + @type in6_addr :: :inet.ip6_address() + @type port_number :: :inet.port_number() + @type flag :: :src | :dst | :persistent | :protocol_hash | :protocol_random + @type nat_range :: :ipv4_min | :ipv4_max | :ipv6_min | :ipv6_max | :proto_min | :proto_max + @type t :: %NxNat{ + flags: [flag()], + ipv4_min: in_addr(), + ipv4_max: in_addr(), + ipv6_min: in6_addr(), + ipv6_max: in6_addr(), + proto_min: port_number(), + proto_max: port_number() + } + + @spec new( + flags: [flag()], + ipv4_min: in_addr(), + ipv4_max: in_addr(), + ipv6_min: in6_addr(), + ipv6_max: in6_addr(), + proto_min: port_number(), + proto_max: port_number() + ) :: t() 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 + flags: options[:flags] || [], + ipv4_min: options[:ipv4_min], + ipv4_max: options[:ipv4_max], + ipv6_min: options[:ipv6_min], + ipv6_max: options[:ipv6_max], + proto_min: options[:proto_min], + proto_max: options[:proto_max] } end + @spec to_binary(t()) :: binary() 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) - + range_flags = get_range_flags(nat) 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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + 0::size(2)-unit(8), + flags_int::16, + range_flags_int::16, + ranges_bin::bytes + >>) end + @spec read(binary()) :: t() 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) @@ -65,81 +78,95 @@ defmodule Openflow.Action.NxNat do # 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) + @range_keys [:ipv4_min, :ipv4_max, :ipv6_min, :ipv6_max, :proto_min, :proto_max] + + @spec get_range_flags(t()) :: [nat_range()] + defp get_range_flags(nat) do + @range_keys + |> Enum.reduce([], fn + key, acc when key in @range_keys -> + if not is_nil(Map.get(nat, key)), do: [key | acc], else: acc + end) + |> Enum.reverse() end + @spec encode_ranges(binary(), [nat_range()], t()) :: binary() 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 + defp encode_ranges(acc, [:ipv4_min | rest], %NxNat{ipv4_min: {a1, a2, a3, a4}} = nat) do + encode_ranges(<>, rest, nat) end + defp encode_ranges(acc, [:ipv4_max | rest], %NxNat{ipv4_max: {a1, a2, a3, a4}} = nat) do + encode_ranges(<>, rest, nat) + end + + defp encode_ranges( + acc, + [:ipv6_min | rest], + %NxNat{ipv6_min: {a1, a2, a3, a4, a5, a6, a7, a8}} = nat + ) do + encode_ranges( + <>, + rest, + nat + ) + end + + defp encode_ranges( + acc, + [:ipv6_max | rest], + %NxNat{ipv6_max: {a1, a2, a3, a4, a5, a6, a7, a8}} = nat + ) do + encode_ranges( + <>, + rest, + nat + ) + end + + defp encode_ranges(acc, [:proto_min | rest], %NxNat{proto_min: proto} = nat) + when is_integer(proto) do + encode_ranges(<>, rest, nat) + end + + defp encode_ranges(acc, [:proto_max | rest], %NxNat{proto_max: proto} = nat) + when is_integer(proto) do + encode_ranges(<>, rest, nat) + end + + @spec decode_ranges(t(), [nat_range()], binary()) :: t() 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) + defp decode_ranges(acc, [:ipv4_min | rest], <>) do + decode_ranges(%{acc | ipv4_min: {a1, a2, a3, a4}}, rest, binary) + end - rest -> - decode_ranges(struct(nat, %{range => ""}), ranges, rest) - end + defp decode_ranges(acc, [:ipv4_max | rest], <>) do + decode_ranges(%{acc | ipv4_max: {a1, a2, a3, a4}}, rest, binary) + 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) + defp decode_ranges( + acc, + [:ipv6_min | rest], + <> + ) do + decode_ranges(%{acc | ipv6_min: {a1, a2, a3, a4, a5, a6, a7, a8}}, rest, binary) + end - rest -> - decode_ranges(struct(nat, %{range => ""}), ranges, rest) - end + defp decode_ranges( + acc, + [:ipv6_max | rest], + <> + ) do + decode_ranges(%{acc | ipv6_max: {a1, a2, a3, a4, a5, a6, a7, a8}}, rest, binary) + end - range == :proto_min or range == :proto_max -> - case bin do - <> when proto in 1..0xFFFF -> - decode_ranges(struct(nat, %{range => proto}), ranges, rest) + defp decode_ranges(acc, [:proto_min | rest], <>) do + decode_ranges(%{acc | proto_min: proto}, rest, binary) + end - rest -> - decode_ranges(struct(nat, %{range => ""}), ranges, rest) - end - end + defp decode_ranges(acc, [:proto_max | rest], <>) do + decode_ranges(%{acc | proto_max: proto}, rest, binary) end end diff --git a/lib/openflow/actions/nx_note.ex b/lib/openflow/actions/nx_note.ex index b96847d..d6ce09a 100644 --- a/lib/openflow/actions/nx_note.ex +++ b/lib/openflow/actions/nx_note.ex @@ -5,18 +5,14 @@ defmodule Openflow.Action.NxNote do @nxast 8 alias __MODULE__ + alias Openflow.Action.Experimenter 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)>> + Experimenter.pack_exp_header(<<@experimenter::32, @nxast::16, note::bytes>>) end def read(<<@experimenter::32, @nxast::16, note_bin::bytes>>) do diff --git a/lib/openflow/actions/nx_output_reg.ex b/lib/openflow/actions/nx_output_reg.ex index 852358a..a5efe89 100644 --- a/lib/openflow/actions/nx_output_reg.ex +++ b/lib/openflow/actions/nx_output_reg.ex @@ -12,26 +12,47 @@ defmodule Openflow.Action.NxOutputReg do @nxast 15 alias __MODULE__ + alias Openflow.Action.Experimenter + @type max_len :: :no_buffer | :max | non_neg_integer() + @type t :: %NxOutputReg{ + n_bits: pos_integer(), + offset: non_neg_integer(), + src_field: atom(), + max_len: max_len() + } + + @spec new( + n_bits: pos_integer(), + offset: non_neg_integer(), + src_field: atom(), + max_len: max_len() + ) :: t() def new(options) do - src_field = Keyword.get(options, :src_field) + src_field = options[:src_field] || raise "src_field must be specified" 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} + + %NxOutputReg{ + n_bits: options[:n_bits] || default_n_bits, + offset: options[:offset] || 0, + src_field: src_field, + max_len: options[:max_len] || :no_buffer + } 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)>> + def to_binary(%NxOutputReg{} = output_reg) do + src_field_bin = Openflow.Match.codec_header(output_reg.src_field) + ofs_nbits = output_reg.offset <<< 6 ||| output_reg.n_bits - 1 + max_len = Openflow.Utils.get_enum(output_reg.max_len, :controller_max_len) + + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + ofs_nbits::16, + src_field_bin::4-bytes, + max_len::16, + 0::size(6)-unit(8) + >>) end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do diff --git a/lib/openflow/actions/nx_output_reg2.ex b/lib/openflow/actions/nx_output_reg2.ex index e254cf3..bfaf212 100644 --- a/lib/openflow/actions/nx_output_reg2.ex +++ b/lib/openflow/actions/nx_output_reg2.ex @@ -12,27 +12,52 @@ defmodule Openflow.Action.NxOutputReg2 do @nxast 32 alias __MODULE__ + alias Openflow.Action.Experimenter + @type max_len :: :no_buffer | :max | non_neg_integer() + @type t :: %NxOutputReg2{ + n_bits: pos_integer(), + offset: non_neg_integer(), + src_field: atom(), + max_len: max_len() + } + + @spec new( + n_bits: pos_integer(), + offset: non_neg_integer(), + src_field: atom(), + max_len: max_len() + ) :: t() def new(options) do - src_field = Keyword.get(options, :src_field) + src_field = options[:src_field] || raise "src_field must be specified" 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} + + %NxOutputReg2{ + n_bits: options[:n_bits] || default_n_bits, + offset: options[:offset] || 0, + src_field: src_field, + max_len: options[:max_len] || :no_buffer + } 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)>> + def to_binary(%NxOutputReg2{} = output_reg) do + src_field_bin = Openflow.Match.codec_header(output_reg.src_field) + ofs_nbits = output_reg.offset <<< 6 ||| output_reg.n_bits - 1 + max_len = Openflow.Utils.get_enum(output_reg.max_len, :controller_max_len) + + padding = + src_field_bin + |> byte_size() + |> Openflow.Utils.padding(10) + + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + ofs_nbits::16, + max_len::16, + src_field_bin::bytes, + 0::size(padding)-unit(8) + >>) end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do diff --git a/lib/openflow/actions/nx_output_trunc.ex b/lib/openflow/actions/nx_output_trunc.ex index 98b3c6e..e60acb7 100644 --- a/lib/openflow/actions/nx_output_trunc.ex +++ b/lib/openflow/actions/nx_output_trunc.ex @@ -8,20 +8,28 @@ defmodule Openflow.Action.NxOutputTrunc do @nxast 39 alias __MODULE__ + alias Openflow.Action.Experimenter + @spec new(Keyword.t()) :: %NxOutputTrunc{} 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} + port_no = options[:port_no] || raise "port_no must be specified" + max_len = options[:max_len] || raise "max_len must be specified" + + %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)>> + + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + port_no_int::16, + max_len::32 + >>) end def read(<<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>>) do diff --git a/lib/openflow/actions/nx_pop_mpls.ex b/lib/openflow/actions/nx_pop_mpls.ex deleted file mode 100644 index f14f863..0000000 --- a/lib/openflow/actions/nx_pop_mpls.ex +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 4821cd3..0000000 --- a/lib/openflow/actions/nx_pop_queue.ex +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 4db52b0..0000000 --- a/lib/openflow/actions/nx_push_mpls.ex +++ /dev/null @@ -1,24 +0,0 @@ -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 index 1ee7da5..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( @@ -12,28 +40,37 @@ defmodule Openflow.Action.NxRegLoad do @nxast 7 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do - dst_field = Keyword.get(options, :dst_field) + def new(options \\ []) do + dst_field = options[:dst_field] || raise "dst_field must be specified" + value = options[:value] || raise "value must be specified" 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} + + %NxRegLoad{ + n_bits: options[:n_bits] || default_n_bits, + offset: options[:offset] || 0, + 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)>> + def to_binary(%NxRegLoad{} = load) do + ofs_nbits = load.offset <<< 6 ||| load.n_bits - 1 + dst_field_bin = Openflow.Match.codec_header(load.dst_field) + + value_int = + load.value + |> Openflow.Match.Field.codec(load.dst_field) + |> :binary.decode_unsigned(:big) + + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + ofs_nbits::16, + dst_field_bin::4-bytes, + value_int::size(8)-unit(8) + >>) end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do diff --git a/lib/openflow/actions/nx_reg_load2.ex b/lib/openflow/actions/nx_reg_load2.ex index 0c1510e..fd98dd0 100644 --- a/lib/openflow/actions/nx_reg_load2.ex +++ b/lib/openflow/actions/nx_reg_load2.ex @@ -1,44 +1,38 @@ defmodule Openflow.Action.NxRegLoad2 do - defstruct( - dst_field: nil, - value: nil - ) + defstruct(field: nil) @experimenter 0x00002320 @nxast 33 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do - dst_field = Keyword.get(options, :dst_field) - value = Keyword.get(options, :value) - %NxRegLoad2{dst_field: dst_field, value: value} + def new([{_field, _value}] = field) do + %NxRegLoad2{field: field} end - def to_binary(%NxRegLoad2{dst_field: dst_field, value: value}) do + def to_binary(%NxRegLoad2{field: field}) do match_bin = - [{dst_field, value}] + field |> 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) + <<1::16, length::16, padded_field::bytes>> = match_bin + field_len = length - 4 + <> = padded_field - 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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + field_bin::bytes + >>) end - def read(<<@experimenter::32, @nxast::16, _::48, match_field_bin::bytes>>) do + def read(<<@experimenter::32, @nxast::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, 0::size(4)-unit(8)>> - {[{dst_field, value} | _], _rest} = Openflow.Match.read(match_bin) - %NxRegLoad2{dst_field: dst_field, value: value} + match_bin = <<1::16, match_len::16, match_field_bin::bytes>> + {[field | _], _rest} = Openflow.Match.read(match_bin) + %NxRegLoad2{field: [field]} end end diff --git a/lib/openflow/actions/nx_reg_move.ex b/lib/openflow/actions/nx_reg_move.ex index 309fd38..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, @@ -11,44 +101,80 @@ defmodule Openflow.Action.NxRegMove do @nxast 6 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do - src_field = Keyword.get(options, :src_field) - dst_field = Keyword.get(options, :dst_field) + @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" 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, + n_bits: options[:n_bits] || default_n_bits, + src_offset: options[:src_offset] || 0, + dst_offset: options[:dst_offset] || 0, 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) + @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) - 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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + move.n_bits::16, + move.src_offset::16, + move.dst_offset::16, + src_field_bin::4-bytes, + dst_field_bin::4-bytes + >>) 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 9ed1623..4d07b39 100644 --- a/lib/openflow/actions/nx_resubmit.ex +++ b/lib/openflow/actions/nx_resubmit.ex @@ -1,24 +1,76 @@ 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 @nxast 1 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) - 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)>> + + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + in_port_int::16, + 0::size(4)-unit(8) + >>) 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_resubmit_table.ex b/lib/openflow/actions/nx_resubmit_table.ex index 04bbba8..1553a71 100644 --- a/lib/openflow/actions/nx_resubmit_table.ex +++ b/lib/openflow/actions/nx_resubmit_table.ex @@ -5,25 +5,31 @@ defmodule Openflow.Action.NxResubmitTable do @nxast 14 alias __MODULE__ + alias Openflow.Action.Experimenter + + def new(options \\ []) 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) + in_port = options[:in_port] || :in_port + table_id = 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)>> + def to_binary(%NxResubmitTable{} = resubmit_table) do + in_port_int = Openflow.Utils.get_enum(resubmit_table.in_port, :openflow10_port_no) + table_id_int = Openflow.Utils.get_enum(resubmit_table.table_id, :table_id) + + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + in_port_int::16, + table_id_int::8, + 0::24 + >>) end def read( diff --git a/lib/openflow/actions/nx_resubmit_table_ct.ex b/lib/openflow/actions/nx_resubmit_table_ct.ex index 849640e..6eb8bc9 100644 --- a/lib/openflow/actions/nx_resubmit_table_ct.ex +++ b/lib/openflow/actions/nx_resubmit_table_ct.ex @@ -5,21 +5,31 @@ defmodule Openflow.Action.NxResubmitTableCt do @nxast 44 alias __MODULE__ + alias Openflow.Action.Experimenter - def new(options) do - in_port = Keyword.get(options, :in_port, :in_port) - table_id = Keyword.get(options, :table_id, :all) + def new(options \\ []) + + def new(table_id) when is_atom(table_id) or is_integer(table_id) do + new(table_id: table_id) + end + + def new(options) when is_list(options) do + in_port = options[:in_port] || :in_port + table_id = 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)>> + def to_binary(%NxResubmitTableCt{} = resubmit_table) do + in_port_int = Openflow.Utils.get_enum(resubmit_table.in_port, :openflow10_port_no) + table_id_int = Openflow.Utils.get_enum(resubmit_table.table_id, :table_id) + + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + in_port_int::16, + table_id_int::8, + 0::24 + >>) end def read( diff --git a/lib/openflow/actions/nx_sample.ex b/lib/openflow/actions/nx_sample.ex index 206c0ed..7c9c04b 100644 --- a/lib/openflow/actions/nx_sample.ex +++ b/lib/openflow/actions/nx_sample.ex @@ -10,35 +10,40 @@ defmodule Openflow.Action.NxSample do @nxast 29 alias __MODULE__ + alias Openflow.Action.Experimenter + @spec new( + probability: 1..0xFFFF, + collector_set_id: 0..0xFFFFFFFF, + obs_domain_id: 0..0xFFFFFFFF, + obs_point_id: 0..0xFFFFFFFF + ) :: %NxSample{ + probability: 1..0xFFFF, + collector_set_id: 0..0xFFFFFFFF, + obs_domain_id: 0..0xFFFFFFFF, + obs_point_id: 0..0xFFFFFFFF + } 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) + (is_integer(options[:probability]) and options[:probability] > 0) || + raise("probability must be greater than 0") %NxSample{ - probability: probability, - collector_set_id: collector_set_id, - obs_domain_id: obs_domain_id, - obs_point_id: obs_point_id + probability: options[:probability], + collector_set_id: options[:collector_set_id] || 0, + obs_domain_id: options[:obs_domain_id] || 0, + obs_point_id: options[:obs_point_id] || 0 } 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)>> + def to_binary(%NxSample{} = sample) do + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + sample.probability::16, + sample.collector_set_id::32, + sample.obs_domain_id::32, + sample.obs_point_id::32 + >>) end def read( diff --git a/lib/openflow/actions/nx_sample2.ex b/lib/openflow/actions/nx_sample2.ex index 271a3c6..6005efb 100644 --- a/lib/openflow/actions/nx_sample2.ex +++ b/lib/openflow/actions/nx_sample2.ex @@ -11,44 +11,44 @@ defmodule Openflow.Action.NxSample2 do @nxast 38 alias __MODULE__ + alias Openflow.Action.Experimenter - 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) + def new(options \\ []) do + (is_integer(options[:probability]) and options[:probability] > 0) || + raise("probability must be greater than 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 + probability: options[:probability], + collector_set_id: options[:collector_set_id] || 0, + obs_domain_id: options[:obs_domain_id] || 0, + obs_point_id: options[:obs_point_id] || 0, + sampling_port: options[:sampling_port] || 0 } 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)>> + def to_binary(%NxSample2{} = sample) do + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + sample.probability::16, + sample.collector_set_id::32, + sample.obs_domain_id::32, + sample.obs_point_id::32, + sample.sampling_port::16, + 0::size(6)-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 + def read(<< + @experimenter::32, + @nxast::16, + probability::16, + collector_set_id::32, + obs_domain_id::32, + obs_point_id::32, + sampling_port::16, + _::size(6)-unit(8) + >>) do %NxSample2{ probability: probability, collector_set_id: collector_set_id, diff --git a/lib/openflow/actions/nx_sample3.ex b/lib/openflow/actions/nx_sample3.ex index bb1c531..136cae6 100644 --- a/lib/openflow/actions/nx_sample3.ex +++ b/lib/openflow/actions/nx_sample3.ex @@ -12,43 +12,36 @@ defmodule Openflow.Action.NxSample3 do @nxast 41 alias __MODULE__ + alias Openflow.Action.Experimenter - 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) + def new(options \\ []) do + (is_integer(options[:probability]) and options[:probability] > 0) || + raise("probability must be greater than 0") %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 + probability: options[:probability] || 0, + collector_set_id: options[:collector_set_id] || 0, + obs_domain_id: options[:obs_domain_id] || 0, + obs_point_id: options[:obs_point_id] || 0, + sampling_port: options[:sampling_port] || 0, + direction: options[:direction] || :default } 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) + def to_binary(%NxSample3{} = sample) do + sample_direction_int = Openflow.Enums.to_int(sample.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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + sample.probability::16, + sample.collector_set_id::32, + sample.obs_domain_id::32, + sample.obs_point_id::32, + sample.sampling_port::16, + sample_direction_int::8, + 0::size(5)-unit(8) + >>) end def read( diff --git a/lib/openflow/actions/nx_set_mpls_label.ex b/lib/openflow/actions/nx_set_mpls_label.ex deleted file mode 100644 index 9fdcf04..0000000 --- a/lib/openflow/actions/nx_set_mpls_label.ex +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 9392558..0000000 --- a/lib/openflow/actions/nx_set_mpls_tc.ex +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index cd932af..0000000 --- a/lib/openflow/actions/nx_set_mpls_ttl.ex +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 845c494..0000000 --- a/lib/openflow/actions/nx_set_queue.ex +++ /dev/null @@ -1,24 +0,0 @@ -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 index 661114b..b0f836e 100644 --- a/lib/openflow/actions/nx_set_tunnel.ex +++ b/lib/openflow/actions/nx_set_tunnel.ex @@ -1,23 +1,43 @@ defmodule Openflow.Action.NxSetTunnel do + @moduledoc """ + Set encapsulating tunnel ID. + """ + defstruct(tunnel_id: 0) @experimenter 0x00002320 @nxast 2 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 - 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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + 0::16, + tunnel_id::32 + >>) 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/nx_set_tunnel64.ex b/lib/openflow/actions/nx_set_tunnel64.ex index d729689..e719dfd 100644 --- a/lib/openflow/actions/nx_set_tunnel64.ex +++ b/lib/openflow/actions/nx_set_tunnel64.ex @@ -5,17 +5,22 @@ defmodule Openflow.Action.NxSetTunnel64 do @nxast 9 alias __MODULE__ + alias Openflow.Action.Experimenter + @spec new(tunnel_id :: 0..0xFFFFFFFFFFFFFFFF) :: %NxSetTunnel64{ + tunnel_id: 0..0xFFFFFFFFFFFFFFFF + } 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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + 0::size(6)-unit(8), + tunnel_id::64 + >>) end def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>>) do diff --git a/lib/openflow/actions/nx_stack_pop.ex b/lib/openflow/actions/nx_stack_pop.ex index 479932c..cc3be8c 100644 --- a/lib/openflow/actions/nx_stack_pop.ex +++ b/lib/openflow/actions/nx_stack_pop.ex @@ -9,26 +9,30 @@ defmodule Openflow.Action.NxStackPop do @nxast 28 alias __MODULE__ + alias Openflow.Action.Experimenter def new(options) do - field = Keyword.get(options, :field) + field = options[:field] || raise "field must be specified" 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} + + %NxStackPop{ + n_bits: options[:n_bits] || default_n_bits, + offset: options[:offset] || 0, + field: field + } end - def to_binary(%NxStackPop{n_bits: n_bits, offset: ofs, field: field}) do - field_bin = Openflow.Match.codec_header(field) + def to_binary(%NxStackPop{} = stack_pop) do + field_bin = Openflow.Match.codec_header(stack_pop.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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + stack_pop.offset::16, + field_bin::4-bytes, + stack_pop.n_bits::16, + 0::size(6)-unit(8) + >>) end def read( diff --git a/lib/openflow/actions/nx_stack_push.ex b/lib/openflow/actions/nx_stack_push.ex index aa33e71..b4eee19 100644 --- a/lib/openflow/actions/nx_stack_push.ex +++ b/lib/openflow/actions/nx_stack_push.ex @@ -9,26 +9,30 @@ defmodule Openflow.Action.NxStackPush do @nxast 27 alias __MODULE__ + alias Openflow.Action.Experimenter def new(options) do - field = Keyword.get(options, :field) + field = options[:field] || raise "field must be specified" 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} + + %NxStackPush{ + n_bits: options[:n_bits] || default_n_bits, + offset: options[:offset] || 0, + field: field + } end - def to_binary(%NxStackPush{n_bits: n_bits, offset: ofs, field: field}) do - field_bin = Openflow.Match.codec_header(field) + def to_binary(%NxStackPush{} = stack_pop) do + field_bin = Openflow.Match.codec_header(stack_pop.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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + stack_pop.offset::16, + field_bin::4-bytes, + stack_pop.n_bits::16, + 0::size(6)-unit(8) + >>) end def read( diff --git a/lib/openflow/actions/nx_write_metadata.ex b/lib/openflow/actions/nx_write_metadata.ex index 4396d6a..50d05f5 100644 --- a/lib/openflow/actions/nx_write_metadata.ex +++ b/lib/openflow/actions/nx_write_metadata.ex @@ -8,25 +8,28 @@ defmodule Openflow.Action.NxWriteMetadata do @nxast 22 alias __MODULE__ + alias Openflow.Action.Experimenter + + def new(options \\ []) 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) + metadata = options[:metadata] || raise "metadata must be specified" + metadata_mask = 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)>> + Experimenter.pack_exp_header(<< + @experimenter::32, + @nxast::16, + 0::size(6)-unit(8), + metadata::64, + metadata_mask::64 + >>) end def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8), metadata::64, metadata_mask::64>>) do diff --git a/lib/openflow/actions/output.ex b/lib/openflow/actions/output.ex index 070b123..2efe8bc 100644 --- a/lib/openflow/actions/output.ex +++ b/lib/openflow/actions/output.ex @@ -1,29 +1,67 @@ defmodule Openflow.Action.Output do - defstruct( - port_number: 0, - max_len: :no_buffer - ) + @moduledoc """ + Action for sends packets out `port_number`. + """ + + defstruct port_number: 0, + max_len: :no_buffer alias __MODULE__ + @type port_no :: + 0..0xFFFFFFFF + | :max + | :in_port + | :table + | :normal + | :flood + | :all + | :controller + | :local + | :any + + @type max_len :: 0..0xFFFFFFFF | :no_buffer | :max + + @type t :: %Output{port_number: port_no(), max_len: max_len()} + + @spec ofpat() :: 0 def ofpat, do: 0 - def new(port) when not is_list(port) do - new(port_number: port) - end + @doc """ + Create a new output action structure + ## Options: + - `port_number`: Output port + - `max_len`: Max length to send to controller + + ## Example + + ```elixir + iex> %Output{port_number: 1, max_len: :no_buffer} = Output.new(1) + ``` + """ + @spec new([port_number: port_no, max_len: max_len()] | port_no()) :: Output.t() + def new(options \\ []) + + @spec new(port :: port_no()) :: Output.t() + def new(port) when is_atom(port) or is_integer(port), + do: new(port_number: port) + + @spec new(options :: [port_number: port_no(), max_len: max_len()]) :: Output.t() def new(options) when is_list(options) do - port_no = Keyword.get(options, :port_number) - max_len = Keyword.get(options, :max_len, :no_buffer) + port_no = options[:port_number] || raise "port_number must be specified" + max_len = options[:max_len] || :no_buffer %Output{port_number: port_no, max_len: max_len} end + @spec to_binary(Output.t()) :: <<_::16, _::_*8>> 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 + @spec read(<<_::16, _::_*8>>) :: Output.t() 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) diff --git a/lib/openflow/actions/pop_mpls.ex b/lib/openflow/actions/pop_mpls.ex index 7ea7950..3695991 100644 --- a/lib/openflow/actions/pop_mpls.ex +++ b/lib/openflow/actions/pop_mpls.ex @@ -1,18 +1,42 @@ defmodule Openflow.Action.PopMpls do + @moduledoc """ + Pop the out MPLS label + + note: The one of ETH__P_MPLS_* is needed to be specified to eth_type field + """ + defstruct(ethertype: 0x8847) alias __MODULE__ + @type t :: %PopMpls{} + + @eth_p_mpls_uc 0x8847 + + @spec ofpat() :: 20 def ofpat, do: 20 - def new(ethertype) do + @doc """ + Create a new pop_mpls action struct + + 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() + def new(ethertype \\ @eth_p_mpls_uc) 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 8ea4f26..87a8e60 100644 --- a/lib/openflow/actions/pop_vlan.ex +++ b/lib/openflow/actions/pop_vlan.ex @@ -1,18 +1,35 @@ defmodule Openflow.Action.PopVlan do + @moduledoc """ + Pop the outer VLAN tag. + """ + defstruct([]) alias __MODULE__ + @type t() :: %PopVlan{} + + @spec ofpat() :: 18 def ofpat, do: 18 + @doc """ + Create a new pop_vlan action struct + + ````elixir + iex> %PopVlan{} = PopVlan.new() + ```` + """ + @spec new() :: t() def new do %PopVlan{} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%PopVlan{}) do <<18::16, 8::16, 0::size(4)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<18::16, 8::16, _::size(4)-unit(8)>>) do %PopVlan{} end diff --git a/lib/openflow/actions/push_mpls.ex b/lib/openflow/actions/push_mpls.ex index f9c7fe0..4f3383f 100644 --- a/lib/openflow/actions/push_mpls.ex +++ b/lib/openflow/actions/push_mpls.ex @@ -1,18 +1,37 @@ defmodule Openflow.Action.PushMpls do + @moduledoc """ + Push a new MPLS label + """ + defstruct(ethertype: 0x8847) + @eth_p_mpls_uc 0x8847 + alias __MODULE__ + @type t :: %PushMpls{ethertype: 0..0xFFFF} + + @spec ofpat() :: 19 def ofpat, do: 19 - def new(ethertype) do + @doc """ + Create a new push_mpls action struct\\ + 0x8847(ETH_P_MPLS_UC) as a default value. + + iex> %PushMpls{ethertype: 0x8847} = PushMpls.new() + """ + @spec new() :: t() + @spec new(ethertype :: 0..0xFFFF) :: t() + def new(ethertype \\ @eth_p_mpls_uc) do %PushMpls{ethertype: ethertype} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%PushMpls{ethertype: ethertype}) do <<19::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<19::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do %PushMpls{ethertype: ethertype} end diff --git a/lib/openflow/actions/push_vlan.ex b/lib/openflow/actions/push_vlan.ex index 8c8ca39..e922adc 100644 --- a/lib/openflow/actions/push_vlan.ex +++ b/lib/openflow/actions/push_vlan.ex @@ -1,18 +1,34 @@ defmodule Openflow.Action.PushVlan do + @moduledoc """ + Push a new VLAN tag + """ + defstruct(ethertype: 0x8100) alias __MODULE__ + @type t :: %PushVlan{ethertype: 0..0xFFFF} + @spec ofpat() :: 17 def ofpat, do: 17 + @doc """ + Create a new push_vlan action struct.\\ + 0x8100 is a default value. + + iex> %PushVlan{ethertype: 0x8100} = PushVlan.new() + """ + @spec new() :: t() + @spec new(ethertype :: 0..0xFFFF) :: t() def new(ethertype \\ 0x8100) do %PushVlan{ethertype: ethertype} end + @spec to_binary(t()) :: <<_::16, _::_*8>> def to_binary(%PushVlan{ethertype: ethertype}) do <<17::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: t() def read(<<17::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do %PushVlan{ethertype: ethertype} end diff --git a/lib/openflow/actions/set_field.ex b/lib/openflow/actions/set_field.ex index 79b4070..a7c9699 100644 --- a/lib/openflow/actions/set_field.ex +++ b/lib/openflow/actions/set_field.ex @@ -1,19 +1,81 @@ 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 - def new({_field, _value} = oxm_field) do + @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 - def to_binary(%SetField{field: {field, value}}) do + def to_binary(%SetField{field: field}) do match_bin = - [{field, value}] + field |> Openflow.Match.new() |> Openflow.Match.to_binary() @@ -29,6 +91,6 @@ defmodule Openflow.Action.SetField do 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} + %SetField{field: [field]} end end diff --git a/lib/openflow/actions/set_mpls_ttl.ex b/lib/openflow/actions/set_mpls_ttl.ex index a82a0ec..84cfcfb 100644 --- a/lib/openflow/actions/set_mpls_ttl.ex +++ b/lib/openflow/actions/set_mpls_ttl.ex @@ -1,18 +1,38 @@ defmodule Openflow.Action.SetMplsTtl do + @moduledoc """ + Replace the existing MPLS TTL.\\ + Only applies to the packets with an existing MPLS shim header. + """ + defstruct(ttl: 0) alias __MODULE__ + @type t :: %SetMplsTtl{ttl: 0..0xFF} + + @spec ofpat() :: 15 def ofpat, do: 15 + @doc """ + Create a set_mpls_ttl action structure + + ## Example: + ```elixir + iex> %SetMplsTtl{ttl: 64} = Openflow.Action.SetMplsTtl.new(64) + ``` + """ + @deprecated "OFPAT_SET_MPLS_TTL is deprecated in OpenFlow13, use SetField" + @spec new(0..0xFF) :: SetMplsTtl.t() def new(ttl) do %SetMplsTtl{ttl: ttl} end + @spec to_binary(SetMplsTtl.t()) :: <<_::16, _::_*8>> def to_binary(%SetMplsTtl{ttl: ttl}) do <<15::16, 8::16, ttl::8, 0::size(3)-unit(8)>> end + @spec read(<<_::16, _::_*8>>) :: SetMplsTtl.t() def read(<<15::16, 8::16, ttl::8, _::size(3)-unit(8)>>) do %SetMplsTtl{ttl: ttl} 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 diff --git a/lib/openflow/barrier/reply.ex b/lib/openflow/barrier/reply.ex index 59f7f68..73a7367 100644 --- a/lib/openflow/barrier/reply.ex +++ b/lib/openflow/barrier/reply.ex @@ -12,15 +12,9 @@ defmodule Openflow.Barrier.Reply do def ofp_type, do: 21 - def new do - %Reply{} - end + def new(xid \\ 0), do: %Reply{xid: xid} - def read(_) do - %Reply{} - end + def read(_), do: %Reply{} - def to_binary(%Reply{}) do - <<>> - end + def to_binary(%Reply{}), do: <<>> end diff --git a/lib/openflow/barrier/request.ex b/lib/openflow/barrier/request.ex index 346270c..f1e2518 100644 --- a/lib/openflow/barrier/request.ex +++ b/lib/openflow/barrier/request.ex @@ -10,17 +10,22 @@ defmodule Openflow.Barrier.Request do alias __MODULE__ + @type t :: %Request{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: non_neg_integer() | nil + } + + @spec ofp_type() :: 20 def ofp_type, do: 20 - def new(xid \\ 0) do - %Request{xid: xid} - end + @spec new(xid :: 0..0xFFFFFFFF) :: t() + def new(xid \\ 0), do: %Request{xid: xid} - def read(_) do - %Request{} - end + @spec read(binary()) :: t() + def read(_), do: %Request{} - def to_binary(%Request{}) do - <<>> - end + @spec to_binary(t()) :: binary() + def to_binary(%Request{}), do: <<>> end diff --git a/lib/openflow/buckets.ex b/lib/openflow/buckets.ex index 005db35..8390452 100644 --- a/lib/openflow/buckets.ex +++ b/lib/openflow/buckets.ex @@ -8,23 +8,35 @@ defmodule Openflow.Bucket do alias __MODULE__ + @type t :: %Bucket{ + weight: 0..0xFFFF, + watch_port: Openflow.Port.no(), + watch_group: Openflow.GroupMod.id(), + actions: [Openflow.Actions.type()] + } + @header_size 16 + @spec new( + weight: 0..0xFFFF, + watch_port: Openflow.Port.no(), + watch_group: Openflow.GroupMod.id(), + actions: [Openflow.Actions.type()] + ) :: t() 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} + %Bucket{ + weight: options[:weight] || 0, + watch_port: options[:watch_port] || :any, + watch_group: options[:watch_group] || :any, + actions: options[:actions] || [] + } end - def read(buckets_bin) do - do_read([], buckets_bin) - end + @spec read(<<_::_*128>>) :: [t()] + def read(buckets_bin), do: do_read([], buckets_bin) - def to_binary(buckets) do - to_binary("", buckets) - end + @spec read([t()]) :: <<_::_*128>> + def to_binary(buckets), do: to_binary("", buckets) # private functions diff --git a/lib/openflow/echo.ex b/lib/openflow/echo.ex deleted file mode 100644 index d0cea11..0000000 --- a/lib/openflow/echo.ex +++ /dev/null @@ -1,2 +0,0 @@ -defmodule Openflow.Echo do -end diff --git a/lib/openflow/echo/reply.ex b/lib/openflow/echo/reply.ex index 9b0b2d8..f78013a 100644 --- a/lib/openflow/echo/reply.ex +++ b/lib/openflow/echo/reply.ex @@ -11,8 +11,18 @@ defmodule Openflow.Echo.Reply do alias __MODULE__ + @type t :: %Reply{ + version: 4, + xid: 0..0xFFFFFFFF, + data: String.t(), + datapath_id: String.t() | nil, + aux_id: 0..0xFFFF | nil + } + + @spec ofp_type :: 3 def ofp_type, do: 3 + @spec new(xid: 0..0xFFFFFFFF, data: String.t()) :: t() def new(options) when is_list(options) do %Reply{ xid: options[:xid] || 0, @@ -20,15 +30,15 @@ defmodule Openflow.Echo.Reply do } end - def new(data) when is_binary(data) do - %Reply{data: data} - end + @spec new(String.t()) :: t() + def new(data) when is_binary(data), do: %Reply{data: data} - def read(data) do - %Reply{data: data} - end + @spec new() :: t() + def new, do: new([]) - def to_binary(%Reply{data: data}) do - data - end + @spec read(String.t()) :: t() + def read(data), do: %Reply{data: data} + + @spec to_binary(t()) :: String.t() + def to_binary(%Reply{data: data}), do: data end diff --git a/lib/openflow/echo/request.ex b/lib/openflow/echo/request.ex index a1684d6..a433f4f 100644 --- a/lib/openflow/echo/request.ex +++ b/lib/openflow/echo/request.ex @@ -11,8 +11,18 @@ defmodule Openflow.Echo.Request do alias __MODULE__ + @type t :: %Request{ + version: 4, + xid: 0..0xFFFFFFFF, + data: String.t(), + datapath_id: String.t() | nil, + aux_id: 0..0xFFFF | nil + } + + @spec ofp_type :: 2 def ofp_type, do: 2 + @spec new(xid: 0..0xFFFFFFFF, data: String.t()) :: t() def new(options) when is_list(options) do %Request{ xid: options[:xid] || 0, @@ -20,15 +30,15 @@ defmodule Openflow.Echo.Request do } end - def new(data) when is_binary(data) do - %Request{data: data} - end + @spec new(String.t()) :: t() + def new(data) when is_binary(data), do: %Request{data: data} - def read(data) do - %Request{data: data} - end + @spec new() :: t() + def new, do: new([]) - def to_binary(%Request{data: data}) do - data - end + @spec read(String.t()) :: t() + def read(data), do: %Request{data: data} + + @spec to_binary(t()) :: String.t() + def to_binary(%Request{data: data}), do: data end diff --git a/lib/openflow/enum_gen.ex b/lib/openflow/enum_gen.ex deleted file mode 100644 index 1c2f023..0000000 --- a/lib/openflow/enum_gen.ex +++ /dev/null @@ -1,1122 +0,0 @@ -defmodule Openflow.EnumGen 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, - hp_ext_match: 0x00002428, - 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: [ - # Beginning of a new connection. - new: 1 <<< 0, - # Part of an existing connection. - est: 1 <<< 1, - # Related to an established connection. - rel: 1 <<< 2, - # Flow is in the reply direction. - rep: 1 <<< 3, - # Could not track connection. - inv: 1 <<< 4, - # Conntrack has occurred. - trk: 1 <<< 5, - # Packet's source address/port was mangled by NAT. - snat: 1 <<< 6, - # Packet's destination address/port was mangled by NAT. - dnat: 1 <<< 7 - ], - 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 - ], - hp_ext_match: [ - hp_udp_src_port_range: 0, - hp_udp_dst_port_range: 1, - hp_tcp_src_port_range: 2, - hp_tcp_dst_port_range: 3, - hp_tcp_flags: 4, - hp_custom_1: 5, - hp_custom_2: 6, - hp_custom_3: 7, - hp_custom_4: 8 - ], - hp_custom_match_type: [ - l2_start: 1, - l3_start: 2, - l4_start: 3 - ], - onf_ext_match: [ - onf_tcp_flags: 42, - onf_actset_output: 43, - onf_pbb_uca: 2560 - ], - 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 - ], - table_feature_prop_type: [ - instructions: 0, - instructions_miss: 1, - next_tables: 2, - next_tables_miss: 3, - write_actions: 4, - write_actions_miss: 5, - apply_actions: 6, - apply_actions_miss: 7, - match: 8, - wildcards: 10, - write_setfield: 12, - write_setfield_miss: 13, - apply_setfield: 14, - apply_setfield_miss: 15, - experimenter: 0xFFFE, - experimenter_miss: 0xFFFF - ] - ] - - def main(_) do - File.write( - "lib/openflow/enums.ex", - """ - defmodule Openflow.Enums do - @moduledoc "auto generated code"\n - """, - [:binary] - ) - - for {enum_name, enum_def} <- @enums do - to_int_fn_name = :"#{enum_name}_to_int" - - for {key, _value} <- enum_def do - File.write( - "lib/openflow/enums.ex", - """ - def to_int(#{inspect(key)}, :#{enum_name}) do - #{to_int_fn_name}(#{inspect(key)}) - catch - _class, _reason -> #{inspect(key)} - end\n - """, - [:append, :binary] - ) - end - - File.write( - "lib/openflow/enums.ex", - """ - def to_int(_int, :#{enum_name}) do - throw(:bad_enum) - end\n - """, - [:append, :binary] - ) - end - - for {enum_name, enum_def} <- @enums do - to_atom_fn_name = :"#{enum_name}_to_atom" - - for {_key, value} <- enum_def do - File.write( - "lib/openflow/enums.ex", - """ - def to_atom(#{inspect(value, base: :hex)}, :#{enum_name}) do - #{to_atom_fn_name}(#{inspect(value, base: :hex)}) - catch - _class, _reason -> #{value} - end\n - """, - [:append, :binary] - ) - end - - File.write( - "lib/openflow/enums.ex", - """ - def to_atom(_, :#{enum_name}) do - throw(:bad_enum) - end\n - """, - [:append, :binary] - ) - end - - for {enum_name, enum_def} <- @enums do - to_int_fn_name = :"#{enum_name}_to_int" - to_atom_fn_name = :"#{enum_name}_to_atom" - - for {key, value} <- enum_def do - File.write( - "lib/openflow/enums.ex", - " def #{to_int_fn_name}(#{inspect(key)}), do: #{inspect(value, base: :hex)}\n", - [ - :append, - :binary - ] - ) - end - - File.write("lib/openflow/enums.ex", " def #{to_int_fn_name}(_), do: throw(:bad_enum)\n", [ - :append, - :binary - ]) - - for {key, value} <- enum_def do - File.write( - "lib/openflow/enums.ex", - " def #{to_atom_fn_name}(#{inspect(value, base: :hex)}), do: #{inspect(key)}\n", - [ - :append, - :binary - ] - ) - end - - File.write("lib/openflow/enums.ex", " def #{to_atom_fn_name}(_), do: throw(:bad_enum)\n", [ - :append, - :binary - ]) - end - - for {enum_name, _enum_def} <- @enums do - File.write( - "lib/openflow/enums.ex", - """ - def int_to_flags(int, :#{enum_name}) do - Openflow.Utils.int_to_flags([], int, enum_of(:#{enum_name})) - end\n - """, - [:append, :binary] - ) - end - - for {enum_name, _enum_def} <- @enums do - File.write( - "lib/openflow/enums.ex", - """ - def flags_to_int(flags, :#{enum_name}) do - Openflow.Utils.flags_to_int(0, flags, enum_of(:#{enum_name})) - end\n - """, - [:append, :binary] - ) - end - - for {enum_name, enum_def} <- @enums do - File.write( - "lib/openflow/enums.ex", - " defp enum_of(:#{enum_name}), do: #{inspect(enum_def, pretty: true, limit: 100_000)}\n", - [:append, :binary] - ) - end - - File.write("lib/openflow/enums.ex", "end", [:append, :binary]) - end -end diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex index 430fffa..3fd02f4 100644 --- a/lib/openflow/enums.ex +++ b/lib/openflow/enums.ex @@ -325,10 +325,10 @@ defmodule Openflow.Enums do _class, _reason -> Openflow.Multipart.Table.Request end - def to_int(Openflow.Multipart.PortStats.Request, :multipart_request_codec) do - multipart_request_codec_to_int(Openflow.Multipart.PortStats.Request) + def to_int(Openflow.Multipart.Port.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Port.Request) catch - _class, _reason -> Openflow.Multipart.PortStats.Request + _class, _reason -> Openflow.Multipart.Port.Request end def to_int(Openflow.Multipart.Queue.Request, :multipart_request_codec) do @@ -419,10 +419,10 @@ defmodule Openflow.Enums do _class, _reason -> Openflow.Multipart.Table.Reply end - def to_int(Openflow.Multipart.PortStats.Reply, :multipart_reply_codec) do - multipart_reply_codec_to_int(Openflow.Multipart.PortStats.Reply) + def to_int(Openflow.Multipart.Port.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Port.Reply) catch - _class, _reason -> Openflow.Multipart.PortStats.Reply + _class, _reason -> Openflow.Multipart.Port.Reply end def to_int(Openflow.Multipart.Queue.Reply, :multipart_reply_codec) do @@ -727,6 +727,66 @@ defmodule Openflow.Enums do _class, _reason -> :multipart_buffer_overflow end + def to_int(:multipart_request_timeout, :bad_request) do + bad_request_to_int(:multipart_request_timeout) + catch + _class, _reason -> :multipart_request_timeout + end + + def to_int(:multipart_reply_timeout, :bad_request) do + bad_request_to_int(:multipart_reply_timeout) + catch + _class, _reason -> :multipart_reply_timeout + end + + def to_int(:nxm_invalid, :bad_request) do + bad_request_to_int(:nxm_invalid) + catch + _class, _reason -> :nxm_invalid + end + + def to_int(:nxm_bad_type, :bad_request) do + bad_request_to_int(:nxm_bad_type) + catch + _class, _reason -> :nxm_bad_type + end + + def to_int(:must_be_zero, :bad_request) do + bad_request_to_int(:must_be_zero) + catch + _class, _reason -> :must_be_zero + end + + def to_int(:bad_reason, :bad_request) do + bad_request_to_int(:bad_reason) + catch + _class, _reason -> :bad_reason + end + + def to_int(:flow_monitor_bad_event, :bad_request) do + bad_request_to_int(:flow_monitor_bad_event) + catch + _class, _reason -> :flow_monitor_bad_event + end + + def to_int(:undecodable_error, :bad_request) do + bad_request_to_int(:undecodable_error) + catch + _class, _reason -> :undecodable_error + end + + def to_int(:resume_not_supported, :bad_request) do + bad_request_to_int(:resume_not_supported) + catch + _class, _reason -> :resume_not_supported + end + + def to_int(:resume_stale, :bad_request) do + bad_request_to_int(:resume_stale) + catch + _class, _reason -> :resume_stale + end + def to_int(_int, :bad_request) do throw(:bad_enum) end @@ -827,6 +887,24 @@ defmodule Openflow.Enums do _class, _reason -> :bad_set_argument end + def to_int(:must_be_zero, :bad_action) do + bad_action_to_int(:must_be_zero) + catch + _class, _reason -> :must_be_zero + end + + def to_int(:conntrack_datapath_support, :bad_action) do + bad_action_to_int(:conntrack_datapath_support) + catch + _class, _reason -> :conntrack_datapath_support + end + + def to_int(:bad_conjunction, :bad_action) do + bad_action_to_int(:bad_conjunction) + catch + _class, _reason -> :bad_conjunction + end + def to_int(_int, :bad_action) do throw(:bad_enum) end @@ -885,6 +963,12 @@ defmodule Openflow.Enums do _class, _reason -> :eperm end + def to_int(:dup_inst, :bad_instruction) do + bad_instruction_to_int(:dup_inst) + catch + _class, _reason -> :dup_inst + end + def to_int(_int, :bad_instruction) do throw(:bad_enum) end @@ -961,6 +1045,12 @@ defmodule Openflow.Enums do _class, _reason -> :eperm end + def to_int(:conntrack_datapath_support, :bad_match) do + bad_match_to_int(:conntrack_datapath_support) + catch + _class, _reason -> :conntrack_datapath_support + end + def to_int(_int, :bad_match) do throw(:bad_enum) end @@ -1107,6 +1197,18 @@ defmodule Openflow.Enums do _class, _reason -> :eperm end + def to_int(:unknown_bucket, :group_mod_failed) do + group_mod_failed_to_int(:unknown_bucket) + catch + _class, _reason -> :unknown_bucket + end + + def to_int(:bucket_exists, :group_mod_failed) do + group_mod_failed_to_int(:bucket_exists) + catch + _class, _reason -> :bucket_exists + end + def to_int(_int, :group_mod_failed) do throw(:bad_enum) end @@ -1401,16 +1503,16 @@ defmodule Openflow.Enums do throw(:bad_enum) end - def to_int(:drop, :config_flags) do - config_flags_to_int(:drop) + def to_int(:fragment_drop, :config_flags) do + config_flags_to_int(:fragment_drop) catch - _class, _reason -> :drop + _class, _reason -> :fragment_drop end - def to_int(:reasm, :config_flags) do - config_flags_to_int(:reasm) + def to_int(:fragment_reassemble, :config_flags) do + config_flags_to_int(:fragment_reassemble) catch - _class, _reason -> :reasm + _class, _reason -> :fragment_reassemble end def to_int(_int, :config_flags) do @@ -3495,6 +3597,46 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_int(:no_match, :packet_in_reason_mask) do + packet_in_reason_mask_to_int(:no_match) + catch + _class, _reason -> :no_match + end + + def to_int(:action, :packet_in_reason_mask) do + packet_in_reason_mask_to_int(:action) + catch + _class, _reason -> :action + end + + def to_int(:invalid_ttl, :packet_in_reason_mask) do + packet_in_reason_mask_to_int(:invalid_ttl) + catch + _class, _reason -> :invalid_ttl + end + + def to_int(:action_set, :packet_in_reason_mask) do + packet_in_reason_mask_to_int(:action_set) + catch + _class, _reason -> :action_set + end + + def to_int(:group, :packet_in_reason_mask) do + packet_in_reason_mask_to_int(:group) + catch + _class, _reason -> :group + end + + def to_int(:packet_out, :packet_in_reason_mask) do + packet_in_reason_mask_to_int(:packet_out) + catch + _class, _reason -> :packet_out + end + + def to_int(_int, :packet_in_reason_mask) do + throw(:bad_enum) + end + def to_int(:add, :flow_mod_command) do flow_mod_command_to_int(:add) catch @@ -3603,6 +3745,46 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_int(:idle_timeout, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_int(:idle_timeout) + catch + _class, _reason -> :idle_timeout + end + + def to_int(:hard_timeout, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_int(:hard_timeout) + catch + _class, _reason -> :hard_timeout + end + + def to_int(:delete, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(:group_delete, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_int(:group_delete) + catch + _class, _reason -> :group_delete + end + + def to_int(:meter_delete, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_int(:meter_delete) + catch + _class, _reason -> :meter_delete + end + + def to_int(:eviction, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_int(:eviction) + catch + _class, _reason -> :eviction + end + + def to_int(_int, :flow_removed_reason_mask) do + throw(:bad_enum) + end + def to_int(:add, :port_reason) do port_reason_to_int(:add) catch @@ -3625,6 +3807,28 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_int(:add, :port_reason_mask) do + port_reason_mask_to_int(:add) + catch + _class, _reason -> :add + end + + def to_int(:delete, :port_reason_mask) do + port_reason_mask_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(:modify, :port_reason_mask) do + port_reason_mask_to_int(:modify) + catch + _class, _reason -> :modify + end + + def to_int(_int, :port_reason_mask) do + throw(:bad_enum) + end + def to_int(:add, :group_mod_command) do group_mod_command_to_int(:add) catch @@ -3675,6 +3879,34 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_int(:all, :group_type_flags) do + group_type_flags_to_int(:all) + catch + _class, _reason -> :all + end + + def to_int(:select, :group_type_flags) do + group_type_flags_to_int(:select) + catch + _class, _reason -> :select + end + + def to_int(:indirect, :group_type_flags) do + group_type_flags_to_int(:indirect) + catch + _class, _reason -> :indirect + end + + def to_int(:fast_failover, :group_type_flags) do + group_type_flags_to_int(:fast_failover) + catch + _class, _reason -> :fast_failover + end + + def to_int(_int, :group_type_flags) do + throw(:bad_enum) + end + def to_int(:max, :group_id) do group_id_to_int(:max) catch @@ -4021,6 +4253,136 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_int(Openflow.Action.Output, :action_flags) do + action_flags_to_int(Openflow.Action.Output) + catch + _class, _reason -> Openflow.Action.Output + end + + def to_int(Openflow.Action.CopyTtlOut, :action_flags) do + action_flags_to_int(Openflow.Action.CopyTtlOut) + catch + _class, _reason -> Openflow.Action.CopyTtlOut + end + + def to_int(Openflow.Action.CopyTtlIn, :action_flags) do + action_flags_to_int(Openflow.Action.CopyTtlIn) + catch + _class, _reason -> Openflow.Action.CopyTtlIn + end + + def to_int(Openflow.Action.SetMplsTtl, :action_flags) do + action_flags_to_int(Openflow.Action.SetMplsTtl) + catch + _class, _reason -> Openflow.Action.SetMplsTtl + end + + def to_int(Openflow.Action.DecMplsTtl, :action_flags) do + action_flags_to_int(Openflow.Action.DecMplsTtl) + catch + _class, _reason -> Openflow.Action.DecMplsTtl + end + + def to_int(Openflow.Action.PushVlan, :action_flags) do + action_flags_to_int(Openflow.Action.PushVlan) + catch + _class, _reason -> Openflow.Action.PushVlan + end + + def to_int(Openflow.Action.PopVlan, :action_flags) do + action_flags_to_int(Openflow.Action.PopVlan) + catch + _class, _reason -> Openflow.Action.PopVlan + end + + def to_int(Openflow.Action.PushMpls, :action_flags) do + action_flags_to_int(Openflow.Action.PushMpls) + catch + _class, _reason -> Openflow.Action.PushMpls + end + + def to_int(Openflow.Action.PopMpls, :action_flags) do + action_flags_to_int(Openflow.Action.PopMpls) + catch + _class, _reason -> Openflow.Action.PopMpls + end + + def to_int(Openflow.Action.SetQueue, :action_flags) do + action_flags_to_int(Openflow.Action.SetQueue) + catch + _class, _reason -> Openflow.Action.SetQueue + end + + def to_int(Openflow.Action.Group, :action_flags) do + action_flags_to_int(Openflow.Action.Group) + catch + _class, _reason -> Openflow.Action.Group + end + + def to_int(Openflow.Action.SetNwTtl, :action_flags) do + action_flags_to_int(Openflow.Action.SetNwTtl) + catch + _class, _reason -> Openflow.Action.SetNwTtl + end + + def to_int(Openflow.Action.DecNwTtl, :action_flags) do + action_flags_to_int(Openflow.Action.DecNwTtl) + catch + _class, _reason -> Openflow.Action.DecNwTtl + end + + def to_int(Openflow.Action.SetField, :action_flags) do + action_flags_to_int(Openflow.Action.SetField) + catch + _class, _reason -> Openflow.Action.SetField + end + + def to_int(Openflow.Action.PushPbb, :action_flags) do + action_flags_to_int(Openflow.Action.PushPbb) + catch + _class, _reason -> Openflow.Action.PushPbb + end + + def to_int(Openflow.Action.PopPbb, :action_flags) do + action_flags_to_int(Openflow.Action.PopPbb) + catch + _class, _reason -> Openflow.Action.PopPbb + end + + def to_int(Openflow.Action.Encap, :action_flags) do + action_flags_to_int(Openflow.Action.Encap) + catch + _class, _reason -> Openflow.Action.Encap + end + + def to_int(Openflow.Action.Decap, :action_flags) do + action_flags_to_int(Openflow.Action.Decap) + catch + _class, _reason -> Openflow.Action.Decap + end + + def to_int(Openflow.Action.SetSequence, :action_flags) do + action_flags_to_int(Openflow.Action.SetSequence) + catch + _class, _reason -> Openflow.Action.SetSequence + end + + def to_int(Openflow.Action.ValidateSequence, :action_flags) do + action_flags_to_int(Openflow.Action.ValidateSequence) + catch + _class, _reason -> Openflow.Action.ValidateSequence + end + + def to_int(Openflow.Action.Experimenter, :action_flags) do + action_flags_to_int(Openflow.Action.Experimenter) + catch + _class, _reason -> Openflow.Action.Experimenter + end + + def to_int(_int, :action_flags) do + throw(:bad_enum) + end + def to_int(:nicira_ext_action, :action_vendor) do action_vendor_to_int(:nicira_ext_action) catch @@ -4059,18 +4421,6 @@ defmodule Openflow.Enums do _class, _reason -> Openflow.Action.NxSetTunnel end - def to_int(Openflow.Action.NxSetQueue, :nicira_ext_action) do - nicira_ext_action_to_int(Openflow.Action.NxSetQueue) - catch - _class, _reason -> Openflow.Action.NxSetQueue - end - - def to_int(Openflow.Action.NxPopQueue, :nicira_ext_action) do - nicira_ext_action_to_int(Openflow.Action.NxPopQueue) - catch - _class, _reason -> Openflow.Action.NxPopQueue - end - def to_int(Openflow.Action.NxRegMove, :nicira_ext_action) do nicira_ext_action_to_int(Openflow.Action.NxRegMove) catch @@ -4167,30 +4517,6 @@ defmodule Openflow.Enums do _class, _reason -> Openflow.Action.NxWriteMetadata end - def to_int(Openflow.Action.NxPushMpls, :nicira_ext_action) do - nicira_ext_action_to_int(Openflow.Action.NxPushMpls) - catch - _class, _reason -> Openflow.Action.NxPushMpls - end - - def to_int(Openflow.Action.NxPopMpls, :nicira_ext_action) do - nicira_ext_action_to_int(Openflow.Action.NxPopMpls) - catch - _class, _reason -> Openflow.Action.NxPopMpls - end - - def to_int(Openflow.Action.NxSetMplsTtl, :nicira_ext_action) do - nicira_ext_action_to_int(Openflow.Action.NxSetMplsTtl) - catch - _class, _reason -> Openflow.Action.NxSetMplsTtl - end - - def to_int(Openflow.Action.NxDecMplsTtl, :nicira_ext_action) do - nicira_ext_action_to_int(Openflow.Action.NxDecMplsTtl) - catch - _class, _reason -> Openflow.Action.NxDecMplsTtl - end - def to_int(Openflow.Action.NxStackPush, :nicira_ext_action) do nicira_ext_action_to_int(Openflow.Action.NxStackPush) catch @@ -4209,18 +4535,6 @@ defmodule Openflow.Enums do _class, _reason -> Openflow.Action.NxSample end - def to_int(Openflow.Action.NxSetMplsLabel, :nicira_ext_action) do - nicira_ext_action_to_int(Openflow.Action.NxSetMplsLabel) - catch - _class, _reason -> Openflow.Action.NxSetMplsLabel - end - - def to_int(Openflow.Action.NxSetMplsTc, :nicira_ext_action) do - nicira_ext_action_to_int(Openflow.Action.NxSetMplsTc) - catch - _class, _reason -> Openflow.Action.NxSetMplsTc - end - def to_int(Openflow.Action.NxOutputReg2, :nicira_ext_action) do nicira_ext_action_to_int(Openflow.Action.NxOutputReg2) catch @@ -5035,6 +5349,74 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_int(:open_request, :bundle_ctrl_type) do + bundle_ctrl_type_to_int(:open_request) + catch + _class, _reason -> :open_request + end + + def to_int(:open_reply, :bundle_ctrl_type) do + bundle_ctrl_type_to_int(:open_reply) + catch + _class, _reason -> :open_reply + end + + def to_int(:close_request, :bundle_ctrl_type) do + bundle_ctrl_type_to_int(:close_request) + catch + _class, _reason -> :close_request + end + + def to_int(:close_reply, :bundle_ctrl_type) do + bundle_ctrl_type_to_int(:close_reply) + catch + _class, _reason -> :close_reply + end + + def to_int(:commit_request, :bundle_ctrl_type) do + bundle_ctrl_type_to_int(:commit_request) + catch + _class, _reason -> :commit_request + end + + def to_int(:commit_reply, :bundle_ctrl_type) do + bundle_ctrl_type_to_int(:commit_reply) + catch + _class, _reason -> :commit_reply + end + + def to_int(:discard_request, :bundle_ctrl_type) do + bundle_ctrl_type_to_int(:discard_request) + catch + _class, _reason -> :discard_request + end + + def to_int(:discard_reply, :bundle_ctrl_type) do + bundle_ctrl_type_to_int(:discard_reply) + catch + _class, _reason -> :discard_reply + end + + def to_int(_int, :bundle_ctrl_type) do + throw(:bad_enum) + end + + def to_int(:atomic, :bundle_flags) do + bundle_flags_to_int(:atomic) + catch + _class, _reason -> :atomic + end + + def to_int(:ordered, :bundle_flags) do + bundle_flags_to_int(:ordered) + catch + _class, _reason -> :ordered + end + + def to_int(_int, :bundle_flags) do + throw(:bad_enum) + end + def to_atom(0x0, :openflow_codec) do openflow_codec_to_atom(0x0) catch @@ -5761,6 +6143,66 @@ defmodule Openflow.Enums do _class, _reason -> 13 end + def to_atom(0xE, :bad_request) do + bad_request_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(0xF, :bad_request) do + bad_request_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0x100, :bad_request) do + bad_request_to_atom(0x100) + catch + _class, _reason -> 256 + end + + def to_atom(0x101, :bad_request) do + bad_request_to_atom(0x101) + catch + _class, _reason -> 257 + end + + def to_atom(0x203, :bad_request) do + bad_request_to_atom(0x203) + catch + _class, _reason -> 515 + end + + def to_atom(0x204, :bad_request) do + bad_request_to_atom(0x204) + catch + _class, _reason -> 516 + end + + def to_atom(0x208, :bad_request) do + bad_request_to_atom(0x208) + catch + _class, _reason -> 520 + end + + def to_atom(0x209, :bad_request) do + bad_request_to_atom(0x209) + catch + _class, _reason -> 521 + end + + def to_atom(0x215, :bad_request) do + bad_request_to_atom(0x215) + catch + _class, _reason -> 533 + end + + def to_atom(0x216, :bad_request) do + bad_request_to_atom(0x216) + catch + _class, _reason -> 534 + end + def to_atom(_, :bad_request) do throw(:bad_enum) end @@ -5861,6 +6303,24 @@ defmodule Openflow.Enums do _class, _reason -> 15 end + def to_atom(0x100, :bad_action) do + bad_action_to_atom(0x100) + catch + _class, _reason -> 256 + end + + def to_atom(0x109, :bad_action) do + bad_action_to_atom(0x109) + catch + _class, _reason -> 265 + end + + def to_atom(0x20E, :bad_action) do + bad_action_to_atom(0x20E) + catch + _class, _reason -> 526 + end + def to_atom(_, :bad_action) do throw(:bad_enum) end @@ -5919,6 +6379,12 @@ defmodule Openflow.Enums do _class, _reason -> 8 end + def to_atom(0x100, :bad_instruction) do + bad_instruction_to_atom(0x100) + catch + _class, _reason -> 256 + end + def to_atom(_, :bad_instruction) do throw(:bad_enum) end @@ -5995,6 +6461,12 @@ defmodule Openflow.Enums do _class, _reason -> 11 end + def to_atom(0x108, :bad_match) do + bad_match_to_atom(0x108) + catch + _class, _reason -> 264 + end + def to_atom(_, :bad_match) do throw(:bad_enum) end @@ -6141,6 +6613,18 @@ defmodule Openflow.Enums do _class, _reason -> 14 end + def to_atom(0xF, :group_mod_failed) do + group_mod_failed_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0x10, :group_mod_failed) do + group_mod_failed_to_atom(0x10) + catch + _class, _reason -> 16 + end + def to_atom(_, :group_mod_failed) do throw(:bad_enum) end @@ -8529,6 +9013,46 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_atom(0x1, :packet_in_reason_mask) do + packet_in_reason_mask_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :packet_in_reason_mask) do + packet_in_reason_mask_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :packet_in_reason_mask) do + packet_in_reason_mask_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :packet_in_reason_mask) do + packet_in_reason_mask_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :packet_in_reason_mask) do + packet_in_reason_mask_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x20, :packet_in_reason_mask) do + packet_in_reason_mask_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(_, :packet_in_reason_mask) do + throw(:bad_enum) + end + def to_atom(0x0, :flow_mod_command) do flow_mod_command_to_atom(0x0) catch @@ -8637,6 +9161,46 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_atom(0x1, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x20, :flow_removed_reason_mask) do + flow_removed_reason_mask_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(_, :flow_removed_reason_mask) do + throw(:bad_enum) + end + def to_atom(0x0, :port_reason) do port_reason_to_atom(0x0) catch @@ -8659,6 +9223,28 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_atom(0x1, :port_reason_mask) do + port_reason_mask_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :port_reason_mask) do + port_reason_mask_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :port_reason_mask) do + port_reason_mask_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(_, :port_reason_mask) do + throw(:bad_enum) + end + def to_atom(0x0, :group_mod_command) do group_mod_command_to_atom(0x0) catch @@ -8709,6 +9295,34 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_atom(0x1, :group_type_flags) do + group_type_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :group_type_flags) do + group_type_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :group_type_flags) do + group_type_flags_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :group_type_flags) do + group_type_flags_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(_, :group_type_flags) do + throw(:bad_enum) + end + def to_atom(0xFFFFFF00, :group_id) do group_id_to_atom(0xFFFFFF00) catch @@ -9055,6 +9669,136 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_atom(0x1, :action_flags) do + action_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x800, :action_flags) do + action_flags_to_atom(0x800) + catch + _class, _reason -> 2048 + end + + def to_atom(0x1000, :action_flags) do + action_flags_to_atom(0x1000) + catch + _class, _reason -> 4096 + end + + def to_atom(0x8000, :action_flags) do + action_flags_to_atom(0x8000) + catch + _class, _reason -> 32768 + end + + def to_atom(0x10000, :action_flags) do + action_flags_to_atom(0x10000) + catch + _class, _reason -> 65536 + end + + def to_atom(0x20000, :action_flags) do + action_flags_to_atom(0x20000) + catch + _class, _reason -> 131_072 + end + + def to_atom(0x40000, :action_flags) do + action_flags_to_atom(0x40000) + catch + _class, _reason -> 262_144 + end + + def to_atom(0x80000, :action_flags) do + action_flags_to_atom(0x80000) + catch + _class, _reason -> 524_288 + end + + def to_atom(0x100000, :action_flags) do + action_flags_to_atom(0x100000) + catch + _class, _reason -> 1_048_576 + end + + def to_atom(0x200000, :action_flags) do + action_flags_to_atom(0x200000) + catch + _class, _reason -> 2_097_152 + end + + def to_atom(0x400000, :action_flags) do + action_flags_to_atom(0x400000) + catch + _class, _reason -> 4_194_304 + end + + def to_atom(0x800000, :action_flags) do + action_flags_to_atom(0x800000) + catch + _class, _reason -> 8_388_608 + end + + def to_atom(0x1000000, :action_flags) do + action_flags_to_atom(0x1000000) + catch + _class, _reason -> 16_777_216 + end + + def to_atom(0x2000000, :action_flags) do + action_flags_to_atom(0x2000000) + catch + _class, _reason -> 33_554_432 + end + + def to_atom(0x4000000, :action_flags) do + action_flags_to_atom(0x4000000) + catch + _class, _reason -> 67_108_864 + end + + def to_atom(0x8000000, :action_flags) do + action_flags_to_atom(0x8000000) + catch + _class, _reason -> 134_217_728 + end + + def to_atom(0x10000000, :action_flags) do + action_flags_to_atom(0x10000000) + catch + _class, _reason -> 268_435_456 + end + + def to_atom(0x20000000, :action_flags) do + action_flags_to_atom(0x20000000) + catch + _class, _reason -> 536_870_912 + end + + def to_atom(0x40000000, :action_flags) do + action_flags_to_atom(0x40000000) + catch + _class, _reason -> 1_073_741_824 + end + + def to_atom(0x80000000, :action_flags) do + action_flags_to_atom(0x80000000) + catch + _class, _reason -> 2_147_483_648 + end + + def to_atom(0xFFFF, :action_flags) do + action_flags_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :action_flags) do + throw(:bad_enum) + end + def to_atom(0x2320, :action_vendor) do action_vendor_to_atom(0x2320) catch @@ -9093,18 +9837,6 @@ defmodule Openflow.Enums do _class, _reason -> 2 end - def to_atom(0x4, :nicira_ext_action) do - nicira_ext_action_to_atom(0x4) - catch - _class, _reason -> 4 - end - - def to_atom(0x5, :nicira_ext_action) do - nicira_ext_action_to_atom(0x5) - catch - _class, _reason -> 5 - end - def to_atom(0x6, :nicira_ext_action) do nicira_ext_action_to_atom(0x6) catch @@ -9201,30 +9933,6 @@ defmodule Openflow.Enums do _class, _reason -> 22 end - def to_atom(0x17, :nicira_ext_action) do - nicira_ext_action_to_atom(0x17) - catch - _class, _reason -> 23 - end - - def to_atom(0x18, :nicira_ext_action) do - nicira_ext_action_to_atom(0x18) - catch - _class, _reason -> 24 - end - - def to_atom(0x19, :nicira_ext_action) do - nicira_ext_action_to_atom(0x19) - catch - _class, _reason -> 25 - end - - def to_atom(0x1A, :nicira_ext_action) do - nicira_ext_action_to_atom(0x1A) - catch - _class, _reason -> 26 - end - def to_atom(0x1B, :nicira_ext_action) do nicira_ext_action_to_atom(0x1B) catch @@ -9243,18 +9951,6 @@ defmodule Openflow.Enums do _class, _reason -> 29 end - def to_atom(0x1E, :nicira_ext_action) do - nicira_ext_action_to_atom(0x1E) - catch - _class, _reason -> 30 - end - - def to_atom(0x1F, :nicira_ext_action) do - nicira_ext_action_to_atom(0x1F) - catch - _class, _reason -> 31 - end - def to_atom(0x20, :nicira_ext_action) do nicira_ext_action_to_atom(0x20) catch @@ -10069,6 +10765,74 @@ defmodule Openflow.Enums do throw(:bad_enum) end + def to_atom(0x0, :bundle_ctrl_type) do + bundle_ctrl_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :bundle_ctrl_type) do + bundle_ctrl_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :bundle_ctrl_type) do + bundle_ctrl_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :bundle_ctrl_type) do + bundle_ctrl_type_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :bundle_ctrl_type) do + bundle_ctrl_type_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :bundle_ctrl_type) do + bundle_ctrl_type_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :bundle_ctrl_type) do + bundle_ctrl_type_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :bundle_ctrl_type) do + bundle_ctrl_type_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(_, :bundle_ctrl_type) do + throw(:bad_enum) + end + + def to_atom(0x1, :bundle_flags) do + bundle_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :bundle_flags) do + bundle_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :bundle_flags) do + throw(:bad_enum) + end + def openflow_codec_to_int(Openflow.Hello), do: 0x0 def openflow_codec_to_int(Openflow.ErrorMsg), do: 0x1 def openflow_codec_to_int(Openflow.Echo.Request), do: 0x2 @@ -10177,7 +10941,7 @@ defmodule Openflow.Enums do def multipart_request_codec_to_int(Openflow.Multipart.Flow.Request), do: 0x1 def multipart_request_codec_to_int(Openflow.Multipart.Aggregate.Request), do: 0x2 def multipart_request_codec_to_int(Openflow.Multipart.Table.Request), do: 0x3 - def multipart_request_codec_to_int(Openflow.Multipart.PortStats.Request), do: 0x4 + def multipart_request_codec_to_int(Openflow.Multipart.Port.Request), do: 0x4 def multipart_request_codec_to_int(Openflow.Multipart.Queue.Request), do: 0x5 def multipart_request_codec_to_int(Openflow.Multipart.Group.Request), do: 0x6 def multipart_request_codec_to_int(Openflow.Multipart.GroupDesc.Request), do: 0x7 @@ -10193,7 +10957,7 @@ defmodule Openflow.Enums do def multipart_request_codec_to_atom(0x1), do: Openflow.Multipart.Flow.Request def multipart_request_codec_to_atom(0x2), do: Openflow.Multipart.Aggregate.Request def multipart_request_codec_to_atom(0x3), do: Openflow.Multipart.Table.Request - def multipart_request_codec_to_atom(0x4), do: Openflow.Multipart.PortStats.Request + def multipart_request_codec_to_atom(0x4), do: Openflow.Multipart.Port.Request def multipart_request_codec_to_atom(0x5), do: Openflow.Multipart.Queue.Request def multipart_request_codec_to_atom(0x6), do: Openflow.Multipart.Group.Request def multipart_request_codec_to_atom(0x7), do: Openflow.Multipart.GroupDesc.Request @@ -10209,7 +10973,7 @@ defmodule Openflow.Enums do def multipart_reply_codec_to_int(Openflow.Multipart.Flow.Reply), do: 0x1 def multipart_reply_codec_to_int(Openflow.Multipart.Aggregate.Reply), do: 0x2 def multipart_reply_codec_to_int(Openflow.Multipart.Table.Reply), do: 0x3 - def multipart_reply_codec_to_int(Openflow.Multipart.PortStats.Reply), do: 0x4 + def multipart_reply_codec_to_int(Openflow.Multipart.Port.Reply), do: 0x4 def multipart_reply_codec_to_int(Openflow.Multipart.Queue.Reply), do: 0x5 def multipart_reply_codec_to_int(Openflow.Multipart.Group.Reply), do: 0x6 def multipart_reply_codec_to_int(Openflow.Multipart.GroupDesc.Reply), do: 0x7 @@ -10225,7 +10989,7 @@ defmodule Openflow.Enums do def multipart_reply_codec_to_atom(0x1), do: Openflow.Multipart.Flow.Reply def multipart_reply_codec_to_atom(0x2), do: Openflow.Multipart.Aggregate.Reply def multipart_reply_codec_to_atom(0x3), do: Openflow.Multipart.Table.Reply - def multipart_reply_codec_to_atom(0x4), do: Openflow.Multipart.PortStats.Reply + def multipart_reply_codec_to_atom(0x4), do: Openflow.Multipart.Port.Reply def multipart_reply_codec_to_atom(0x5), do: Openflow.Multipart.Queue.Reply def multipart_reply_codec_to_atom(0x6), do: Openflow.Multipart.Group.Reply def multipart_reply_codec_to_atom(0x7), do: Openflow.Multipart.GroupDesc.Reply @@ -10305,6 +11069,16 @@ defmodule Openflow.Enums do def bad_request_to_int(:bad_port), do: 0xB def bad_request_to_int(:bad_packet), do: 0xC def bad_request_to_int(:multipart_buffer_overflow), do: 0xD + def bad_request_to_int(:multipart_request_timeout), do: 0xE + def bad_request_to_int(:multipart_reply_timeout), do: 0xF + def bad_request_to_int(:nxm_invalid), do: 0x100 + def bad_request_to_int(:nxm_bad_type), do: 0x101 + def bad_request_to_int(:must_be_zero), do: 0x203 + def bad_request_to_int(:bad_reason), do: 0x204 + def bad_request_to_int(:flow_monitor_bad_event), do: 0x208 + def bad_request_to_int(:undecodable_error), do: 0x209 + def bad_request_to_int(:resume_not_supported), do: 0x215 + def bad_request_to_int(:resume_stale), do: 0x216 def bad_request_to_int(_), do: throw(:bad_enum) def bad_request_to_atom(0x0), do: :bad_version def bad_request_to_atom(0x1), do: :bad_type @@ -10320,6 +11094,16 @@ defmodule Openflow.Enums do def bad_request_to_atom(0xB), do: :bad_port def bad_request_to_atom(0xC), do: :bad_packet def bad_request_to_atom(0xD), do: :multipart_buffer_overflow + def bad_request_to_atom(0xE), do: :multipart_request_timeout + def bad_request_to_atom(0xF), do: :multipart_reply_timeout + def bad_request_to_atom(0x100), do: :nxm_invalid + def bad_request_to_atom(0x101), do: :nxm_bad_type + def bad_request_to_atom(0x203), do: :must_be_zero + def bad_request_to_atom(0x204), do: :bad_reason + def bad_request_to_atom(0x208), do: :flow_monitor_bad_event + def bad_request_to_atom(0x209), do: :undecodable_error + def bad_request_to_atom(0x215), do: :resume_not_supported + def bad_request_to_atom(0x216), do: :resume_stale def bad_request_to_atom(_), do: throw(:bad_enum) def bad_action_to_int(:bad_type), do: 0x0 def bad_action_to_int(:bad_len), do: 0x1 @@ -10337,6 +11121,9 @@ defmodule Openflow.Enums do def bad_action_to_int(:bad_set_type), do: 0xD def bad_action_to_int(:bad_set_len), do: 0xE def bad_action_to_int(:bad_set_argument), do: 0xF + def bad_action_to_int(:must_be_zero), do: 0x100 + def bad_action_to_int(:conntrack_datapath_support), do: 0x109 + def bad_action_to_int(:bad_conjunction), do: 0x20E def bad_action_to_int(_), do: throw(:bad_enum) def bad_action_to_atom(0x0), do: :bad_type def bad_action_to_atom(0x1), do: :bad_len @@ -10354,6 +11141,9 @@ defmodule Openflow.Enums do def bad_action_to_atom(0xD), do: :bad_set_type def bad_action_to_atom(0xE), do: :bad_set_len def bad_action_to_atom(0xF), do: :bad_set_argument + def bad_action_to_atom(0x100), do: :must_be_zero + def bad_action_to_atom(0x109), do: :conntrack_datapath_support + def bad_action_to_atom(0x20E), do: :bad_conjunction def bad_action_to_atom(_), do: throw(:bad_enum) def bad_instruction_to_int(:unknown_instruction), do: 0x0 def bad_instruction_to_int(:unsupported_instruction), do: 0x1 @@ -10364,6 +11154,7 @@ defmodule Openflow.Enums do def bad_instruction_to_int(:bad_exp_type), do: 0x6 def bad_instruction_to_int(:bad_len), do: 0x7 def bad_instruction_to_int(:eperm), do: 0x8 + def bad_instruction_to_int(:dup_inst), do: 0x100 def bad_instruction_to_int(_), do: throw(:bad_enum) def bad_instruction_to_atom(0x0), do: :unknown_instruction def bad_instruction_to_atom(0x1), do: :unsupported_instruction @@ -10374,6 +11165,7 @@ defmodule Openflow.Enums do def bad_instruction_to_atom(0x6), do: :bad_exp_type def bad_instruction_to_atom(0x7), do: :bad_len def bad_instruction_to_atom(0x8), do: :eperm + def bad_instruction_to_atom(0x100), do: :dup_inst def bad_instruction_to_atom(_), do: throw(:bad_enum) def bad_match_to_int(:bad_type), do: 0x0 def bad_match_to_int(:bad_len), do: 0x1 @@ -10387,6 +11179,7 @@ defmodule Openflow.Enums do def bad_match_to_int(:bad_prereq), do: 0x9 def bad_match_to_int(:dup_field), do: 0xA def bad_match_to_int(:eperm), do: 0xB + def bad_match_to_int(:conntrack_datapath_support), do: 0x108 def bad_match_to_int(_), do: throw(:bad_enum) def bad_match_to_atom(0x0), do: :bad_type def bad_match_to_atom(0x1), do: :bad_len @@ -10400,6 +11193,7 @@ defmodule Openflow.Enums do def bad_match_to_atom(0x9), do: :bad_prereq def bad_match_to_atom(0xA), do: :dup_field def bad_match_to_atom(0xB), do: :eperm + def bad_match_to_atom(0x108), do: :conntrack_datapath_support def bad_match_to_atom(_), do: throw(:bad_enum) def flow_mod_failed_to_int(:unknown), do: 0x0 def flow_mod_failed_to_int(:table_full), do: 0x1 @@ -10434,6 +11228,8 @@ defmodule Openflow.Enums do def group_mod_failed_to_int(:bad_bucket), do: 0xC def group_mod_failed_to_int(:bad_watch), do: 0xD def group_mod_failed_to_int(:eperm), do: 0xE + def group_mod_failed_to_int(:unknown_bucket), do: 0xF + def group_mod_failed_to_int(:bucket_exists), do: 0x10 def group_mod_failed_to_int(_), do: throw(:bad_enum) def group_mod_failed_to_atom(0x0), do: :group_exists def group_mod_failed_to_atom(0x1), do: :invalid_group @@ -10450,6 +11246,8 @@ defmodule Openflow.Enums do def group_mod_failed_to_atom(0xC), do: :bad_bucket def group_mod_failed_to_atom(0xD), do: :bad_watch def group_mod_failed_to_atom(0xE), do: :eperm + def group_mod_failed_to_atom(0xF), do: :unknown_bucket + def group_mod_failed_to_atom(0x10), do: :bucket_exists def group_mod_failed_to_atom(_), do: throw(:bad_enum) def port_mod_failed_to_int(:bad_port), do: 0x0 def port_mod_failed_to_int(:bad_hw_addr), do: 0x1 @@ -10553,11 +11351,11 @@ defmodule Openflow.Enums do def switch_capabilities_to_atom(0x80), do: :arp_match_ip def switch_capabilities_to_atom(0x100), do: :port_blocked def switch_capabilities_to_atom(_), do: throw(:bad_enum) - def config_flags_to_int(:drop), do: 0x1 - def config_flags_to_int(:reasm), do: 0x2 + def config_flags_to_int(:fragment_drop), do: 0x1 + def config_flags_to_int(:fragment_reassemble), do: 0x2 def config_flags_to_int(_), do: throw(:bad_enum) - def config_flags_to_atom(0x1), do: :drop - def config_flags_to_atom(0x2), do: :reasm + def config_flags_to_atom(0x1), do: :fragment_drop + def config_flags_to_atom(0x2), do: :fragment_reassemble def config_flags_to_atom(_), do: throw(:bad_enum) def controller_max_len_to_int(:max), do: 0xFFE5 def controller_max_len_to_int(:no_buffer), do: 0xFFFF @@ -11267,6 +12065,20 @@ defmodule Openflow.Enums do def packet_in_reason_to_atom(0x4), do: :group def packet_in_reason_to_atom(0x5), do: :packet_out def packet_in_reason_to_atom(_), do: throw(:bad_enum) + def packet_in_reason_mask_to_int(:no_match), do: 0x1 + def packet_in_reason_mask_to_int(:action), do: 0x2 + def packet_in_reason_mask_to_int(:invalid_ttl), do: 0x4 + def packet_in_reason_mask_to_int(:action_set), do: 0x8 + def packet_in_reason_mask_to_int(:group), do: 0x10 + def packet_in_reason_mask_to_int(:packet_out), do: 0x20 + def packet_in_reason_mask_to_int(_), do: throw(:bad_enum) + def packet_in_reason_mask_to_atom(0x1), do: :no_match + def packet_in_reason_mask_to_atom(0x2), do: :action + def packet_in_reason_mask_to_atom(0x4), do: :invalid_ttl + def packet_in_reason_mask_to_atom(0x8), do: :action_set + def packet_in_reason_mask_to_atom(0x10), do: :group + def packet_in_reason_mask_to_atom(0x20), do: :packet_out + def packet_in_reason_mask_to_atom(_), do: throw(:bad_enum) def flow_mod_command_to_int(:add), do: 0x0 def flow_mod_command_to_int(:modify), do: 0x1 def flow_mod_command_to_int(:modify_strict), do: 0x2 @@ -11305,6 +12117,20 @@ defmodule Openflow.Enums do def flow_removed_reason_to_atom(0x4), do: :meter_delete def flow_removed_reason_to_atom(0x5), do: :eviction def flow_removed_reason_to_atom(_), do: throw(:bad_enum) + def flow_removed_reason_mask_to_int(:idle_timeout), do: 0x1 + def flow_removed_reason_mask_to_int(:hard_timeout), do: 0x2 + def flow_removed_reason_mask_to_int(:delete), do: 0x4 + def flow_removed_reason_mask_to_int(:group_delete), do: 0x8 + def flow_removed_reason_mask_to_int(:meter_delete), do: 0x10 + def flow_removed_reason_mask_to_int(:eviction), do: 0x20 + def flow_removed_reason_mask_to_int(_), do: throw(:bad_enum) + def flow_removed_reason_mask_to_atom(0x1), do: :idle_timeout + def flow_removed_reason_mask_to_atom(0x2), do: :hard_timeout + def flow_removed_reason_mask_to_atom(0x4), do: :delete + def flow_removed_reason_mask_to_atom(0x8), do: :group_delete + def flow_removed_reason_mask_to_atom(0x10), do: :meter_delete + def flow_removed_reason_mask_to_atom(0x20), do: :eviction + def flow_removed_reason_mask_to_atom(_), do: throw(:bad_enum) def port_reason_to_int(:add), do: 0x0 def port_reason_to_int(:delete), do: 0x1 def port_reason_to_int(:modify), do: 0x2 @@ -11313,6 +12139,14 @@ defmodule Openflow.Enums do def port_reason_to_atom(0x1), do: :delete def port_reason_to_atom(0x2), do: :modify def port_reason_to_atom(_), do: throw(:bad_enum) + def port_reason_mask_to_int(:add), do: 0x1 + def port_reason_mask_to_int(:delete), do: 0x2 + def port_reason_mask_to_int(:modify), do: 0x4 + def port_reason_mask_to_int(_), do: throw(:bad_enum) + def port_reason_mask_to_atom(0x1), do: :add + def port_reason_mask_to_atom(0x2), do: :delete + def port_reason_mask_to_atom(0x4), do: :modify + def port_reason_mask_to_atom(_), do: throw(:bad_enum) def group_mod_command_to_int(:add), do: 0x0 def group_mod_command_to_int(:modify), do: 0x1 def group_mod_command_to_int(:delete), do: 0x2 @@ -11331,6 +12165,16 @@ defmodule Openflow.Enums do def group_type_to_atom(0x2), do: :indirect def group_type_to_atom(0x3), do: :fast_failover def group_type_to_atom(_), do: throw(:bad_enum) + def group_type_flags_to_int(:all), do: 0x1 + def group_type_flags_to_int(:select), do: 0x2 + def group_type_flags_to_int(:indirect), do: 0x4 + def group_type_flags_to_int(:fast_failover), do: 0x8 + def group_type_flags_to_int(_), do: throw(:bad_enum) + def group_type_flags_to_atom(0x1), do: :all + def group_type_flags_to_atom(0x2), do: :select + def group_type_flags_to_atom(0x4), do: :indirect + def group_type_flags_to_atom(0x8), do: :fast_failover + def group_type_flags_to_atom(_), do: throw(:bad_enum) def group_id_to_int(:max), do: 0xFFFFFF00 def group_id_to_int(:all), do: 0xFFFFFFFC def group_id_to_int(:any), do: 0xFFFFFFFF @@ -11453,6 +12297,50 @@ defmodule Openflow.Enums do def action_type_to_atom(0x1F), do: Openflow.Action.ValidateSequence def action_type_to_atom(0xFFFF), do: Openflow.Action.Experimenter def action_type_to_atom(_), do: throw(:bad_enum) + def action_flags_to_int(Openflow.Action.Output), do: 0x1 + def action_flags_to_int(Openflow.Action.CopyTtlOut), do: 0x800 + def action_flags_to_int(Openflow.Action.CopyTtlIn), do: 0x1000 + def action_flags_to_int(Openflow.Action.SetMplsTtl), do: 0x8000 + def action_flags_to_int(Openflow.Action.DecMplsTtl), do: 0x10000 + def action_flags_to_int(Openflow.Action.PushVlan), do: 0x20000 + def action_flags_to_int(Openflow.Action.PopVlan), do: 0x40000 + def action_flags_to_int(Openflow.Action.PushMpls), do: 0x80000 + def action_flags_to_int(Openflow.Action.PopMpls), do: 0x100000 + def action_flags_to_int(Openflow.Action.SetQueue), do: 0x200000 + def action_flags_to_int(Openflow.Action.Group), do: 0x400000 + def action_flags_to_int(Openflow.Action.SetNwTtl), do: 0x800000 + def action_flags_to_int(Openflow.Action.DecNwTtl), do: 0x1000000 + def action_flags_to_int(Openflow.Action.SetField), do: 0x2000000 + def action_flags_to_int(Openflow.Action.PushPbb), do: 0x4000000 + def action_flags_to_int(Openflow.Action.PopPbb), do: 0x8000000 + def action_flags_to_int(Openflow.Action.Encap), do: 0x10000000 + def action_flags_to_int(Openflow.Action.Decap), do: 0x20000000 + def action_flags_to_int(Openflow.Action.SetSequence), do: 0x40000000 + def action_flags_to_int(Openflow.Action.ValidateSequence), do: 0x80000000 + def action_flags_to_int(Openflow.Action.Experimenter), do: 0xFFFF + def action_flags_to_int(_), do: throw(:bad_enum) + def action_flags_to_atom(0x1), do: Openflow.Action.Output + def action_flags_to_atom(0x800), do: Openflow.Action.CopyTtlOut + def action_flags_to_atom(0x1000), do: Openflow.Action.CopyTtlIn + def action_flags_to_atom(0x8000), do: Openflow.Action.SetMplsTtl + def action_flags_to_atom(0x10000), do: Openflow.Action.DecMplsTtl + def action_flags_to_atom(0x20000), do: Openflow.Action.PushVlan + def action_flags_to_atom(0x40000), do: Openflow.Action.PopVlan + def action_flags_to_atom(0x80000), do: Openflow.Action.PushMpls + def action_flags_to_atom(0x100000), do: Openflow.Action.PopMpls + def action_flags_to_atom(0x200000), do: Openflow.Action.SetQueue + def action_flags_to_atom(0x400000), do: Openflow.Action.Group + def action_flags_to_atom(0x800000), do: Openflow.Action.SetNwTtl + def action_flags_to_atom(0x1000000), do: Openflow.Action.DecNwTtl + def action_flags_to_atom(0x2000000), do: Openflow.Action.SetField + def action_flags_to_atom(0x4000000), do: Openflow.Action.PushPbb + def action_flags_to_atom(0x8000000), do: Openflow.Action.PopPbb + def action_flags_to_atom(0x10000000), do: Openflow.Action.Encap + def action_flags_to_atom(0x20000000), do: Openflow.Action.Decap + def action_flags_to_atom(0x40000000), do: Openflow.Action.SetSequence + def action_flags_to_atom(0x80000000), do: Openflow.Action.ValidateSequence + def action_flags_to_atom(0xFFFF), do: Openflow.Action.Experimenter + def action_flags_to_atom(_), do: throw(:bad_enum) def action_vendor_to_int(:nicira_ext_action), do: 0x2320 def action_vendor_to_int(:onf_ext_action), do: 0x4F4E4600 def action_vendor_to_int(_), do: throw(:bad_enum) @@ -11465,8 +12353,6 @@ defmodule Openflow.Enums do def onf_ext_action_to_atom(_), do: throw(:bad_enum) def nicira_ext_action_to_int(Openflow.Action.NxResubmit), do: 0x1 def nicira_ext_action_to_int(Openflow.Action.NxSetTunnel), do: 0x2 - def nicira_ext_action_to_int(Openflow.Action.NxSetQueue), do: 0x4 - def nicira_ext_action_to_int(Openflow.Action.NxPopQueue), do: 0x5 def nicira_ext_action_to_int(Openflow.Action.NxRegMove), do: 0x6 def nicira_ext_action_to_int(Openflow.Action.NxRegLoad), do: 0x7 def nicira_ext_action_to_int(Openflow.Action.NxNote), do: 0x8 @@ -11483,15 +12369,9 @@ defmodule Openflow.Enums do def nicira_ext_action_to_int(Openflow.Action.NxController), do: 0x14 def nicira_ext_action_to_int(Openflow.Action.NxDecTtlCntIds), do: 0x15 def nicira_ext_action_to_int(Openflow.Action.NxWriteMetadata), do: 0x16 - def nicira_ext_action_to_int(Openflow.Action.NxPushMpls), do: 0x17 - def nicira_ext_action_to_int(Openflow.Action.NxPopMpls), do: 0x18 - def nicira_ext_action_to_int(Openflow.Action.NxSetMplsTtl), do: 0x19 - def nicira_ext_action_to_int(Openflow.Action.NxDecMplsTtl), do: 0x1A def nicira_ext_action_to_int(Openflow.Action.NxStackPush), do: 0x1B def nicira_ext_action_to_int(Openflow.Action.NxStackPop), do: 0x1C def nicira_ext_action_to_int(Openflow.Action.NxSample), do: 0x1D - def nicira_ext_action_to_int(Openflow.Action.NxSetMplsLabel), do: 0x1E - def nicira_ext_action_to_int(Openflow.Action.NxSetMplsTc), do: 0x1F def nicira_ext_action_to_int(Openflow.Action.NxOutputReg2), do: 0x20 def nicira_ext_action_to_int(Openflow.Action.NxRegLoad2), do: 0x21 def nicira_ext_action_to_int(Openflow.Action.NxConjunction), do: 0x22 @@ -11512,8 +12392,6 @@ defmodule Openflow.Enums do def nicira_ext_action_to_int(_), do: throw(:bad_enum) def nicira_ext_action_to_atom(0x1), do: Openflow.Action.NxResubmit def nicira_ext_action_to_atom(0x2), do: Openflow.Action.NxSetTunnel - def nicira_ext_action_to_atom(0x4), do: Openflow.Action.NxSetQueue - def nicira_ext_action_to_atom(0x5), do: Openflow.Action.NxPopQueue def nicira_ext_action_to_atom(0x6), do: Openflow.Action.NxRegMove def nicira_ext_action_to_atom(0x7), do: Openflow.Action.NxRegLoad def nicira_ext_action_to_atom(0x8), do: Openflow.Action.NxNote @@ -11530,15 +12408,9 @@ defmodule Openflow.Enums do def nicira_ext_action_to_atom(0x14), do: Openflow.Action.NxController def nicira_ext_action_to_atom(0x15), do: Openflow.Action.NxDecTtlCntIds def nicira_ext_action_to_atom(0x16), do: Openflow.Action.NxWriteMetadata - def nicira_ext_action_to_atom(0x17), do: Openflow.Action.NxPushMpls - def nicira_ext_action_to_atom(0x18), do: Openflow.Action.NxPopMpls - def nicira_ext_action_to_atom(0x19), do: Openflow.Action.NxSetMplsTtl - def nicira_ext_action_to_atom(0x1A), do: Openflow.Action.NxDecMplsTtl def nicira_ext_action_to_atom(0x1B), do: Openflow.Action.NxStackPush def nicira_ext_action_to_atom(0x1C), do: Openflow.Action.NxStackPop def nicira_ext_action_to_atom(0x1D), do: Openflow.Action.NxSample - def nicira_ext_action_to_atom(0x1E), do: Openflow.Action.NxSetMplsLabel - def nicira_ext_action_to_atom(0x1F), do: Openflow.Action.NxSetMplsTc def nicira_ext_action_to_atom(0x20), do: Openflow.Action.NxOutputReg2 def nicira_ext_action_to_atom(0x21), do: Openflow.Action.NxRegLoad2 def nicira_ext_action_to_atom(0x22), do: Openflow.Action.NxConjunction @@ -11807,6 +12679,30 @@ defmodule Openflow.Enums do def table_feature_prop_type_to_atom(0xFFFE), do: :experimenter def table_feature_prop_type_to_atom(0xFFFF), do: :experimenter_miss def table_feature_prop_type_to_atom(_), do: throw(:bad_enum) + def bundle_ctrl_type_to_int(:open_request), do: 0x0 + def bundle_ctrl_type_to_int(:open_reply), do: 0x1 + def bundle_ctrl_type_to_int(:close_request), do: 0x2 + def bundle_ctrl_type_to_int(:close_reply), do: 0x3 + def bundle_ctrl_type_to_int(:commit_request), do: 0x4 + def bundle_ctrl_type_to_int(:commit_reply), do: 0x5 + def bundle_ctrl_type_to_int(:discard_request), do: 0x6 + def bundle_ctrl_type_to_int(:discard_reply), do: 0x7 + def bundle_ctrl_type_to_int(_), do: throw(:bad_enum) + def bundle_ctrl_type_to_atom(0x0), do: :open_request + def bundle_ctrl_type_to_atom(0x1), do: :open_reply + def bundle_ctrl_type_to_atom(0x2), do: :close_request + def bundle_ctrl_type_to_atom(0x3), do: :close_reply + def bundle_ctrl_type_to_atom(0x4), do: :commit_request + def bundle_ctrl_type_to_atom(0x5), do: :commit_reply + def bundle_ctrl_type_to_atom(0x6), do: :discard_request + def bundle_ctrl_type_to_atom(0x7), do: :discard_reply + def bundle_ctrl_type_to_atom(_), do: throw(:bad_enum) + def bundle_flags_to_int(:atomic), do: 0x1 + def bundle_flags_to_int(:ordered), do: 0x2 + def bundle_flags_to_int(_), do: throw(:bad_enum) + def bundle_flags_to_atom(0x1), do: :atomic + def bundle_flags_to_atom(0x2), do: :ordered + def bundle_flags_to_atom(_), do: throw(:bad_enum) def int_to_flags(int, :openflow_codec) do Openflow.Utils.int_to_flags([], int, enum_of(:openflow_codec)) @@ -12008,6 +12904,10 @@ defmodule Openflow.Enums do Openflow.Utils.int_to_flags([], int, enum_of(:packet_in_reason)) end + def int_to_flags(int, :packet_in_reason_mask) do + Openflow.Utils.int_to_flags([], int, enum_of(:packet_in_reason_mask)) + end + def int_to_flags(int, :flow_mod_command) do Openflow.Utils.int_to_flags([], int, enum_of(:flow_mod_command)) end @@ -12020,10 +12920,18 @@ defmodule Openflow.Enums do Openflow.Utils.int_to_flags([], int, enum_of(:flow_removed_reason)) end + def int_to_flags(int, :flow_removed_reason_mask) do + Openflow.Utils.int_to_flags([], int, enum_of(:flow_removed_reason_mask)) + end + def int_to_flags(int, :port_reason) do Openflow.Utils.int_to_flags([], int, enum_of(:port_reason)) end + def int_to_flags(int, :port_reason_mask) do + Openflow.Utils.int_to_flags([], int, enum_of(:port_reason_mask)) + end + def int_to_flags(int, :group_mod_command) do Openflow.Utils.int_to_flags([], int, enum_of(:group_mod_command)) end @@ -12032,6 +12940,10 @@ defmodule Openflow.Enums do Openflow.Utils.int_to_flags([], int, enum_of(:group_type)) end + def int_to_flags(int, :group_type_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:group_type_flags)) + end + def int_to_flags(int, :group_id) do Openflow.Utils.int_to_flags([], int, enum_of(:group_id)) end @@ -12072,6 +12984,10 @@ defmodule Openflow.Enums do Openflow.Utils.int_to_flags([], int, enum_of(:action_type)) end + def int_to_flags(int, :action_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:action_flags)) + end + def int_to_flags(int, :action_vendor) do Openflow.Utils.int_to_flags([], int, enum_of(:action_vendor)) end @@ -12168,6 +13084,14 @@ defmodule Openflow.Enums do Openflow.Utils.int_to_flags([], int, enum_of(:table_feature_prop_type)) end + def int_to_flags(int, :bundle_ctrl_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:bundle_ctrl_type)) + end + + def int_to_flags(int, :bundle_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:bundle_flags)) + end + def flags_to_int(flags, :openflow_codec) do Openflow.Utils.flags_to_int(0, flags, enum_of(:openflow_codec)) end @@ -12368,6 +13292,10 @@ defmodule Openflow.Enums do Openflow.Utils.flags_to_int(0, flags, enum_of(:packet_in_reason)) end + def flags_to_int(flags, :packet_in_reason_mask) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:packet_in_reason_mask)) + end + def flags_to_int(flags, :flow_mod_command) do Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_mod_command)) end @@ -12380,10 +13308,18 @@ defmodule Openflow.Enums do Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_removed_reason)) end + def flags_to_int(flags, :flow_removed_reason_mask) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_removed_reason_mask)) + end + def flags_to_int(flags, :port_reason) do Openflow.Utils.flags_to_int(0, flags, enum_of(:port_reason)) end + def flags_to_int(flags, :port_reason_mask) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:port_reason_mask)) + end + def flags_to_int(flags, :group_mod_command) do Openflow.Utils.flags_to_int(0, flags, enum_of(:group_mod_command)) end @@ -12392,6 +13328,10 @@ defmodule Openflow.Enums do Openflow.Utils.flags_to_int(0, flags, enum_of(:group_type)) end + def flags_to_int(flags, :group_type_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:group_type_flags)) + end + def flags_to_int(flags, :group_id) do Openflow.Utils.flags_to_int(0, flags, enum_of(:group_id)) end @@ -12432,6 +13372,10 @@ defmodule Openflow.Enums do Openflow.Utils.flags_to_int(0, flags, enum_of(:action_type)) end + def flags_to_int(flags, :action_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:action_flags)) + end + def flags_to_int(flags, :action_vendor) do Openflow.Utils.flags_to_int(0, flags, enum_of(:action_vendor)) end @@ -12528,6 +13472,14 @@ defmodule Openflow.Enums do Openflow.Utils.flags_to_int(0, flags, enum_of(:table_feature_prop_type)) end + def flags_to_int(flags, :bundle_ctrl_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:bundle_ctrl_type)) + end + + def flags_to_int(flags, :bundle_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:bundle_flags)) + end + defp enum_of(:openflow_codec), do: [ {Openflow.Hello, 0}, @@ -12590,7 +13542,7 @@ defmodule Openflow.Enums do {Openflow.Multipart.Flow.Request, 1}, {Openflow.Multipart.Aggregate.Request, 2}, {Openflow.Multipart.Table.Request, 3}, - {Openflow.Multipart.PortStats.Request, 4}, + {Openflow.Multipart.Port.Request, 4}, {Openflow.Multipart.Queue.Request, 5}, {Openflow.Multipart.Group.Request, 6}, {Openflow.Multipart.GroupDesc.Request, 7}, @@ -12609,7 +13561,7 @@ defmodule Openflow.Enums do {Openflow.Multipart.Flow.Reply, 1}, {Openflow.Multipart.Aggregate.Reply, 2}, {Openflow.Multipart.Table.Reply, 3}, - {Openflow.Multipart.PortStats.Reply, 4}, + {Openflow.Multipart.Port.Reply, 4}, {Openflow.Multipart.Queue.Reply, 5}, {Openflow.Multipart.Group.Reply, 6}, {Openflow.Multipart.GroupDesc.Reply, 7}, @@ -12669,7 +13621,17 @@ defmodule Openflow.Enums do is_slave: 10, bad_port: 11, bad_packet: 12, - multipart_buffer_overflow: 13 + multipart_buffer_overflow: 13, + multipart_request_timeout: 14, + multipart_reply_timeout: 15, + nxm_invalid: 256, + nxm_bad_type: 257, + must_be_zero: 515, + bad_reason: 516, + flow_monitor_bad_event: 520, + undecodable_error: 521, + resume_not_supported: 533, + resume_stale: 534 ] defp enum_of(:bad_action), @@ -12689,7 +13651,10 @@ defmodule Openflow.Enums do bad_tag: 12, bad_set_type: 13, bad_set_len: 14, - bad_set_argument: 15 + bad_set_argument: 15, + must_be_zero: 256, + conntrack_datapath_support: 265, + bad_conjunction: 526 ] defp enum_of(:bad_instruction), @@ -12702,7 +13667,8 @@ defmodule Openflow.Enums do bad_experimeter: 5, bad_exp_type: 6, bad_len: 7, - eperm: 8 + eperm: 8, + dup_inst: 256 ] defp enum_of(:bad_match), @@ -12718,7 +13684,8 @@ defmodule Openflow.Enums do bad_mask: 8, bad_prereq: 9, dup_field: 10, - eperm: 11 + eperm: 11, + conntrack_datapath_support: 264 ] defp enum_of(:flow_mod_failed), @@ -12749,7 +13716,9 @@ defmodule Openflow.Enums do bad_command: 11, bad_bucket: 12, bad_watch: 13, - eperm: 14 + eperm: 14, + unknown_bucket: 15, + bucket_exists: 16 ] defp enum_of(:port_mod_failed), @@ -12798,7 +13767,7 @@ defmodule Openflow.Enums do port_blocked: 256 ] - defp enum_of(:config_flags), do: [drop: 1, reasm: 2] + defp enum_of(:config_flags), do: [fragment_drop: 1, fragment_reassemble: 2] defp enum_of(:controller_max_len), do: [max: 65509, no_buffer: 65535] defp enum_of(:experimenter_oxm_vendors), @@ -13143,6 +14112,16 @@ defmodule Openflow.Enums do defp enum_of(:packet_in_reason), do: [no_match: 0, action: 1, invalid_ttl: 2, action_set: 3, group: 4, packet_out: 5] + defp enum_of(:packet_in_reason_mask), + do: [ + no_match: 1, + action: 2, + invalid_ttl: 4, + action_set: 8, + group: 16, + packet_out: 32 + ] + defp enum_of(:flow_mod_command), do: [add: 0, modify: 1, modify_strict: 2, delete: 3, delete_strict: 4] @@ -13165,9 +14144,21 @@ defmodule Openflow.Enums do eviction: 5 ] + defp enum_of(:flow_removed_reason_mask), + do: [ + idle_timeout: 1, + hard_timeout: 2, + delete: 4, + group_delete: 8, + meter_delete: 16, + eviction: 32 + ] + defp enum_of(:port_reason), do: [add: 0, delete: 1, modify: 2] + defp enum_of(:port_reason_mask), do: [add: 1, delete: 2, modify: 4] defp enum_of(:group_mod_command), do: [add: 0, modify: 1, delete: 2] defp enum_of(:group_type), do: [all: 0, select: 1, indirect: 2, fast_failover: 3] + defp enum_of(:group_type_flags), do: [all: 1, select: 2, indirect: 4, fast_failover: 8] defp enum_of(:group_id), do: [max: 4_294_967_040, all: 4_294_967_292, any: 4_294_967_295] defp enum_of(:group_capabilities), @@ -13229,6 +14220,31 @@ defmodule Openflow.Enums do {Openflow.Action.Experimenter, 65535} ] + defp enum_of(:action_flags), + do: [ + {Openflow.Action.Output, 1}, + {Openflow.Action.CopyTtlOut, 2048}, + {Openflow.Action.CopyTtlIn, 4096}, + {Openflow.Action.SetMplsTtl, 32768}, + {Openflow.Action.DecMplsTtl, 65536}, + {Openflow.Action.PushVlan, 131_072}, + {Openflow.Action.PopVlan, 262_144}, + {Openflow.Action.PushMpls, 524_288}, + {Openflow.Action.PopMpls, 1_048_576}, + {Openflow.Action.SetQueue, 2_097_152}, + {Openflow.Action.Group, 4_194_304}, + {Openflow.Action.SetNwTtl, 8_388_608}, + {Openflow.Action.DecNwTtl, 16_777_216}, + {Openflow.Action.SetField, 33_554_432}, + {Openflow.Action.PushPbb, 67_108_864}, + {Openflow.Action.PopPbb, 134_217_728}, + {Openflow.Action.Encap, 268_435_456}, + {Openflow.Action.Decap, 536_870_912}, + {Openflow.Action.SetSequence, 1_073_741_824}, + {Openflow.Action.ValidateSequence, 2_147_483_648}, + {Openflow.Action.Experimenter, 65535} + ] + defp enum_of(:action_vendor), do: [nicira_ext_action: 8992, onf_ext_action: 1_330_529_792] defp enum_of(:onf_ext_action), do: [{Openflow.Action.OnfCopyField, 3200}] @@ -13236,8 +14252,6 @@ defmodule Openflow.Enums do do: [ {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}, @@ -13254,15 +14268,9 @@ defmodule Openflow.Enums do {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}, @@ -13390,4 +14398,18 @@ defmodule Openflow.Enums do experimenter: 65534, experimenter_miss: 65535 ] + + defp enum_of(:bundle_ctrl_type), + do: [ + open_request: 0, + open_reply: 1, + close_request: 2, + close_reply: 3, + commit_request: 4, + commit_reply: 5, + discard_request: 6, + discard_reply: 7 + ] + + defp enum_of(:bundle_flags), do: [atomic: 1, ordered: 2] end diff --git a/lib/openflow/features.ex b/lib/openflow/features.ex deleted file mode 100644 index 43b26b8..0000000 --- a/lib/openflow/features.ex +++ /dev/null @@ -1,2 +0,0 @@ -defmodule Openflow.Features do -end diff --git a/lib/openflow/features/reply.ex b/lib/openflow/features/reply.ex index e104ba8..5d3df0a 100644 --- a/lib/openflow/features/reply.ex +++ b/lib/openflow/features/reply.ex @@ -11,11 +11,38 @@ defmodule Openflow.Features.Reply do alias __MODULE__ + @type t :: %Reply{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + n_buffers: 0..0xFFFFFFFF, + n_tables: 0..0xFF, + aux_id: 0..0xFF, + capabilities: [ + :flow_stats + | :table_stats + | :port_stats + | :group_stats + | :ip_reasm + | :queue_stats + | :arp_match_ip + | :port_blocked + ] + } + + @spec ofp_type :: 6 def ofp_type, do: 6 - def read( - <> - ) do + @spec read(<<_::192>>) :: t() + def read(<< + datapath_id::64-bits, + n_buf::32, + n_tab::8, + aux_id::8, + _pad::16, + caps_int::32, + _rsv::32 + >>) do dpid = Openflow.Utils.to_hex_string(datapath_id) flags = Openflow.Enums.int_to_flags(caps_int, :switch_capabilities) @@ -28,6 +55,7 @@ defmodule Openflow.Features.Reply do } end + @spec to_binary(t()) :: <<_::192>> def to_binary(%Reply{ datapath_id: datapath_id, n_buffers: n_buf, diff --git a/lib/openflow/features/request.ex b/lib/openflow/features/request.ex index 8fb5f4e..8f8f057 100644 --- a/lib/openflow/features/request.ex +++ b/lib/openflow/features/request.ex @@ -10,17 +10,22 @@ defmodule Openflow.Features.Request do alias __MODULE__ + @type t :: %Request{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xFFFF | nil + } + + @spec ofp_type :: 5 def ofp_type, do: 5 - def new(xid \\ 0) do - %Request{xid: xid} - end + @spec new(0..0xFFFF) :: t() + def new(xid \\ 0), do: %Request{xid: xid} - def read(_) do - %Request{} - end + @spec read(any()) :: t() + def read(_), do: %Request{} - def to_binary(%Request{}) do - <<>> - end + @spec to_binary(t()) :: <<>> + def to_binary(%Request{}), do: <<>> end diff --git a/lib/openflow/flow_mod.ex b/lib/openflow/flow_mod.ex index 4b3b541..7725b9a 100644 --- a/lib/openflow/flow_mod.ex +++ b/lib/openflow/flow_mod.ex @@ -21,39 +21,87 @@ defmodule Openflow.FlowMod do alias __MODULE__ + @type buffer_id :: :no_buffer | 0..0xFFFFFFFF + + @type command :: :add | :modify | :modify_strict | :delete | :delete_strict + + @type out_port :: Openflow.Port.number() + + @type out_group :: Openflow.GroupMod.id() + + @type flags :: [ + :send_flow_rem | :check_overlap | :reset_counts | :no_packet_counts | :no_byte_counts + ] + + @type t :: %FlowMod{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xF | nil, + cookie: 0..0xFFFFFFFFFFFFFFFF, + cookie_mask: 0..0xFFFFFFFFFFFFFFFF, + table_id: 0..0xFF, + command: command(), + idle_timeout: 0..0xFFFF, + hard_timeout: 0..0xFFFF, + priority: 0..0xFFFF, + buffer_id: buffer_id(), + out_port: out_port(), + out_group: out_group(), + flags: flags(), + match: %Openflow.Match{}, + instructions: [ + %Openflow.Instruction.ApplyActions{} + | %Openflow.Instruction.WriteActions{} + | %Openflow.Instruction.ClearActions{} + | %Openflow.Instruction.GotoTable{} + | %Openflow.Instruction.Meter{} + | %Openflow.Instruction.WriteMetadata{} + ] + } + + @spec ofp_type() :: 14 def ofp_type, do: 14 + @spec new( + xid: 0..0xFFFFFFFF, + cookie: 0..0xFFFFFFFFFFFFFFFF, + cookie_mask: 0..0xFFFFFFFFFFFFFFFF, + table_id: 0..0xF, + command: command(), + idle_timeout: 0..0xFFFF, + hard_timeout: 0..0xFFFF, + priority: 0..0xFFFF, + buffer_id: buffer_id(), + out_port: out_port(), + out_group: out_group(), + flags: flags(), + match: %Openflow.Match{}, + instructions: [ + %Openflow.Instruction.ApplyActions{} + | %Openflow.Instruction.WriteActions{} + | %Openflow.Instruction.ClearActions{} + | %Openflow.Instruction.GotoTable{} + | %Openflow.Instruction.Meter{} + | %Openflow.Instruction.WriteMetadata{} + ] + ) :: t() def new(options \\ []) do - xid = Keyword.get(options, :xid, 0) - 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{ - xid: xid, - 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 + xid: options[:xid] || 0, + cookie: options[:cookie] || 0, + cookie_mask: options[:cookie_mask] || 0, + priority: options[:priority] || 0x8000, + table_id: options[:table_id] || 0, + command: options[:command] || :add, + idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0, + buffer_id: options[:buffer_id] || :no_buffer, + out_port: options[:out_port] || :any, + out_group: options[:out_group] || :any, + flags: options[:flags] || [], + match: options[:match] || Openflow.Match.new(), + instructions: options[:instructions] || [] } end diff --git a/lib/openflow/flow_removed.ex b/lib/openflow/flow_removed.ex index eea87d3..9035804 100644 --- a/lib/openflow/flow_removed.ex +++ b/lib/openflow/flow_removed.ex @@ -19,6 +19,8 @@ defmodule Openflow.FlowRemoved do alias __MODULE__ + @type reason :: :idle_timeout | :hard_timeout | :delete | :group_delete | :meter | :eviction + def ofp_type, do: 11 def read( diff --git a/lib/openflow/get_async/reply.ex b/lib/openflow/get_async/reply.ex index 2f2e1a7..05caa8d 100644 --- a/lib/openflow/get_async/reply.ex +++ b/lib/openflow/get_async/reply.ex @@ -6,41 +6,89 @@ defmodule Openflow.GetAsync.Reply do datapath_id: nil, # virtual field aux_id: 0, - 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 + packet_in_mask_master: [], + packet_in_mask_slave: [], + port_status_mask_master: [], + port_status_mask_slave: [], + flow_removed_mask_master: [], + flow_removed_mask_slave: [] ) alias __MODULE__ + alias Openflow.Enums + @type t :: %Reply{ + version: 4, + datapath_id: String.t() | nil, + aux_id: 0..0xF, + xid: 0..0xFFFFFFFF, + packet_in_mask_master: [Openflow.Packet.reason()], + packet_in_mask_slave: [Openflow.Packet.reason()], + port_status_mask_master: [Openflow.Packet.reason()], + port_status_mask_slave: [Openflow.Packet.reason()], + flow_removed_mask_master: [Openflow.Packet.reason()], + flow_removed_mask_slave: [Openflow.Packet.reason()] + } + + @spec ofp_type() :: 27 def ofp_type, do: 27 - def read( - <> - ) do + @spec new( + xid: 0..0xFFFFFFFF, + packet_in_mask_master: [Openflow.Packet.reason()], + packet_in_mask_slave: [Openflow.Packet.reason()], + port_status_mask_master: [Openflow.Packet.reason()], + port_status_mask_slave: [Openflow.Packet.reason()], + flow_removed_mask_master: [Openflow.Packet.reason()], + flow_removed_mask_slave: [Openflow.Packet.reason()] + ) :: t() + def new(options \\ []) 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 + xid: options[:xid] || 0, + packet_in_mask_master: options[:packet_in_mask_master] || [], + packet_in_mask_slave: options[:packet_in_mask_slave] || [], + port_status_mask_master: options[:port_status_mask_master] || [], + port_status_mask_slave: options[:port_status_mask_slave] || [], + flow_removed_mask_master: options[:flow_removed_mask_master] || [], + flow_removed_mask_slave: options[:flow_removed_mask_slave] || [] } end + @spec read(<<_::192>>) :: t() + def read(<< + pin_mask_master::32, + pin_mask_slave::32, + ps_mask_master::32, + ps_mask_slave::32, + fr_mask_master::32, + fr_mask_slave::32 + >>) do + %Reply{ + packet_in_mask_master: Enums.int_to_flags(pin_mask_master, :packet_in_reason_mask), + packet_in_mask_slave: Enums.int_to_flags(pin_mask_slave, :packet_in_reason_mask), + port_status_mask_master: Enums.int_to_flags(ps_mask_master, :port_reason_mask), + port_status_mask_slave: Enums.int_to_flags(ps_mask_slave, :port_reason_mask), + flow_removed_mask_master: Enums.int_to_flags(fr_mask_master, :flow_removed_reason_mask), + flow_removed_mask_slave: Enums.int_to_flags(fr_mask_slave, :flow_removed_reason_mask) + } + end + + @spec to_binary(t()) :: <<_::192>> 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 + packet_in_mask_master: pin_mask_master, + packet_in_mask_slave: pin_mask_slave, + port_status_mask_master: ps_mask_master, + port_status_mask_slave: ps_mask_slave, + flow_removed_mask_master: fr_mask_master, + flow_removed_mask_slave: fr_mask_slave }) do - <> + << + Enums.flags_to_int(pin_mask_master, :packet_in_reason_mask)::32, + Enums.flags_to_int(pin_mask_slave, :packet_in_reason_mask)::32, + Enums.flags_to_int(ps_mask_master, :port_reason_mask)::32, + Enums.flags_to_int(ps_mask_slave, :port_reason_mask)::32, + Enums.flags_to_int(fr_mask_master, :flow_removed_reason_mask)::32, + Enums.flags_to_int(fr_mask_slave, :flow_removed_reason_mask)::32 + >> end end diff --git a/lib/openflow/get_async/request.ex b/lib/openflow/get_async/request.ex index 0306095..8c24a3c 100644 --- a/lib/openflow/get_async/request.ex +++ b/lib/openflow/get_async/request.ex @@ -10,17 +10,22 @@ defmodule Openflow.GetAsync.Request do alias __MODULE__ + @type t :: %Request{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xF | nil + } + + @spec ofp_type() :: 26 def ofp_type, do: 26 - def new(xid \\ 0) do - %Request{xid: xid} - end + @spec new(xid :: 0..0xFFFFFFFF) :: t() + def new(xid \\ 0), do: %Request{xid: xid} - def read(_) do - %Request{} - end + @spec read(any()) :: t() + def read(_), do: %Request{} - def to_binary(%Request{}) do - <<>> - end + @spec to_binary(t()) :: <<>> + def to_binary(%Request{}), do: <<>> end diff --git a/lib/openflow/get_config.ex b/lib/openflow/get_config.ex deleted file mode 100644 index ceb38e2..0000000 --- a/lib/openflow/get_config.ex +++ /dev/null @@ -1,2 +0,0 @@ -defmodule Openflow.GetConfig do -end diff --git a/lib/openflow/get_config/reply.ex b/lib/openflow/get_config/reply.ex index 55f08ff..f717a2b 100644 --- a/lib/openflow/get_config/reply.ex +++ b/lib/openflow/get_config/reply.ex @@ -11,14 +11,29 @@ defmodule Openflow.GetConfig.Reply do alias __MODULE__ + @type flags :: [:drop | :reasm] + @type max_len :: :max | :no_buffer | 0..0xFFFF + + @type t :: %Reply{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xF | nil, + flags: flags(), + miss_send_len: max_len() + } + + @spec ofp_type() :: t() def ofp_type, do: 8 + @spec read(<<_::32>>) :: t() 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 + @spec to_binary(t()) :: <<_::32>> 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) diff --git a/lib/openflow/get_config/request.ex b/lib/openflow/get_config/request.ex index f5ec984..bccd0cb 100644 --- a/lib/openflow/get_config/request.ex +++ b/lib/openflow/get_config/request.ex @@ -10,17 +10,22 @@ defmodule Openflow.GetConfig.Request do alias __MODULE__ + @type t :: %Request{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: non_neg_integer() | nil + } + + @spec ofp_type() :: 7 def ofp_type, do: 7 - def new(xid \\ 0) do - %Request{xid: xid} - end + @spec new(xid :: 0..0xFFFFFFFF) :: t() + def new(xid \\ 0), do: %Request{xid: xid} - def read(_) do - %Request{} - end + @spec read(any()) :: t() + def read(_), do: %Request{} - def to_binary(%Request{}) do - <<>> - end + @spec to_binary(t()) :: <<>> + def to_binary(%Request{}), do: <<>> end diff --git a/lib/openflow/group_mod.ex b/lib/openflow/group_mod.ex index 6f76684..bb3c217 100644 --- a/lib/openflow/group_mod.ex +++ b/lib/openflow/group_mod.ex @@ -12,17 +12,42 @@ defmodule Openflow.GroupMod do alias __MODULE__ + @type command :: :add | :delete | :modify + @type type :: :add | :select | :indirect | :fast_failover + @type id :: :max | :all | :any | 0..0xFFFFFFFF + + @type t :: %GroupMod{ + version: 4, + datapath_id: String.t() | nil, + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF, + command: command(), + type: type(), + group_id: id(), + buckets: [Openflow.Bucket.t()] + } + + @spec ofp_type() :: 15 def ofp_type, do: 15 + @spec new( + xid: 0..0xFFFFFFFF, + command: command(), + type: type(), + group_id: id(), + buckets: [Openflow.Bucket.t()] + ) :: t() def new(options \\ []) do - xid = Keyword.get(options, :xid, 0) - 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{xid: xid, command: command, type: type, group_id: group_id, buckets: buckets} + %GroupMod{ + xid: options[:xid] || 0, + command: options[:command] || :add, + type: options[:type] || :all, + group_id: options[:group_id] || 0, + buckets: options[:buckets] || [] + } end + @spec read(<<_::64, _::_*128>>) :: t() def read(<>) do command = Openflow.Utils.get_enum(command_int, :group_mod_command) type = Openflow.Utils.get_enum(type_int, :group_type) @@ -31,6 +56,7 @@ defmodule Openflow.GroupMod do %GroupMod{command: command, type: type, group_id: group_id, buckets: buckets} end + @spec to_binary(t()) :: <<_::64, _::_*128>> 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) diff --git a/lib/openflow/hello.ex b/lib/openflow/hello.ex index 463e32e..415ccc9 100644 --- a/lib/openflow/hello.ex +++ b/lib/openflow/hello.ex @@ -23,8 +23,9 @@ defmodule Openflow.Hello do 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) + elements + |> 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/instruction.ex b/lib/openflow/instruction.ex index b64b1c7..063de39 100644 --- a/lib/openflow/instruction.ex +++ b/lib/openflow/instruction.ex @@ -1,18 +1,31 @@ defmodule Openflow.Instruction do - def read(instruction_bin) do - do_read([], instruction_bin) - end + @moduledoc """ + Openflow instruction codec handler + """ - def to_binary(instructions) when is_list(instructions) do - to_binary(<<>>, instructions) - end + @type instruction :: + Openflow.Instruction.ApplyActions.t() + | Openflow.Instruction.WriteActions.t() + | Openflow.Instruction.ClearActions.t() + | Openflow.Instruction.GotoTable.t() + | Openflow.Instruction.WriteMetadata.t() + | Openflow.Instruction.Meter.t() - def to_binary(instruction) do - to_binary([instruction]) - end + @spec read(<<_::32, _::_*8>>) :: [instruction()] + def read(instruction_bin), + do: do_read([], instruction_bin) + + @spec to_binary(instruction()) :: <<_::32, _::_*8>> + def to_binary(instructions) when is_list(instructions), + do: to_binary(<<>>, instructions) + + @spec to_binary([instruction()]) :: <<_::32, _::_*8>> + def to_binary(instruction), + do: to_binary([instruction]) # private functions + @spec do_read([instruction()], <<_::_*8>>) :: [instruction()] defp do_read(acc, <<>>), do: Enum.reverse(acc) defp do_read(acc, <> = binary) do @@ -21,6 +34,7 @@ defmodule Openflow.Instruction do do_read([codec.read(instruction_bin) | acc], rest) end + @spec to_binary(<<_::_*8>>) :: [instruction()] defp to_binary(acc, []), do: acc defp to_binary(acc, [instruction | rest]) do diff --git a/lib/openflow/instructions/apply_actions.ex b/lib/openflow/instructions/apply_actions.ex index b331419..0def15d 100644 --- a/lib/openflow/instructions/apply_actions.ex +++ b/lib/openflow/instructions/apply_actions.ex @@ -3,16 +3,19 @@ defmodule Openflow.Instruction.ApplyActions do alias __MODULE__ - def new(actions) do - %ApplyActions{actions: actions} - end + @type t :: %ApplyActions{actions: [Openflow.Action.type()]} + @spec new(Openflow.Action.type() | [Openflow.Action.type()]) :: t() + def new(actions), do: %ApplyActions{actions: actions} + + @spec to_binary(t()) :: <<_::64, _::_*8>> 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 + @spec read(<<_::64, _::_*8>>) :: t() def read(<<4::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do actions = Openflow.Action.read(actions_bin) %ApplyActions{actions: actions} diff --git a/lib/openflow/instructions/clear_actions.ex b/lib/openflow/instructions/clear_actions.ex index 6d52c08..4ff2cf3 100644 --- a/lib/openflow/instructions/clear_actions.ex +++ b/lib/openflow/instructions/clear_actions.ex @@ -3,17 +3,17 @@ defmodule Openflow.Instruction.ClearActions do alias __MODULE__ - def new do - %ClearActions{} - end + @type t :: %ClearActions{} - 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 + @spec new() :: t() + def new, + do: %ClearActions{} - def read(<<5::16, _length::16, _::size(4)-unit(8), _::bytes>>) do - %ClearActions{} - end + @spec to_binary(t()) :: <<_::64>> + def to_binary(%ClearActions{}), + do: <<5::16, 8::16, 0::32>> + + @spec read(<<_::64, _::_*8>>) :: t() + def read(<<5::16, _length::16, _::size(4)-unit(8), _::bytes>>), + do: %ClearActions{} end diff --git a/lib/openflow/instructions/experimenter.ex b/lib/openflow/instructions/experimenter.ex index f4cc3f2..c2c65bf 100644 --- a/lib/openflow/instructions/experimenter.ex +++ b/lib/openflow/instructions/experimenter.ex @@ -3,15 +3,19 @@ defmodule Openflow.Instruction.Experimenter do alias __MODULE__ - def new(exp_id, data \\ "") do - %Experimenter{exp_id: exp_id, data: data} - end + @type t :: %Experimenter{exp_id: 0..0xFFFFFFFF, data: binary()} + @spec new(exp_id :: 0..0xFFFFFFFF, data :: String.t()) :: t() + def new(exp_id, data \\ ""), + do: %Experimenter{exp_id: exp_id, data: data} + + @spec to_binary(t()) :: <<_::64, _::_*8>> 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 + @spec read(<<_::64, _::_*8>>) :: t() def read(<<0xFFFF::16, _::16, exp_id::32, data::bytes>>) do %Experimenter{exp_id: exp_id, data: data} end diff --git a/lib/openflow/instructions/goto_table.ex b/lib/openflow/instructions/goto_table.ex index f78c2c9..aec4051 100644 --- a/lib/openflow/instructions/goto_table.ex +++ b/lib/openflow/instructions/goto_table.ex @@ -3,15 +3,18 @@ defmodule Openflow.Instruction.GotoTable do alias __MODULE__ - def new(table_id) do - %GotoTable{table_id: table_id} - end + @type t :: %GotoTable{table_id: 0..0xFF} + @spec new(table_id :: 0..0xFF) :: t() + def new(table_id), do: %GotoTable{table_id: table_id} + + @spec to_binary(t()) :: <<_::64>> 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 + @spec read(<<_::64>>) :: t() 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} diff --git a/lib/openflow/instructions/meter.ex b/lib/openflow/instructions/meter.ex index f038fc9..8e6f6f9 100644 --- a/lib/openflow/instructions/meter.ex +++ b/lib/openflow/instructions/meter.ex @@ -3,15 +3,18 @@ defmodule Openflow.Instruction.Meter do alias __MODULE__ - def new(meter_id) do - %Meter{meter_id: meter_id} - end + @type t :: %Meter{meter_id: 0..0xFFFFFFFF} + @spec new(meter_id :: 0..0xFFFFFFFF) :: t() + def new(meter_id), do: %Meter{meter_id: meter_id} + + @spec to_binary(t()) :: <<_::64>> 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 + @spec read(<<_::64>>) :: t() 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} diff --git a/lib/openflow/instructions/write_actions.ex b/lib/openflow/instructions/write_actions.ex index fb9eeea..827bfe9 100644 --- a/lib/openflow/instructions/write_actions.ex +++ b/lib/openflow/instructions/write_actions.ex @@ -3,16 +3,19 @@ defmodule Openflow.Instruction.WriteActions do alias __MODULE__ - def new(actions) do - %WriteActions{actions: actions} - end + @type t :: %WriteActions{actions: [Openflow.Action.type()]} + @spec new(Openflow.Action.type() | [Openflow.Action.type()]) :: t() + def new(actions), do: %WriteActions{actions: actions} + + @spec to_binary(t()) :: <<_::32, _::_*8>> 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 + @spec read(<<_::32, _::_*8>>) :: t() def read(<<3::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do actions = Openflow.Action.read(actions_bin) %WriteActions{actions: actions} diff --git a/lib/openflow/instructions/write_metadata.ex b/lib/openflow/instructions/write_metadata.ex index 5b0df98..5adf7f0 100644 --- a/lib/openflow/instructions/write_metadata.ex +++ b/lib/openflow/instructions/write_metadata.ex @@ -1,19 +1,31 @@ defmodule Openflow.Instruction.WriteMetadata do - defstruct(metadata: 0, metadata_mask: 0xFFFFFFFFFFFFFFFF) + defstruct(value: 0, mask: 0xFFFFFFFFFFFFFFFF) alias __MODULE__ + @type t :: %WriteMetadata{ + value: 0..0xFFFFFFFFFFFFFFFF, + mask: 0..0xFFFFFFFFFFFFFFFF + } + + @spec new( + value: 0..0xFFFFFFFFFFFFFFFF, + mask: 0..0xFFFFFFFFFFFFFFFF + ) :: t() 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} + %WriteMetadata{ + value: options[:value] || 0, + mask: options[:mask] || 0xFFFFFFFFFFFFFFFF + } 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>> + @spec to_binary(t()) :: <<_::192>> + def to_binary(%WriteMetadata{value: value, mask: mask}) do + <<2::16, 24::16, 0::size(4)-unit(8), value::64, 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} + @spec read(<<_::192>>) :: t() + def read(<<2::16, 24::16, _::size(4)-unit(8), value::64, mask::64>>) do + %WriteMetadata{value: value, mask: mask} end end diff --git a/lib/openflow/meter_band/drop.ex b/lib/openflow/meter_band/drop.ex index bacd01d..4728bb4 100644 --- a/lib/openflow/meter_band/drop.ex +++ b/lib/openflow/meter_band/drop.ex @@ -6,17 +6,17 @@ defmodule Openflow.MeterBand.Drop do 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 + @type t :: %Drop{rate: 0..0xFFFFFFFF, burst_size: 0..0xFFFFFFFF} - def read(<<1::16, 16::16, rate::32, burst_size::32, _::size(4)-unit(8)>>) do - %Drop{rate: rate, burst_size: burst_size} - end + @spec new(rate: 0..0xFFFFFFFF, burst_size: 0..0xFFFFFFFF) :: t() + def new(options), + do: %Drop{rate: options[:rate] || 0, burst_size: options[:burst_size] || 0} - 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 + @spec read(<<_::128>>) :: t() + def read(<<1::16, 16::16, rate::32, burst_size::32, _::size(4)-unit(8)>>), + do: %Drop{rate: rate, burst_size: burst_size} + + @spec to_binary(t()) :: <<_::128>> + 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 diff --git a/lib/openflow/meter_band/remark.ex b/lib/openflow/meter_band/remark.ex index 1bb3f93..a6d0102 100644 --- a/lib/openflow/meter_band/remark.ex +++ b/lib/openflow/meter_band/remark.ex @@ -7,18 +7,30 @@ defmodule Openflow.MeterBand.Remark do alias __MODULE__ + @type t :: %Remark{ + rate: 0..0xFFFFFFFF, + burst_size: 0..0xFFFFFFFF, + prec_level: 0..0xFF + } + + @spec new( + rate: 0..0xFFFFFFFF, + burst_size: 0..0xFFFFFFFF, + prec_level: 0..0xFF + ) :: t() 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} + %Remark{ + rate: options[:rate] || 0, + burst_size: options[:burst_size] || 0, + prec_level: options[:prec_level] || 0 + } 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 + @spec read(<<_::128>>) :: t() + 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} - 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 + @spec to_binary(t()) :: <<_::128>> + 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 diff --git a/lib/openflow/meter_mod.ex b/lib/openflow/meter_mod.ex index 05e47b3..bce7a74 100644 --- a/lib/openflow/meter_mod.ex +++ b/lib/openflow/meter_mod.ex @@ -12,8 +12,41 @@ defmodule Openflow.MeterMod do alias __MODULE__ + @type command :: :add | :modify | :delete + + @type t :: %MeterMod{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xFF | nil, + command: command(), + flags: [:kbps | :pktps | :burst | :stats], + bands: [Openflow.MeterBand.Drop.t() | Openflow.MeterBand.Remark.t()] + } + + @spec ofp_type() :: 29 def ofp_type, do: 29 + @spec new( + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xFF | nil, + command: command(), + flags: [:kbps | :pktps | :burst | :stats], + bands: [Openflow.MeterBand.Drop.t() | Openflow.MeterBand.Remark.t()] + ) :: t() + def new(options \\ []) do + %MeterMod{ + xid: options[:xid] || 0, + command: options[:command] || :add, + flags: options[:flags] || [], + meter_id: options[:meter_id] || 0, + bands: options[:bands] || [] + } + end + + @spec read(<<_::64, _::_*128>>) :: t() def read(<>) do command = Openflow.Enums.to_atom(command_int, :meter_mod_command) flags = Openflow.Enums.int_to_flags(flags_int, :meter_flags) @@ -22,6 +55,7 @@ defmodule Openflow.MeterMod do %MeterMod{command: command, flags: flags, meter_id: meter_id, bands: bands} end + @spec to_binary(t()) :: <<_::64, _::_*128>> 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) diff --git a/lib/openflow/multipart/aggregate/reply.ex b/lib/openflow/multipart/aggregate/reply.ex index 7b710c2..0dadfa1 100644 --- a/lib/openflow/multipart/aggregate/reply.ex +++ b/lib/openflow/multipart/aggregate/reply.ex @@ -13,9 +13,59 @@ defmodule Openflow.Multipart.Aggregate.Reply do alias __MODULE__ - def ofp_type, do: 18 + @type t :: %Reply{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xFF, + flags: [], + packet_count: 0..0xFFFFFFFFFFFFFFFF, + byte_count: 0..0xFFFFFFFFFFFFFFFF, + flow_count: 0..0xFFFFFFFF + } - def read(<>) do - %Reply{packet_count: packet_count, byte_count: byte_count, flow_count: flow_count} + @spec ofp_type() :: 19 + def ofp_type, do: 19 + + @spec new( + xid: 0..0xFFFFFFFF, + flags: [], + packet_count: 0..0xFFFFFFFFFFFFFFFF, + byte_count: 0..0xFFFFFFFFFFFFFFFF, + flow_count: 0..0xFFFFFFFF + ) :: t() + def new(options \\ []) do + %Reply{ + xid: options[:xid] || 0, + flags: options[:flags] || [], + packet_count: options[:packet_count] || 0, + byte_count: options[:byte_count] || 0, + flow_count: options[:flow_count] || 0 + } + end + + @spec to_binary(t()) :: <<_::64, _::_*8>> + def to_binary(aggregate) do + << + Openflow.Multipart.Reply.header(aggregate)::bytes, + aggregate.packet_count::64, + aggregate.byte_count::64, + aggregate.flow_count::32, + 0::size(4)-unit(8) + >> + end + + @spec read(<<_::192>>) :: t() + def read(<< + packet_count::64, + byte_count::64, + flow_count::32, + _::size(4)-unit(8) + >>) 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 index 0335cb9..089d7da 100644 --- a/lib/openflow/multipart/aggregate/request.ex +++ b/lib/openflow/multipart/aggregate/request.ex @@ -4,58 +4,82 @@ defmodule Openflow.Multipart.Aggregate.Request do xid: 0, # virtual field datapath_id: nil, + aux_id: nil, flags: [], table_id: :all, out_port: :any, out_group: :any, cookie: 0, cookie_mask: 0, - match: [] + match: Openflow.Match.new() ) alias __MODULE__ + @type t :: %Request{ + version: 4, + datapath_id: String.t(), + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF, + table_id: 0..0xFF | :all | :max, + out_port: Openflow.Port.no(), + out_group: Openflow.GroupMod.id(), + cookie: 0..0xFFFFFFFFFFFFFFFF, + cookie_mask: 0..0xFFFFFFFFFFFFFFFF, + match: %Openflow.Match{fields: [map()], type: :oxm} + } + + @spec ofp_type() :: 18 def ofp_type, do: 18 - def new(options) do - xid = Keyword.get(options, :xid, 0) - table_id = Keyword.get(options, :table_id, :all) - out_port = Keyword.get(options, :out_port, :any) - out_group = Keyword.get(options, :out_group, :any) - cookie = Keyword.get(options, :cookie, 0) - cookie_mask = Keyword.get(options, :cookie, 0) - match = Keyword.get(options, :match, []) + @spec new( + xid: 0..0xFFFFFFFF, + table_id: 0..0xFF | :all | :max, + out_port: Openflow.Port.no(), + out_group: Openflow.GroupMod.id(), + cookie: 0..0xFFFFFFFFFFFFFFFF, + cookie_mask: 0..0xFFFFFFFFFFFFFFFF, + match: %Openflow.Match{fields: [map()], type: :oxm} + ) :: t() + def new(options \\ []) do + %Request{ + xid: options[:xid] || 0, + table_id: options[:table_id] || :all, + out_port: options[:out_port] || :any, + out_group: options[:out_group] || :any, + cookie: options[:cookie] || 0, + cookie_mask: options[:cookie_mask] || 0, + match: options[:match] || Openflow.Match.new() + } + end + + @spec read(<<_::256, _::_*8>>) :: t() + def read(<< + table_id_int::8, + _::size(3)-unit(8), + out_port_int::32, + out_group_int::32, + _::size(4)-unit(8), + cookie::64, + cookie_mask::64, + match_bin::bytes + >>) do + match = + match_bin + |> Openflow.Match.read() + |> Kernel.elem(0) %Request{ - xid: xid, - table_id: table_id, - out_port: out_port, - out_group: out_group, - cookie: cookie, - cookie_mask: cookie_mask, - match: match - } - end - - def read( - <> - ) do - table_id = Openflow.Utils.get_enum(table_id_int, :table_id) - out_port = Openflow.Utils.get_enum(out_port_int, :openflow13_port_no) - out_group = Openflow.Utils.get_enum(out_group_int, :group_id) - {match, _rest} = Openflow.Match.read(match_bin) - - %Request{ - table_id: table_id, - out_port: out_port, - out_group: out_group, + table_id: Openflow.Utils.get_enum(table_id_int, :table_id), + out_port: Openflow.Utils.get_enum(out_port_int, :openflow13_port_no), + out_group: Openflow.Utils.get_enum(out_group_int, :group_id), cookie: cookie, cookie_mask: cookie_mask, match: match } end + @spec to_binary(t()) :: <<_::256, _::_*8>> def to_binary( %Request{ table_id: table_id, @@ -66,16 +90,16 @@ defmodule Openflow.Multipart.Aggregate.Request do match: match } = msg ) do - table_id_int = Openflow.Utils.get_enum(table_id, :table_id) - out_port_int = Openflow.Utils.get_enum(out_port, :openflow13_port_no) - out_group_int = Openflow.Utils.get_enum(out_group, :group_id) - match_bin = Openflow.Match.to_binary(match) - - body_bin = - <> - - header_bin = Openflow.Multipart.Request.header(msg) - <> + << + Openflow.Multipart.Request.header(msg)::bytes, + Openflow.Utils.get_enum(table_id, :table_id)::8, + 0::size(3)-unit(8), + Openflow.Utils.get_enum(out_port, :openflow13_port_no)::32, + Openflow.Utils.get_enum(out_group, :group_id)::32, + 0::size(4)-unit(8), + cookie::64, + cookie_mask::64, + Openflow.Match.to_binary(match)::bytes + >> end end diff --git a/lib/openflow/multipart/desc/reply.ex b/lib/openflow/multipart/desc/reply.ex index a995cf2..20158e5 100644 --- a/lib/openflow/multipart/desc/reply.ex +++ b/lib/openflow/multipart/desc/reply.ex @@ -15,22 +15,70 @@ defmodule Openflow.Multipart.Desc.Reply do alias __MODULE__ + @type t :: %Reply{ + version: 4, + datapath_id: String.t() | nil, + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF, + flags: [], + mfr_desc: String.t(), + hw_desc: String.t(), + sw_desc: String.t(), + serial_num: String.t(), + dp_desc: String.t() + } + @desc_str_len 256 @serial_num_len 32 + @spec ofp_type() :: 19 def ofp_type, do: 19 - def read( - <> - ) do + @spec new( + xid: 0..0xFFFFFFFF, + mfr_desc: String.t(), + hw_desc: String.t(), + sw_desc: String.t(), + serial_num: String.t(), + dp_desc: String.t() + ) :: t() + def new(options \\ []) 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) + xid: options[:xid] || 0, + mfr_desc: options[:mfr_desc] || "", + hw_desc: options[:hw_desc] || "", + sw_desc: options[:sw_desc] || "", + serial_num: options[:serial_num] || "", + dp_desc: options[:dp_desc] || "" + } + end + + @spec to_binary(t()) :: <<_::64, _::_*8>> + def to_binary(%Reply{} = desc) do + << + Openflow.Multipart.Reply.header(desc)::bytes, + String.pad_trailing(desc.mfr_desc, @desc_str_len, <<0>>)::bytes, + String.pad_trailing(desc.hw_desc, @desc_str_len, <<0>>)::bytes, + String.pad_trailing(desc.sw_desc, @desc_str_len, <<0>>)::bytes, + String.pad_trailing(desc.serial_num, @serial_num_len, <<0>>)::bytes, + String.pad_trailing(desc.dp_desc, @desc_str_len, <<0>>)::bytes + >> + end + + @spec read(<<_::8448>>) :: t() + def read(<< + mfr_desc::size(@desc_str_len)-bytes, + hw_desc::size(@desc_str_len)-bytes, + sw_desc::size(@desc_str_len)-bytes, + serial_num::size(@serial_num_len)-bytes, + dp_desc::size(@desc_str_len)-bytes + >>) do + %Reply{ + mfr_desc: hd(String.split(mfr_desc, <<0>>, parts: 2)), + hw_desc: hd(String.split(hw_desc, <<0>>, parts: 2)), + sw_desc: hd(String.split(sw_desc, <<0>>, parts: 2)), + serial_num: hd(String.split(serial_num, <<0>>, parts: 2)), + dp_desc: hd(String.split(dp_desc, <<0>>, parts: 2)) } end end diff --git a/lib/openflow/multipart/desc/request.ex b/lib/openflow/multipart/desc/request.ex index f0b17c5..999968e 100644 --- a/lib/openflow/multipart/desc/request.ex +++ b/lib/openflow/multipart/desc/request.ex @@ -4,22 +4,28 @@ defmodule Openflow.Multipart.Desc.Request do xid: 0, # virtual field datapath_id: nil, + aux_id: nil, flags: [] ) alias __MODULE__ + @type t :: %Request{ + version: 4, + datapath_id: String.t(), + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF + } + + @spec ofp_type() :: 18 def ofp_type, do: 18 - def new(xid \\ 0) do - %Request{xid: xid} - end + @spec new(0..0xFFFFFFFF) :: t() + def new(xid \\ 0), do: %Request{xid: xid} - def read("") do - %Request{} - end + @spec read(<<>>) :: t() + def read(""), do: %Request{} - def to_binary(%Request{} = msg) do - Openflow.Multipart.Request.header(msg) - end + @spec to_binary(t()) :: <<_::64>> + def to_binary(%Request{} = msg), do: Openflow.Multipart.Request.header(msg) end diff --git a/lib/openflow/multipart/flow/reply.ex b/lib/openflow/multipart/flow/reply.ex index 135a222..0618e7a 100644 --- a/lib/openflow/multipart/flow/reply.ex +++ b/lib/openflow/multipart/flow/reply.ex @@ -11,12 +11,19 @@ defmodule Openflow.Multipart.Flow.Reply do alias __MODULE__ - def ofp_type, do: 18 + @type t :: %Reply{ + version: 4, + datapath_id: String.t() | nil, + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF, + flags: [:more], + flows: [Openflow.Multipart.FlowStats.t()] + } - def new(flows \\ []) do - %Reply{flows: flows} - end + @spec ofp_type() :: 19 + def ofp_type, do: 19 + @spec read(<<_::16, _::_*384>>) :: t() def read(<>) do flows = Openflow.Multipart.FlowStats.read(flows_bin) %Reply{flows: flows} @@ -54,27 +61,53 @@ defmodule Openflow.Multipart.FlowStats do alias __MODULE__ - def read(binary) do - do_read([], binary) - end + @type t :: %FlowStats{ + table_id: 0..0xFF, + duration_sec: 0..0xFFFFFFFF, + duration_nsec: 0..0xFFFFFFFF, + priority: 0..0xFFFF, + idle_timeout: 0..0xFFFF, + hard_timeout: 0..0xFFFF, + flags: [:send_flow_rem | :delete_learned | :write_result], + cookie: 0..0xFFFFFFFFFFFFFFFF, + packet_count: 0..0xFFFFFFFFFFFFFFFF, + byte_count: 0..0xFFFFFFFFFFFFFFFF, + match: Openflow.Match.new(), + instructions: [Openflow.Instruction.instruction()] + } + + @spec read(<<_::_*8>>) :: [t()] + def read(binary), do: do_read([], binary) # private functions - defp do_read(acc, ""), do: Enum.reverse(acc) + @spec do_read(acc :: [t()], binary()) :: [t()] + defp do_read(acc, ""), do: acc + @spec do_read(acc :: [t()], <<_::16, _::_*8>>) :: [t()] defp do_read(acc, <> = binary) do <> = binary - do_read([codec(flow_stats_bin) | acc], rest) + do_read(acc ++ [codec(flow_stats_bin)], rest) end - defp codec( - <<_length::16, table_id_int::8, 0::8, duration_sec::32, duration_nsec::32, priority::16, - idle::16, hard::16, flags_int::16, _::size(4)-unit(8), cookie::64, packet_count::64, - byte_count::64, tail::bytes>> - ) do - {match, instructions_bin} = Openflow.Match.read(tail) - table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + defp codec(<< + _length::16, + table_id::8, + 0::8, + duration_sec::32, + duration_nsec::32, + priority::16, + idle::16, + hard::16, + flags_int::16, + _::size(4)-unit(8), + cookie::64, + packet_count::64, + byte_count::64, + tail::bytes + >>) do flags = Openflow.Enums.int_to_flags(flags_int, :flow_mod_flags) + {match, instructions_bin} = Openflow.Match.read(tail) instructions = Openflow.Instruction.read(instructions_bin) %FlowStats{ diff --git a/lib/openflow/multipart/flow/request.ex b/lib/openflow/multipart/flow/request.ex index c3b37b6..36fc004 100644 --- a/lib/openflow/multipart/flow/request.ex +++ b/lib/openflow/multipart/flow/request.ex @@ -4,58 +4,82 @@ defmodule Openflow.Multipart.Flow.Request do xid: 0, # virtual field datapath_id: nil, + aux_id: nil, flags: [], table_id: :all, out_port: :any, out_group: :any, cookie: 0, cookie_mask: 0, - match: [] + match: Openflow.Match.new() ) alias __MODULE__ + @type t :: %Request{ + version: 4, + datapath_id: String.t(), + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF, + table_id: 0..0xFF | :all | :max, + out_port: Openflow.Port.no(), + out_group: Openflow.GroupMod.id(), + cookie: 0..0xFFFFFFFFFFFFFFFF, + cookie_mask: 0..0xFFFFFFFFFFFFFFFF, + match: %Openflow.Match{fields: [map()], type: :oxm} + } + + @spec ofp_type() :: 18 def ofp_type, do: 18 + @spec new( + xid: 0..0xFFFFFFFF, + table_id: 0..0xFF | :all | :max, + out_port: Openflow.Port.no(), + out_group: Openflow.GroupMod.id(), + cookie: 0..0xFFFFFFFFFFFFFFFF, + cookie_mask: 0..0xFFFFFFFFFFFFFFFF, + match: %Openflow.Match{fields: [map()], type: :oxm} + ) :: t() def new(options \\ []) do - xid = Keyword.get(options, :xid, 0) - table_id = Keyword.get(options, :table_id, :all) - out_port = Keyword.get(options, :out_port, :any) - out_group = Keyword.get(options, :out_group, :any) - cookie = Keyword.get(options, :cookie, 0) - cookie_mask = Keyword.get(options, :cookie, 0) - match = Keyword.get(options, :match, Openflow.Match.new()) + %Request{ + xid: options[:xid] || 0, + table_id: options[:table_id] || :all, + out_port: options[:out_port] || :any, + out_group: options[:out_group] || :any, + cookie: options[:cookie] || 0, + cookie_mask: options[:cookie_mask] || 0, + match: options[:match] || Openflow.Match.new() + } + end + + @spec read(<<_::256, _::_*8>>) :: t() + def read(<< + table_id_int::8, + _::size(3)-unit(8), + out_port_int::32, + out_group_int::32, + _::size(4)-unit(8), + cookie::64, + cookie_mask::64, + match_bin::bytes + >>) do + match = + match_bin + |> Openflow.Match.read() + |> Kernel.elem(0) %Request{ - xid: xid, - table_id: table_id, - out_port: out_port, - out_group: out_group, - cookie: cookie, - cookie_mask: cookie_mask, - match: match - } - end - - def read( - <> - ) do - table_id = Openflow.Utils.get_enum(table_id_int, :table_id) - out_port = Openflow.Utils.get_enum(out_port_int, :openflow13_port_no) - out_group = Openflow.Utils.get_enum(out_group_int, :group_id) - {match, _rest} = Openflow.Match.read(match_bin) - - %Request{ - table_id: table_id, - out_port: out_port, - out_group: out_group, + table_id: Openflow.Utils.get_enum(table_id_int, :table_id), + out_port: Openflow.Utils.get_enum(out_port_int, :openflow13_port_no), + out_group: Openflow.Utils.get_enum(out_group_int, :group_id), cookie: cookie, cookie_mask: cookie_mask, match: match } end + @spec to_binary(t()) :: <<_::256, _::_*8>> def to_binary( %Request{ table_id: table_id, @@ -66,16 +90,16 @@ defmodule Openflow.Multipart.Flow.Request do match: match } = msg ) do - table_id_int = Openflow.Utils.get_enum(table_id, :table_id) - out_port_int = Openflow.Utils.get_enum(out_port, :openflow13_port_no) - out_group_int = Openflow.Utils.get_enum(out_group, :group_id) - match_bin = Openflow.Match.to_binary(match) - - body_bin = - <> - - header_bin = Openflow.Multipart.Request.header(msg) - <> + << + Openflow.Multipart.Request.header(msg)::bytes, + Openflow.Utils.get_enum(table_id, :table_id)::8, + 0::size(3)-unit(8), + Openflow.Utils.get_enum(out_port, :openflow13_port_no)::32, + Openflow.Utils.get_enum(out_group, :group_id)::32, + 0::size(4)-unit(8), + cookie::64, + cookie_mask::64, + Openflow.Match.to_binary(match)::bytes + >> end end diff --git a/lib/openflow/multipart/group/reply.ex b/lib/openflow/multipart/group/reply.ex index 5e4b86f..499973d 100644 --- a/lib/openflow/multipart/group/reply.ex +++ b/lib/openflow/multipart/group/reply.ex @@ -11,14 +11,21 @@ defmodule Openflow.Multipart.Group.Reply do alias __MODULE__ + @type t :: %Reply{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xFF | nil, + flags: [:more], + groups: [Openflow.Multipart.GroupStats.t()] + } + + @spec ofp_type() :: 18 def ofp_type, do: 18 - def new(groups \\ []) do - %Reply{groups: groups} - end - + @spec read(binary()) :: t() def read(<>) do - groups = Openflow.Multipart.Group.read(groups_bin) + groups = Openflow.Multipart.GroupStats.read(groups_bin) %Reply{groups: groups} end @@ -36,7 +43,7 @@ defmodule Openflow.Multipart.Group.Reply do end end -defmodule Openflow.Multipart.Group do +defmodule Openflow.Multipart.GroupStats do defstruct( group_id: 0, ref_count: 0, @@ -51,6 +58,15 @@ defmodule Openflow.Multipart.Group do alias __MODULE__ + @type t :: %GroupStats{ + group_id: 0..0xFFFFFFFF, + ref_count: 0..0xFFFFFFFF, + packet_count: 0..0xFFFFFFFFFFFFFFFF, + byte_count: 0..0xFFFFFFFFFFFFFFFF, + duration_sec: 0..0xFFFFFFFF, + duration_nsec: 0..0xFFFFFFFF + } + def read(binary) do do_read([], binary) end @@ -72,11 +88,10 @@ defmodule Openflow.Multipart.Group do <> = tail bucket_stats = - for <> do - %{packet_count: packet_count, byte_count: byte_count} - end + for <>, + do: %{packet_count: packet_count, byte_count: byte_count} - %Group{ + %GroupStats{ group_id: group_id, ref_count: ref_count, packet_count: packet_count, diff --git a/lib/openflow/multipart/group/request.ex b/lib/openflow/multipart/group/request.ex index 30103d8..523467f 100644 --- a/lib/openflow/multipart/group/request.ex +++ b/lib/openflow/multipart/group/request.ex @@ -4,34 +4,43 @@ defmodule Openflow.Multipart.Group.Request do xid: 0, # virtual field datapath_id: nil, + aux_id: nil, flags: [], group_id: :all ) alias __MODULE__ + @type t :: %Request{ + version: 4, + xid: 0..0xFFFFFFFF, + datapath_id: String.t() | nil, + aux_id: 0..0xFF | nil, + flags: [], + group_id: Openflow.GroupMod.id() + } + + @spec ofp_type() :: 18 def ofp_type, do: 18 - def new(options) when is_list(options) do + @spec new(xid: 0..0xFFFFFFFF, group_id: Openflow.GroupMod.id()) :: t() + def new(options \\ []) do %Request{ xid: options[:xid] || 0, group_id: options[:group_id] || :all } end - def new(group_id) when is_integer(group_id) or is_atom(group_id) do - %Request{group_id: group_id} - end - + @spec read(<<_::32, _::_*8>>) :: t() def read(<>) do group_id = Openflow.Utils.get_enum(group_id_int, :group_id) %Request{group_id: group_id} end + @spec to_binary(t()) :: binary() 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 index 8e8d424..a56b837 100644 --- a/lib/openflow/multipart/group_desc/reply.ex +++ b/lib/openflow/multipart/group_desc/reply.ex @@ -4,6 +4,7 @@ defmodule Openflow.Multipart.GroupDesc.Reply do xid: 0, # virtual field datapath_id: nil, + aux_id: nil, flags: [], groups: [] ) diff --git a/lib/openflow/multipart/group_desc/request.ex b/lib/openflow/multipart/group_desc/request.ex index c8c3dae..59053d2 100644 --- a/lib/openflow/multipart/group_desc/request.ex +++ b/lib/openflow/multipart/group_desc/request.ex @@ -4,11 +4,20 @@ defmodule Openflow.Multipart.GroupDesc.Request do xid: 0, # virtual field datapath_id: nil, + aux_id: nil, flags: [] ) alias __MODULE__ + @type t :: %Request{ + version: 4, + datapath_id: String.t(), + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF, + flags: [:more] + } + def ofp_type, do: 18 def new(xid \\ 0) do diff --git a/lib/openflow/multipart/group_features/reply.ex b/lib/openflow/multipart/group_features/reply.ex index a43667b..eb664ca 100644 --- a/lib/openflow/multipart/group_features/reply.ex +++ b/lib/openflow/multipart/group_features/reply.ex @@ -19,29 +19,29 @@ defmodule Openflow.Multipart.GroupFeatures.Reply do ) alias __MODULE__ + alias Openflow.Enums def ofp_type, do: 18 def read( - <> ) do capabilities = Openflow.Enums.int_to_flags(capabilities_int, :group_capabilities) %Reply{ - types: types_int, + types: Enums.int_to_flags(types_int, :group_type_flags), 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 + max_groups_for_all: max_groups_for_all, + max_groups_for_select: max_groups_for_select, + max_groups_for_indirect: max_groups_for_indirect, + max_groups_for_fast_failover: max_groups_for_fast_failover, + actions_for_all: Enums.int_to_flags(actions_for_all_int, :action_flags), + actions_for_select: Enums.int_to_flags(actions_for_select_int, :action_flags), + actions_for_indirect: Enums.int_to_flags(actions_for_indirect_int, :action_flags), + actions_for_fast_failover: Enums.int_to_flags(actions_for_fast_failover_int, :action_flags) } end end diff --git a/lib/openflow/multipart/group_features/request.ex b/lib/openflow/multipart/group_features/request.ex index e473878..f41e0e5 100644 --- a/lib/openflow/multipart/group_features/request.ex +++ b/lib/openflow/multipart/group_features/request.ex @@ -4,22 +4,28 @@ defmodule Openflow.Multipart.GroupFeatures.Request do xid: 0, # virtual field datapath_id: nil, + aux_id: nil, flags: [] ) alias __MODULE__ + @type t :: %Request{ + version: 4, + datapath_id: String.t(), + aux_id: 0..0xFF | nil, + xid: 0..0xFFFFFFFF + } + + @spec ofp_type() :: 18 def ofp_type, do: 18 - def new(xid \\ 0) do - %Request{xid: xid} - end + @spec new(xid :: 0..0xFFFFFFFF) :: t() + def new(xid \\ 0), do: %Request{xid: xid} - def read("") do - %Request{} - end + @spec read(<<>>) :: t() + def read(""), do: %Request{} - def to_binary(%Request{} = msg) do - Openflow.Multipart.Request.header(msg) - end + @spec to_binary(t()) :: binary() + def to_binary(%Request{} = msg), do: Openflow.Multipart.Request.header(msg) end diff --git a/lib/openflow/multipart/meter/request.ex b/lib/openflow/multipart/meter/request.ex index ea7bbdf..383e4de 100644 --- a/lib/openflow/multipart/meter/request.ex +++ b/lib/openflow/multipart/meter/request.ex @@ -12,11 +12,7 @@ defmodule Openflow.Multipart.Meter.Request do def ofp_type, do: 18 - def new(meter_id) when is_integer(meter_id) or is_atom(meter_id) do - %Request{meter_id: meter_id} - end - - def new(options) when is_list(options) do + def new(options \\ []) do %Request{ xid: options[:xid] || 0, meter_id: options[:meter_id] || :all diff --git a/lib/openflow/multipart/meter_config/request.ex b/lib/openflow/multipart/meter_config/request.ex deleted file mode 100644 index bd4c7a2..0000000 --- a/lib/openflow/multipart/meter_config/request.ex +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Openflow.Multipart.MeterConfig.Request do - defstruct( - version: 4, - xid: 0, - # virtual field - datapath_id: nil, - flags: [], - meter_id: :all - ) - - alias __MODULE__ - - def ofp_type, do: 18 - - def new(meter_id) when is_integer(meter_id) or is_atom(meter_id) do - %Request{meter_id: meter_id} - end - - def new(options) when is_list(options) do - %Request{ - xid: options[:xid] || 0, - meter_id: options[:meter_id] || :all - } - 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_desc/reply.ex b/lib/openflow/multipart/port_desc/reply.ex index 6025dab..a491d69 100644 --- a/lib/openflow/multipart/port_desc/reply.ex +++ b/lib/openflow/multipart/port_desc/reply.ex @@ -13,10 +13,6 @@ defmodule Openflow.Multipart.PortDesc.Reply do def ofp_type, do: 18 - def new(ports \\ []) do - %Reply{ports: ports} - end - def read(<>) do ports = for <>, do: Openflow.Port.read(port_bin) %Reply{ports: Enum.reverse(ports)} diff --git a/lib/openflow/multipart/port_stats/reply.ex b/lib/openflow/multipart/port_stats/reply.ex index 880c639..8d78e70 100644 --- a/lib/openflow/multipart/port_stats/reply.ex +++ b/lib/openflow/multipart/port_stats/reply.ex @@ -1,4 +1,4 @@ -defmodule Openflow.Multipart.PortStats.Reply do +defmodule Openflow.Multipart.Port.Reply do defstruct( version: 4, xid: 0, @@ -13,10 +13,6 @@ defmodule Openflow.Multipart.PortStats.Reply do 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} diff --git a/lib/openflow/multipart/port_stats/request.ex b/lib/openflow/multipart/port_stats/request.ex index ba85c04..0851a3d 100644 --- a/lib/openflow/multipart/port_stats/request.ex +++ b/lib/openflow/multipart/port_stats/request.ex @@ -1,4 +1,4 @@ -defmodule Openflow.Multipart.PortStats.Request do +defmodule Openflow.Multipart.Port.Request do defstruct( version: 4, xid: 0, @@ -12,11 +12,7 @@ defmodule Openflow.Multipart.PortStats.Request do def ofp_type, do: 18 - def new(port_no) when is_integer(port_no) or is_atom(port_no) do - %Request{port_number: port_no} - end - - def new(options) when is_list(options) do + def new(options \\ []) do %Request{ xid: options[:xid] || 0, port_number: options[:port_no] || :any diff --git a/lib/openflow/multipart/queue/reply.ex b/lib/openflow/multipart/queue/reply.ex index a420ba1..3e6e83e 100644 --- a/lib/openflow/multipart/queue/reply.ex +++ b/lib/openflow/multipart/queue/reply.ex @@ -13,12 +13,8 @@ defmodule Openflow.Multipart.Queue.Reply do def ofp_type, do: 18 - def new(queues \\ []) do - %Reply{queues: queues} - end - def read(<>) do - queues = Openflow.Multipart.Queue.read(queues_bin) + queues = Openflow.Multipart.QueueStats.read(queues_bin) %Reply{queues: queues} end @@ -36,7 +32,7 @@ defmodule Openflow.Multipart.Queue.Reply do end end -defmodule Openflow.Multipart.Queue do +defmodule Openflow.Multipart.QueueStats do defstruct( port_number: 0, queue_id: 0, @@ -65,7 +61,7 @@ defmodule Openflow.Multipart.Queue do <> ) do - %Queue{ + %QueueStats{ port_number: port_no, queue_id: queue_id, tx_bytes: tx_bytes, diff --git a/lib/openflow/multipart/queue/request.ex b/lib/openflow/multipart/queue/request.ex index 4240ede..dce2ef2 100644 --- a/lib/openflow/multipart/queue/request.ex +++ b/lib/openflow/multipart/queue/request.ex @@ -13,11 +13,12 @@ defmodule Openflow.Multipart.Queue.Request do def ofp_type, do: 18 - def new(options) do - xid = Keyword.get(options, :xid, 0) - port_no = Keyword.get(options, :port_number, :any) - queue_id = Keyword.get(options, :queue_id, :all) - %Request{xid: xid, port_number: port_no, queue_id: queue_id} + def new(options \\ []) do + %Request{ + xid: options[:xid] || 0, + port_number: options[:port_number] || :any, + queue_id: options[:queue_id] || :all + } end def read(<>) do diff --git a/lib/openflow/multipart/table/request.ex b/lib/openflow/multipart/table/request.ex index 1e0390d..bf73a2a 100644 --- a/lib/openflow/multipart/table/request.ex +++ b/lib/openflow/multipart/table/request.ex @@ -11,8 +11,8 @@ defmodule Openflow.Multipart.Table.Request do def ofp_type, do: 18 - def new(xid \\ 0) do - %Request{xid: xid} + def new(options \\ []) do + %Request{xid: options[:xid] || 0} end def read("") do diff --git a/lib/openflow/multipart/table_features/body.ex b/lib/openflow/multipart/table_features/body.ex index 3399a06..b1b89d9 100644 --- a/lib/openflow/multipart/table_features/body.ex +++ b/lib/openflow/multipart/table_features/body.ex @@ -18,7 +18,9 @@ defmodule Openflow.Multipart.TableFeatures.Body do write_setfield: nil, write_setfield_miss: nil, apply_setfield: nil, - apply_setfield_miss: nil + apply_setfield_miss: nil, + experimenter: nil, + experimenter_miss: nil alias __MODULE__ @@ -40,6 +42,8 @@ defmodule Openflow.Multipart.TableFeatures.Body do @write_setfield_miss 13 @apply_setfield 14 @apply_setfield_miss 15 + @experimenter 0xFFFE + @experimenter_miss 0xFFFF @prop_keys [ :instructions, @@ -55,7 +59,9 @@ defmodule Openflow.Multipart.TableFeatures.Body do :write_setfield, :write_setfield_miss, :apply_setfield, - :apply_setfield_miss + :apply_setfield_miss, + :experimenter, + :experimenter_miss ] def new(options) do @@ -79,7 +85,9 @@ defmodule Openflow.Multipart.TableFeatures.Body do write_setfield: options[:write_setfield], write_setfield_miss: options[:write_setfield_miss], apply_setfield: options[:apply_setfield], - apply_setfield_miss: options[:apply_setfield_miss] + apply_setfield_miss: options[:apply_setfield_miss], + experimenter: options[:experimenter], + experimenter_miss: options[:experimenter_miss] } end @@ -111,7 +119,7 @@ defmodule Openflow.Multipart.TableFeatures.Body do name_bin::size(@max_table_name_len)-bytes, metadata_match::64, metadata_write::64, config_int::32, max_entries::32, props_bin::bytes>> ) do - name = Openflow.Utils.decode_string(name_bin) + name = hd(String.split(name_bin, <<0>>, parts: 2)) config = Openflow.Enums.int_to_flags(config_int, :table_config) body = %Body{ @@ -142,7 +150,7 @@ defmodule Openflow.Multipart.TableFeatures.Body do } = table config_int = Openflow.Enums.flags_to_int(config, :table_config) - name_bin = Openflow.Utils.encode_string(name, @max_table_name_len) + name_bin = String.pad_trailing(name, @max_table_name_len, <<0>>) <> @@ -171,7 +179,7 @@ defmodule Openflow.Multipart.TableFeatures.Body do pad_length = Openflow.Utils.pad_length(length, 8) value_length = length - @prop_header_length <> = tail - next_tables = for <>, do: table_id + next_tables = :erlang.binary_to_list(next_tables_bin) type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) body @@ -208,9 +216,13 @@ defmodule Openflow.Multipart.TableFeatures.Body do |> decode_props(rest) end - defp decode_props(body, <<_type_int::16, length::16, tail::bytes>>) do + defp decode_props( + body, + <> + ) + when type_int == @experimenter or type_int == @experimenter_miss do pad_length = Openflow.Utils.pad_length(length, 8) - value_length = length - @prop_header_length + value_length = length - 12 <<_::size(value_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail decode_props(body, rest) end @@ -230,7 +242,7 @@ defmodule Openflow.Multipart.TableFeatures.Body do defp encode_props(acc, table, [type | rest]) when type == :next_tables or type == :next_tables_miss do type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) - next_tables_bin = to_string(Map.get(table, type)) + next_tables_bin = :erlang.list_to_binary(Map.get(table, type)) length = @prop_header_length + byte_size(next_tables_bin) pad_length = Openflow.Utils.pad_length(length, 8) body = <> @@ -260,6 +272,10 @@ defmodule Openflow.Multipart.TableFeatures.Body do encode_props(<>, table, rest) end + defp encode_props(acc, table, [_type | rest]) do + encode_props(acc, table, rest) + end + defp decode_instructions(acc, ""), do: Enum.reverse(acc) defp decode_instructions(acc, <<0xFFFF::16, _::16, exp_id::32, rest::bytes>>) do diff --git a/lib/openflow/multipart/table_features/reply.ex b/lib/openflow/multipart/table_features/reply.ex index f44f4fa..3e53960 100644 --- a/lib/openflow/multipart/table_features/reply.ex +++ b/lib/openflow/multipart/table_features/reply.ex @@ -12,24 +12,13 @@ defmodule Openflow.Multipart.TableFeatures.Reply do alias __MODULE__ alias Openflow.Multipart.TableFeatures.Body - def ofp_type, do: 18 - - def new(tables \\ []) do - %Reply{tables: tables} - end + def ofp_type, do: 19 def read(<>) do tables = Body.read(tables_bin) %Reply{tables: tables} end - def to_binary(msg) do - header_bin = Openflow.Multipart.Reply.header(msg) - %Reply{tables: tables} = msg - tables_bin = Openflow.Multipart.TableFeatures.Body.to_binary(tables) - <> - end - def append_body(%Reply{tables: tables} = message, %Reply{flags: [:more], tables: continue}) do %{message | tables: [continue | tables]} end diff --git a/lib/openflow/multipart/table_features/request.ex b/lib/openflow/multipart/table_features/request.ex index 451977d..dbcd331 100644 --- a/lib/openflow/multipart/table_features/request.ex +++ b/lib/openflow/multipart/table_features/request.ex @@ -16,6 +16,7 @@ defmodule Openflow.Multipart.TableFeatures.Request do def new(options \\ []) do %Request{ + flags: options[:flags] || [], xid: options[:xid] || 0, tables: options[:tables] || [] } @@ -32,17 +33,4 @@ defmodule Openflow.Multipart.TableFeatures.Request do tables_bin = Openflow.Multipart.TableFeatures.Body.to_binary(tables) <> end - - def append_body(%Request{tables: tables} = message, %Request{flags: [:more], tables: continue}) do - %{message | tables: [continue | tables]} - end - - def append_body(%Request{tables: tables} = message, %Request{flags: [], tables: continue}) do - new_tables = - [continue | tables] - |> Enum.reverse() - |> List.flatten() - - %{message | tables: new_tables} - end end diff --git a/lib/openflow/nx_packet_in2.ex b/lib/openflow/nx_packet_in2.ex index 77955be..a2d9af6 100644 --- a/lib/openflow/nx_packet_in2.ex +++ b/lib/openflow/nx_packet_in2.ex @@ -14,12 +14,13 @@ 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 ) diff --git a/lib/openflow/nx_packet_in_format.ex b/lib/openflow/nx_packet_in_format.ex index e42b570..968e487 100644 --- a/lib/openflow/nx_packet_in_format.ex +++ b/lib/openflow/nx_packet_in_format.ex @@ -14,8 +14,15 @@ defmodule Openflow.NxSetPacketInFormat do def ofp_type, do: 4 - def new(format \\ :standard) do - %NxSetPacketInFormat{format: format} + def new(format) when is_atom(format) do + new(format: format) + end + + def new(options) when is_list(options) do + %NxSetPacketInFormat{ + format: options[:format] || :standard, + xid: options[:xid] || 0 + } end def read(<<@experimenter::32, @nx_type::32, format_int::32>>) do diff --git a/lib/openflow/nx_resume.ex b/lib/openflow/nx_resume.ex new file mode 100644 index 0000000..631a391 --- /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/openflow/nx_set_controller_id.ex b/lib/openflow/nx_set_controller_id.ex index f249960..b036a70 100644 --- a/lib/openflow/nx_set_controller_id.ex +++ b/lib/openflow/nx_set_controller_id.ex @@ -16,10 +16,6 @@ defmodule Openflow.NxSetControllerId 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 diff --git a/lib/openflow/onf_bundle_add.ex b/lib/openflow/onf_bundle_add.ex new file mode 100644 index 0000000..2f53b23 --- /dev/null +++ b/lib/openflow/onf_bundle_add.ex @@ -0,0 +1,53 @@ +defmodule Openflow.OnfBundleAdd do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + # bundle header + bundle_id: 0, + flags: [], + message: nil + ) + + alias __MODULE__ + + # ONF Experimenter + @experimenter 0x4F4E4600 + # BUNDLE_ADD + @onf_type 2301 + + @onf_bundle_add_message_size 24 + + # experimenter + def ofp_type, do: 4 + + def new(options \\ []) do + %OnfBundleAdd{ + xid: options[:xid] || 0, + bundle_id: options[:bundle_id] || 0, + flags: options[:flags] || [], + message: options[:message] + } + end + + def to_binary(%OnfBundleAdd{} = bundle_add) do + bundle_id = bundle_add.bundle_id + message_bin = Openflow.to_binary(%{bundle_add.message | xid: bundle_add.xid}) + length = @onf_bundle_add_message_size + byte_size(message_bin) + pad_length = Openflow.Utils.pad_length(length, 8) + flags_int = Openflow.Enums.flags_to_int(bundle_add.flags, :bundle_flags) + + <<@experimenter::32, @onf_type::32, bundle_id::32, 0::2-unit(8), flags_int::16, + message_bin::bytes, 0::size(pad_length)-unit(8)>> + end + + def read( + <<@experimenter::32, @onf_type::32, bundle_id::32, _pad::16, flags_int::16, + message_bin::bytes>> + ) do + message = Openflow.read(message_bin) + flags = Openflow.Enums.int_to_flags(flags_int, :bundle_flags) + %OnfBundleAdd{bundle_id: bundle_id, flags: flags, message: message} + end +end diff --git a/lib/openflow/onf_bundle_control.ex b/lib/openflow/onf_bundle_control.ex new file mode 100644 index 0000000..8f32987 --- /dev/null +++ b/lib/openflow/onf_bundle_control.ex @@ -0,0 +1,44 @@ +defmodule Openflow.OnfBundleControl do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + # bundle header + bundle_id: 0, + type: :open_request, + flags: [] + ) + + alias __MODULE__ + + # ONF Experimenter + @experimenter 0x4F4E4600 + # BUNDLE_CONTROL + @onf_type 2300 + + # experimenter + def ofp_type, do: 4 + + def new(options \\ []) do + %OnfBundleControl{ + xid: options[:xid] || 0, + bundle_id: options[:bundle_id] || 0, + type: options[:type] || :open_request, + flags: options[:flags] || [] + } + end + + def to_binary(%OnfBundleControl{} = ctrl) do + bundle_id = ctrl.bundle_id + type_int = Openflow.Utils.get_enum(ctrl.type, :bundle_ctrl_type) + flags_int = Openflow.Enums.flags_to_int(ctrl.flags, :bundle_flags) + <<@experimenter::32, @onf_type::32, bundle_id::32, type_int::16, flags_int::16>> + end + + def read(<<@experimenter::32, @onf_type::32, bundle_id::32, type_int::16, flags_int::16>>) do + type = Openflow.Utils.get_enum(type_int, :bundle_ctrl_type) + flags = Openflow.Enums.int_to_flags(flags_int, :bundle_flags) + %OnfBundleControl{bundle_id: bundle_id, type: type, flags: flags} + end +end diff --git a/lib/openflow/packet_in.ex b/lib/openflow/packet_in.ex index 2d436e8..ea4a738 100644 --- a/lib/openflow/packet_in.ex +++ b/lib/openflow/packet_in.ex @@ -16,6 +16,8 @@ defmodule Openflow.PacketIn do alias __MODULE__ + @type reason :: :no_match | :action | :invalid_ttl | :action_set | :group | :packet_out + def ofp_type, do: 10 def read( @@ -41,29 +43,4 @@ defmodule Openflow.PacketIn do 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/port.ex b/lib/openflow/port.ex index 099b0bc..f500fa3 100644 --- a/lib/openflow/port.ex +++ b/lib/openflow/port.ex @@ -17,6 +17,17 @@ defmodule Openflow.Port do alias __MODULE__ + @type no :: + 0..0xFFFFFFFF + | :max + | :table + | :normal + | :flood + | :all + | :controller + | :local + | :any + def read( <>) do diff --git a/lib/openflow/role/reply.ex b/lib/openflow/role/reply.ex index 0480a0f..24e6f73 100644 --- a/lib/openflow/role/reply.ex +++ b/lib/openflow/role/reply.ex @@ -14,19 +14,8 @@ defmodule Openflow.Role.Reply do 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 index 3bacde8..ee96941 100644 --- a/lib/openflow/role/request.ex +++ b/lib/openflow/role/request.ex @@ -21,11 +21,6 @@ defmodule Openflow.Role.Request do %Request{xid: xid, 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) <> diff --git a/lib/openflow/set_async.ex b/lib/openflow/set_async.ex index f42095b..c33c540 100644 --- a/lib/openflow/set_async.ex +++ b/lib/openflow/set_async.ex @@ -6,53 +6,46 @@ defmodule Openflow.SetAsync do datapath_id: nil, # virtual field aux_id: 0, - 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 + packet_in_mask_master: [], + packet_in_mask_slave: [], + port_status_mask_master: [], + port_status_mask_slave: [], + flow_removed_mask_master: [], + flow_removed_mask_slave: [] ) alias __MODULE__ + alias Openflow.Enums def ofp_type, do: 28 def new(options \\ []) do %SetAsync{ - xid: options[:xid] || 0, - packet_in_mask_master: options[:packet_in_mask_master], - packet_in_mask_slave: options[:packet_in_mask_slave], - port_status_mask_master: options[:port_status_mask_master], - port_status_mask_slave: options[:port_status_mask_slave], - flow_removed_mask_master: options[:flow_removed_mask_master], - flow_removed_mask_slave: options[:flow_removed_mask_slave] - } - end - - 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 + xid: Keyword.get(options, :xid, 0), + packet_in_mask_master: Keyword.get(options, :packet_in_mask_master, []), + packet_in_mask_slave: Keyword.get(options, :packet_in_mask_slave, []), + port_status_mask_master: Keyword.get(options, :port_status_mask_master, []), + port_status_mask_slave: Keyword.get(options, :port_status_mask_slave, []), + flow_removed_mask_master: Keyword.get(options, :flow_removed_mask_master, []), + flow_removed_mask_slave: Keyword.get(options, :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 + packet_in_mask_master: pin_mask_master, + packet_in_mask_slave: pin_mask_slave, + port_status_mask_master: ps_mask_master, + port_status_mask_slave: ps_mask_slave, + flow_removed_mask_master: fr_mask_master, + flow_removed_mask_slave: fr_mask_slave }) do - <> + << + Enums.flags_to_int(pin_mask_master, :packet_in_reason_mask)::32, + Enums.flags_to_int(pin_mask_slave, :packet_in_reason_mask)::32, + Enums.flags_to_int(ps_mask_master, :port_reason_mask)::32, + Enums.flags_to_int(ps_mask_slave, :port_reason_mask)::32, + Enums.flags_to_int(fr_mask_master, :flow_removed_reason_mask)::32, + Enums.flags_to_int(fr_mask_slave, :flow_removed_reason_mask)::32 + >> end end diff --git a/lib/openflow/table_mod.ex b/lib/openflow/table_mod.ex index f0e148e..4242b61 100644 --- a/lib/openflow/table_mod.ex +++ b/lib/openflow/table_mod.ex @@ -14,15 +14,11 @@ defmodule Openflow.TableMod do def new(options) when is_list(options) do %TableMod{ - xid: options[:xid] || 0, - table_id: options[:table_id] || 0 + xid: Keyword.get(options, :xid, 0), + table_id: Keyword.get(options, :table_id, 0) } end - def new(table_id) when is_integer(table_id) or is_atom(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} diff --git a/lib/ovsdb.ex b/lib/ovsdb.ex deleted file mode 100644 index 7a4c9d0..0000000 --- a/lib/ovsdb.ex +++ /dev/null @@ -1,22 +0,0 @@ -defmodule OVSDB do - @moduledoc false - - @behaviour :supervisor - - def start_link do - :supervisor.start_link({:local, __MODULE__}, __MODULE__, []) - end - - def init([]) do - child = OVSDB.OpenvSwitch - strategy = :simple_one_for_one - max_r = 1000 - intensity = 3600 - sup_flags = {strategy, max_r, intensity} - {:ok, {sup_flags, [{child, {child, :start_link, []}, :temporary, 1000, :worker, [child]}]}} - end - - def start_child(server) do - :supervisor.start_child(__MODULE__, [server]) - end -end diff --git a/lib/ovsdb/open_vswitch.ex b/lib/ovsdb/open_vswitch.ex deleted file mode 100644 index 43b15ca..0000000 --- a/lib/ovsdb/open_vswitch.ex +++ /dev/null @@ -1,174 +0,0 @@ -defmodule OVSDB.OpenvSwitch do - use GenServer - - defmodule State do - defstruct server: nil, - client_pid: nil, - monitor_pid: nil, - ovs_uuid: nil - end - - @database "Open_vSwitch" - - @open_vswitch "Open_vSwitch" - @interface "Interface" - @port "Port" - @controller "Controller" - @bridge "Bridge" - - def start_link(server) do - GenServer.start_link(__MODULE__, [server]) - end - - def find_by_name(pid, table, name) do - GenServer.call(pid, {:find_by_name, table, name}) - end - - def add_br(pid, bridge, options \\ []) do - br_uuids = GenServer.call(pid, {:sync_get, @open_vswitch, "bridges"}) - - add_br_options = [ - bridge: bridge, - controller: options[:controller] || "tcp:127.0.0.1:6653", - protocol: options[:protocol] || "OpenFlow13", - fail_mode: options[:fail_mode] || "standalone", - br_uuids: br_uuids - ] - - GenServer.call(pid, {:add_br, add_br_options}) - end - - def del_br(pid, bridge) do - case find_by_name(pid, @bridge, bridge) do - %{"_uuid" => uuid} -> - new_bridges = - case GenServer.call(pid, {:sync_get, @open_vswitch, "bridges"}) do - ["set", bridges] -> %{"bridges" => ["set", bridges -- [uuid]]} - ^uuid -> %{"bridges" => ["set", []]} - curr_bridges -> %{"bridges" => curr_bridges} - end - - GenServer.call(pid, {:del_br, new_bridges}) - - :not_found -> - {:error, :not_found} - end - end - - def init([server]) do - state = - server - |> String.to_charlist() - |> init_client - - {:ok, state} - end - - def handle_call({:sync_get, table, col_name}, _from, state) do - [%{"rows" => [%{^col_name => values} | _]}] = - [col_name] - |> :eovsdb_op.select(table, []) - |> xact(state.client_pid) - - {:reply, values, state} - end - - def handle_call({:add_br, options}, _from, state) do - %State{client_pid: pid, ovs_uuid: ovs} = state - br_iface = %{name: options[:bridge], type: :internal} - br_port = %{name: options[:bridge], interfaces: ["named-uuid", "brinterface"]} - controller = %{target: options[:controller]} - - new_bridge = %{ - name: options[:bridge], - ports: ["named-uuid", "brport"], - controller: ["named-uuid", "brcontroller"], - fail_mode: options[:fail_mode], - protocols: options[:protocol] - } - - named_uuid = ["named-uuid", "bridge"] - - new_bridges = - case options[:br_uuids] do - ["set", []] -> %{bridges: named_uuid} - ["set", bridges] -> %{bridges: ["set", bridges ++ [named_uuid]]} - ["uuid", _] = bridge -> %{bridges: ["set", [bridge] ++ [named_uuid]]} - end - - next_config = [{"next_cfg", "+=", 1}] - eq_ovs_uuid = [{"_uuid", "==", ovs}] - - replies = - xact( - [ - :eovsdb_op.insert(@interface, br_iface, "brinterface"), - :eovsdb_op.insert(@port, br_port, "brport"), - :eovsdb_op.insert(@controller, controller, "brcontroller"), - :eovsdb_op.insert(@bridge, new_bridge, "bridge"), - :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), - :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) - ], - pid - ) - - {:reply, replies, state} - end - - def handle_call({:del_br, new_bridges}, _from, state) do - %State{client_pid: pid, ovs_uuid: ovs} = state - eq_ovs_uuid = [{"_uuid", "==", ovs}] - next_config = [{"next_cfg", "+=", 1}] - - replies = - xact( - [ - :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), - :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) - ], - pid - ) - - {:reply, replies, state} - end - - def handle_call({:find_by_name, table, name}, _from, state) do - %State{client_pid: pid} = state - query = :eovsdb_op.select('*', table, [{"name", "==", name}]) - - reply = - case xact(query, pid) do - [%{"rows" => []}] -> :not_found - [%{"rows" => [row]}] -> row - end - - {:reply, reply, state} - end - - def handle_cast({:async_get, "_uuid"}, state) do - [%{"rows" => [%{"_uuid" => values} | _]}] = - ["_uuid"] - |> :eovsdb_op.select(@open_vswitch, []) - |> xact(state.client_pid) - - {:noreply, %{state | ovs_uuid: values}} - end - - # private functions - - defp init_client(server) do - {:ok, pid} = :eovsdb_client.connect(server, database: @database) - :eovsdb_client.regist_schema(pid) - :ok = GenServer.cast(self(), {:async_get, "_uuid"}) - %State{server: server, client_pid: pid} - end - - defp xact(query, pid) when is_list(query) do - {:ok, res} = :eovsdb_client.transaction(pid, query) - res - end - - defp xact(query, pid) when is_map(query) do - xact([query], pid) - end -end diff --git a/lib/tres.ex b/lib/tres.ex deleted file mode 100644 index 36ddade..0000000 --- a/lib/tres.ex +++ /dev/null @@ -1,2 +0,0 @@ -defmodule Tres do -end diff --git a/lib/tres/application.ex b/lib/tres/application.ex index f5ecd20..7bed3f0 100644 --- a/lib/tres/application.ex +++ b/lib/tres/application.ex @@ -4,16 +4,15 @@ defmodule Tres.Application do use Application alias Tres.SwitchRegistry + alias Tres.HandlerRegistry def start(_type, _args) do import Supervisor.Spec - {cb_mod, _cb_args} = Tres.Utils.get_callback_module() - children = [ worker(Registry, [[keys: :unique, name: SwitchRegistry]], id: SwitchRegistry), - supervisor(Tres.MessageHandlerSup, [cb_mod], id: MessageHandlerSup), - supervisor(OVSDB, [], id: OVSDB) + worker(Registry, [[keys: :unique, name: HandlerRegistry]], id: HandlerRegistry), + supervisor(Tres.MessageHandlerSup, [], id: MessageHandlerSup) ] opts = [strategy: :one_for_one, name: Tres.Supervisor] diff --git a/lib/tres/controller.ex b/lib/tres/controller.ex index 92d962e..323355e 100644 --- a/lib/tres/controller.ex +++ b/lib/tres/controller.ex @@ -1,14 +1,30 @@ defmodule Tres.Controller do def controller_helpers do - quote do + quote location: :keep do import Tres.SwitchRegistry, only: [ + lookup_handler_pid: 1, send_message: 2, + send_message: 3, + blocking_send_message: 2, get_current_xid: 1 ] use Tres.Messages use Tres.MessageHelper + + 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]]}, + restart: :permanent, + shutdown: 5000, + type: :worker, + modules: [__MODULE__] + } + end end end diff --git a/lib/tres/example_handler.ex b/lib/tres/example_handler.ex index 5e9a7b7..ae96954 100644 --- a/lib/tres/example_handler.ex +++ b/lib/tres/example_handler.ex @@ -7,83 +7,61 @@ defmodule Tres.ExampleHandler do defmodule State do defstruct datapath_id: nil, aux_id: nil, - conn_ref: nil + queue: :queue.new(), + client: nil + end + + # API functions + + @spec send(String.t(), map()) :: :ok + def send(datapath_id, msg), do: send_message(msg, datapath_id) + + @spec get(datapath_id :: String.t()) :: map() | nil + def get(datapath_id) do + datapath_id + |> lookup_handler_pid() + |> GenServer.call(:get) end def start_link(datapath, args) do GenServer.start_link(__MODULE__, [datapath, args]) end + # GenServer callbacks + def init([{datapath_id, aux_id}, _args]) do - info( - "[#{__MODULE__}] Switch Ready: " <> - "datapath_id: #{datapath_id} " <> "aux_id: #{aux_id} " <> "on #{inspect(self())}" - ) - - _ = send_desc_stats_request(datapath_id) - _ = send_port_desc_stats_request(datapath_id) - state = %State{datapath_id: datapath_id, aux_id: aux_id} - {:ok, state} + :ok = info("datapath connected: #{datapath_id}") + :ok = send_flow_mod_add(datapath_id, priority: 0) + {:ok, %State{datapath_id: datapath_id, aux_id: aux_id}} end - def handle_info(%PortDesc.Reply{datapath_id: datapath_id} = desc, state) do - handle_port_desc_stats_reply(desc, datapath_id) - {:noreply, state} + def handle_call(:get, from, %State{queue: {[], []}} = state) do + {:noreply, %{state | client: from}} end - def handle_info(%Desc.Reply{datapath_id: datapath_id} = desc, state) do - handle_desc_stats_reply(desc, datapath_id) - {:noreply, state} + def handle_call(:get, _from, %State{} = state) do + {{:value, msg}, new_queue} = :queue.out(state.queue) + {:reply, msg, %{state | queue: new_queue}} end - def handle_info({:switch_disconnected, reason}, state) do - :ok = - warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id} by #{reason}") + def handle_info(%{datapath_id: _datapath_id} = msg, %State{client: nil} = state) do + {:noreply, %{state | queue: :queue.in(msg, state.queue)}} + end + def handle_info(%{datapath_id: _datapath_id} = msg, state) do + GenServer.reply(state.client, msg) + {:noreply, %{state | client: nil}} + end + + def handle_info({:switch_disconnected, _reason}, state) do + :ok = warn("Switch disconnected") {:stop, :normal, state} end - def handle_info({:switch_hang, _datapath_id}, state) do - :ok = warn("[#{__MODULE__}] Switch possible hang: datapath_id: #{state.datapath_id}") - {:noreply, state} - end - - # `Catch all` function is required. def handle_info(info, state) do - :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}: #{state.datapath_id}") + :ok = warn("unhandled message #{inspect(info)}: #{state.datapath_id}") {:noreply, state} end # private functions - - defp send_desc_stats_request(datapath_id) do - Desc.Request.new() - |> send_message(datapath_id) - end - - defp send_port_desc_stats_request(datapath_id) do - PortDesc.Request.new() - |> send_message(datapath_id) - end - - defp handle_desc_stats_reply(desc, datapath_id) do - info( - "[#{__MODULE__}] Switch Desc: " <> - "mfr = #{desc.mfr_desc} " <> - "hw = #{desc.hw_desc} " <> "sw = #{desc.sw_desc} " <> "for #{datapath_id}" - ) - end - - defp handle_port_desc_stats_reply(port_desc, datapath_id) do - for port <- port_desc.ports do - info( - "[#{__MODULE__}] Switch has port: " <> - "number = #{port.number} " <> - "hw_addr = #{port.hw_addr} " <> - "name = #{port.name} " <> - "config = #{inspect(port.config)} " <> - "current_speed = #{port.current_speed} " <> "on #{datapath_id}" - ) - end - end end diff --git a/lib/tres/message_handler_sup.ex b/lib/tres/message_handler_sup.ex index b847bff..81a277f 100644 --- a/lib/tres/message_handler_sup.ex +++ b/lib/tres/message_handler_sup.ex @@ -1,17 +1,57 @@ defmodule Tres.MessageHandlerSup do use Supervisor - def start_link(cb_mod) do - Supervisor.start_link(__MODULE__, [cb_mod], name: __MODULE__) - end + # API functions - def init([cb_mod]) do - children = [worker(cb_mod, [], restart: :temporary, shutdown: 5000)] - supervise(children, strategy: :simple_one_for_one) - end - - def start_child({dpid, aux_id}) do + def start_child(dpid) do {_cb_mod, cb_args} = Tres.Utils.get_callback_module() - Supervisor.start_child(__MODULE__, [{dpid, aux_id}, cb_args]) + {:ok, pid} = Supervisor.start_child(__MODULE__, [dpid, cb_args]) + :ok = Tres.SwitchRegistry.register_handler_pid(dpid, pid) + {:ok, pid} + end + + def terminate_child(dpid) do + {cb_mod, _cb_args} = Tres.Utils.get_callback_module() + _ = Supervisor.terminate_child(__MODULE__, {cb_mod, dpid}) + _ = Supervisor.delete_child(__MODULE__, {cb_mod, dpid}) + end + + @spec count_handlers() :: non_neg_integer() + def count_handlers do + count_value = Supervisor.count_children(__MODULE__) + count_value[:active] + end + + def start_link do + Supervisor.start_link(__MODULE__, [], name: __MODULE__) + end + + # supervisor callback functions + + @impl Supervisor + def init(_init_args) do + children = [handler_spec()] + + Supervisor.init( + children, + strategy: :simple_one_for_one, + max_restarts: 10, + max_seconds: 10 + ) + end + + # private functions + + defp handler_spec do + {cb_mod, _cb_args} = Tres.Utils.get_callback_module() + + %{ + id: :undefined, + start: {cb_mod, :start_link, []}, + restart: :temporary, + shutdown: 5000, + type: :worker, + modules: [cb_mod] + } end end diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index b9b1655..e3aa398 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -17,7 +17,20 @@ defmodule Tres.MessageHelper do instructions: options[:instructions] || [] } - send_message(flow_mod, datapath_id) + case options[:bundle_id] do + nil -> + send_message(flow_mod, datapath_id, Keyword.get(options, :blocking, false)) + + bundle_id when is_integer(bundle_id) -> + bundle = + Openflow.OnfBundleAdd.new( + bundle_id: bundle_id, + flags: options[:bundle_flags] || [], + message: flow_mod + ) + + send_message(bundle, datapath_id, Keyword.get(options, :blocking, false)) + end end defp send_flow_mod_modify(datapath_id, options \\ []) do @@ -36,7 +49,20 @@ defmodule Tres.MessageHelper do instructions: options[:instructions] || [] } - send_message(flow_mod, datapath_id) + case options[:bundle_id] do + nil -> + send_message(flow_mod, datapath_id, Keyword.get(options, :blocking, false)) + + bundle_id when is_integer(bundle_id) -> + bundle = + Openflow.OnfBundleAdd.new( + bundle_id: bundle_id, + flags: options[:bundle_flags] || [], + message: flow_mod + ) + + send_message(bundle, datapath_id, Keyword.get(options, :blocking, false)) + end end defp send_flow_mod_delete(datapath_id, options \\ []) do @@ -53,10 +79,59 @@ defmodule Tres.MessageHelper do match: options[:match] || Openflow.Match.new() } - send_message(flow_mod, datapath_id) + case options[:bundle_id] do + nil -> + send_message(flow_mod, datapath_id, Keyword.get(options, :blocking, false)) + + bundle_id when is_integer(bundle_id) -> + bundle = + Openflow.OnfBundleAdd.new( + bundle_id: bundle_id, + flags: options[:bundle_flags] || [], + message: flow_mod + ) + + send_message(bundle, datapath_id, Keyword.get(options, :blocking, false)) + end end - defp send_packet_out(datapath_id, options \\ []) do + defp send_packet_out(options \\ []) do + case options[:packet_in] do + %Openflow.PacketIn{} = pin -> + send_packet_out( + pin.datapath_id, + xid: options[:xid] || 0, + buffer_id: options[:buffer_id], + in_port: options[:in_port], + actions: options[:actions], + data: options[:data] || pin.data, + blocking: options[:blocking] + ) + + %Openflow.NxPacketIn2{continuation_bridge: nil} = pin -> + send_packet_out( + pin.datapath_id, + xid: options[:xid] || 0, + buffer_id: options[:buffer_id], + in_port: options[:in_port], + actions: options[:actions], + data: options[:data] || pin.packet, + blocking: options[:blocking] + ) + + %Openflow.NxPacketIn2{} = pin -> + send_nx_resume( + pin.datapath_id, + xid: options[:xid] || 0, + packet_in: options[:packet_in], + packet: options[:data] || pin.packet, + metadata: options[:metadata] || pin.metadata, + blocking: options[:blocking] + ) + end + end + + defp send_packet_out(datapath_id, options) do packet_out = %Openflow.PacketOut{ xid: options[:xid] || 0, buffer_id: options[:buffer_id] || :no_buffer, @@ -65,7 +140,20 @@ defmodule Tres.MessageHelper do data: options[:data] || "" } - send_message(packet_out, datapath_id) + case options[:bundle_id] do + nil -> + send_message(packet_out, datapath_id, Keyword.get(options, :blocking, false)) + + bundle_id when is_integer(bundle_id) -> + bundle = + Openflow.OnfBundleAdd.new( + bundle_id: bundle_id, + flags: options[:bundle_flags], + message: packet_out + ) + + send_message(bundle, datapath_id, Keyword.get(options, :blocking, false)) + end end defp send_group_mod_add(datapath_id, options \\ []) do @@ -78,7 +166,20 @@ defmodule Tres.MessageHelper do buckets: options[:buckets] || [] ) - send_message(group_mod, datapath_id) + case options[:bundle_id] do + nil -> + send_message(group_mod, datapath_id, Keyword.get(options, :blocking, false)) + + bundle_id when is_integer(bundle_id) -> + bundle = + Openflow.OnfBundleAdd.new( + bundle_id: bundle_id, + flags: options[:bundle_flags] || [], + message: group_mod + ) + + send_message(bundle, datapath_id, Keyword.get(options, :blocking, false)) + end end defp send_group_mod_delete(datapath_id, options \\ []) do @@ -89,7 +190,20 @@ defmodule Tres.MessageHelper do group_id: options[:group_id] || :all ) - send_message(group_mod, datapath_id) + case options[:bundle_id] do + nil -> + send_message(group_mod, datapath_id, Keyword.get(options, :blocking, false)) + + bundle_id when is_integer(bundle_id) -> + bundle = + Openflow.OnfBundleAdd.new( + bundle_id: bundle_id, + flags: options[:bundle_flags] || [], + message: group_mod + ) + + send_message(bundle, datapath_id, Keyword.get(options, :blocking, false)) + end end defp send_group_mod_modify(datapath_id, options \\ []) do @@ -102,7 +216,20 @@ defmodule Tres.MessageHelper do buckets: options[:buckets] || [] ) - send_message(group_mod, datapath_id) + case options[:bundle_id] do + nil -> + send_message(group_mod, datapath_id, Keyword.get(options, :blocking, false)) + + bundle_id when is_integer(bundle_id) -> + bundle = + Openflow.OnfBundleAdd.new( + bundle_id: bundle_id, + flags: options[:bundle_flags] || [], + message: group_mod + ) + + send_message(bundle, datapath_id, Keyword.get(options, :blocking, false)) + end end defp send_role_request(datapath_id, options) do @@ -113,7 +240,75 @@ defmodule Tres.MessageHelper do generation_id: options[:generation_id] || 0 ) - send_message(role_request, datapath_id) + send_message(role_request, datapath_id, Keyword.get(options, :blocking, false)) + end + + defp send_meter_mod_add(datapath_id, options \\ []) do + meter_mod = + Openflow.MeterMod.new( + xid: options[:xid] || 0, + command: :add, + flags: options[:flags] || [], + meter_id: options[:meter_id] || 0, + bands: options[:bands] || [] + ) + + send_message(meter_mod, datapath_id, options[:blocking] || false) + end + + defp send_meter_mod_modify(datapath_id, options \\ []) do + meter_mod = + Openflow.MeterMod.new( + xid: options[:xid] || 0, + command: :modify, + flags: options[:flags] || [], + meter_id: options[:meter_id] || 0, + bands: options[:bands] || [] + ) + + send_message(meter_mod, datapath_id, options[:blocking] || false) + end + + defp send_meter_mod_delete(datapath_id, options \\ []) do + meter_mod = + Openflow.MeterMod.new( + xid: options[:xid] || 0, + command: :delete, + meter_id: options[:meter_id] || 0 + ) + + send_message(meter_mod, datapath_id, options[:blocking] || false) + end + + defp send_nx_resume(datapath_id, options) do + resume = Openflow.NxResume.new(options) + send_message(resume, datapath_id, options[:blocking] || false) + end + + # ONF Bundle Control + + defp onf_bundle_open(datapath_id, options \\ []) do + options2 = Keyword.merge(options, type: :open_request) + bundle = Openflow.OnfBundleControl.new(options2) + send_message(bundle, datapath_id) + end + + defp onf_bundle_close(datapath_id, options) do + options2 = Keyword.merge(options, type: :close_request) + bundle = Openflow.OnfBundleControl.new(options2) + send_message(bundle, datapath_id) + end + + defp onf_bundle_commit(datapath_id, options) do + options2 = Keyword.merge(options, type: :commit_request) + bundle = Openflow.OnfBundleControl.new(options2) + send_message(bundle, datapath_id) + end + + defp onf_bundle_discard(datapath_id, options) do + options2 = Keyword.merge(options, type: :discard_request) + bundle = Openflow.OnfBundleControl.new(options2) + send_message(bundle, datapath_id) end end end diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 033809b..3074267 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -10,13 +10,12 @@ defmodule Tres.SecureChannel do alias Tres.MessageHandlerSup @process_flags [ - trap_exit: true, message_queue_data: :off_heap ] @supported_version 4 - @hello_handshake_timeout 1000 + @hello_handshake_timeout 3000 @features_handshake_timeout 1000 @ping_timeout 5000 # @transaction_timeout 5000 @@ -37,11 +36,11 @@ defmodule Tres.SecureChannel do state_data = init_secure_channel(ref, socket, transport) debug( - "[#{__MODULE__}] TCP connected to Switch on" <> + "TCP connected to Switch on" <> " #{state_data.ip_addr}:#{state_data.port}" <> " on #{inspect(self())}" ) - :gen_statem.enter_loop(__MODULE__, [], :INIT, state_data, []) + :gen_statem.enter_loop(__MODULE__, [debug: []], :INIT, state_data, []) end # TCP handler @@ -56,6 +55,7 @@ defmodule Tres.SecureChannel do end def handle_event(:info, {:tcp_closed, socket}, _state, %State{socket: socket} = state_data) do + :ok = debug("TCP disconnected with #{state_data.ip_addr}:#{state_data.port}") close_connection(:tcp_closed, state_data) end @@ -102,7 +102,7 @@ defmodule Tres.SecureChannel do aux_id: aux_id, xact_kv_ref: kv_ref }) do - warn("[#{__MODULE__}] termiate: #{inspect(reason)} state = #{inspect(state)}") + debug("termiate: #{inspect(reason)} state = #{inspect(state)}") true = XACT_KV.drop(kv_ref) :ok = SwitchRegistry.unregister({datapath_id, aux_id}) end @@ -137,9 +137,7 @@ defmodule Tres.SecureChannel do # INIT state defp handle_INIT(:enter, _old_state, state_data) do - debug( - "[#{__MODULE__}] 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,25 +151,20 @@ defmodule Tres.SecureChannel do end defp handle_INIT(:internal, message, _state_data) do - debug( - "[#{__MODULE__}] 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( - "[#{__MODULE__}] 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 defp handle_CONNECTING(:enter, :WAITING, state_data) do - debug("[#{__MODULE__}] Re-entered features handshake") + debug("Re-entered features handshake") initiate_features_handshake(state_data) end @@ -185,7 +178,7 @@ defmodule Tres.SecureChannel do state_data ) do debug( - "[#{__MODULE__}] Switch connected " <> + "Switch connected " <> "datapath_id: #{features.datapath_id}" <> " auxiliary_id: #{features.aux_id}" ) @@ -195,8 +188,7 @@ defmodule Tres.SecureChannel do defp handle_CONNECTING(:internal, {:openflow, message}, _state_data) do debug( - "[#{__MODULE__}] Features handshake in progress," <> - " dropping message: #{inspect(message.__struct__)}" + "Features handshake in progress," <> " dropping message: #{inspect(message.__struct__)}" ) :keep_state_and_data @@ -208,6 +200,8 @@ 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() @@ -228,6 +222,12 @@ defmodule Tres.SecureChannel do handle_ping_timeout(state_data, :CONNECTED) end + defp handle_CONNECTED(:info, :xactdb_ageout, state_data) do + _num_deleted = XACT_KV.aged_out(state_data.xact_kv_ref) + _tref = schedule_xactdb_ageout() + :keep_state_and_data + end + defp handle_CONNECTED( :internal, {:openflow, %Openflow.Echo.Reply{xid: xid}}, @@ -262,6 +262,11 @@ defmodule Tres.SecureChannel do :keep_state_and_data end + defp handle_CONNECTED({:call, from}, {:send_message, message}, state_data) do + xactional_send_message({from, message}, state_data) + :keep_state_and_data + end + defp handle_CONNECTED({:call, from}, :get_xid, state_data) do xid = State.get_transaction_id(state_data.xid) {:keep_state_and_data, [{:reply, from, {:ok, xid}}]} @@ -281,7 +286,7 @@ defmodule Tres.SecureChannel do # WATING state defp handle_WATING(:enter, _state, state_data) do - warn("[#{__MODULE__}] Possible HANG Detected on datapath_id: #{state_data.datapath_id} !") + debug("Possible HANG Detected on datapath_id: #{state_data.datapath_id} !") %State{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data send(handler_pid, {:switch_hang, {dpid, aux_id}}) start_periodic_idle_check() @@ -298,6 +303,12 @@ defmodule Tres.SecureChannel do handle_ping_timeout(state_data, :WAITING) end + defp handle_WATING(:info, :xactdb_ageout, state_data) do + _num_deleted = XACT_KV.aged_out(state_data.xact_kv_ref) + _tref = schedule_xactdb_ageout() + :keep_state_and_data + end + defp handle_WATING(:internal, {:openflow, message}, state_data) do %State{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data send(handler_pid, %{message | datapath_id: dpid, aux_id: aux_id}) @@ -306,7 +317,7 @@ defmodule Tres.SecureChannel do defp handle_WATING(type, message, state_data) when type == :cast or type == :call do - debug("[#{__MODULE__}] Postponed: #{inspect(message)}, now WATING") + debug("Postponed: #{inspect(message)}, now WATING") {:keep_state, state_data, [{:postpone, true}]} end @@ -314,16 +325,16 @@ defmodule Tres.SecureChannel do {:keep_state, %{state_data | ping_fail_count: 0}, Enum.reverse(actions)} end - defp handle_packet(packet, %State{buffer: buffer} = state_data, state, actions) do + defp handle_packet( + packet, + %State{buffer: buffer, datapath_id: dpid} = 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]) @@ -331,14 +342,17 @@ defmodule Tres.SecureChannel do {:error, :binary_too_small} -> handle_packet("", %{state_data | buffer: binary}, state, actions) - {:error, _reason} -> - handle_packet("", state_data, state, actions) + {:error, {:malformed_packet, {reason, st}}} -> + :ok = + debug("malformed packet #{dpid} reason: #{inspect(reason)} stack_trace: #{inspect(st)}") + + handle_packet("", %{state_data | buffer: ""}, state, actions) end end defp handle_message(_in_xact = true, message, state_data) do case XACT_KV.get(state_data.xact_kv_ref, message.xid) do - [{:xact_entry, _xid, prev_message, _orig} | _] -> + [{:xact_entry, _xid, prev_message, _orig, _from, _inserted_at} | _] -> new_message = Openflow.append_body(prev_message, message) XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message) @@ -359,11 +373,18 @@ defmodule Tres.SecureChannel do pop_action_queue(state_data) end - defp process_xact_entry({:xact_entry, xid, message, _orig}, state_data) do + defp process_xact_entry({:xact_entry, xid, message, _orig, nil, _inserted_at}, state_data) 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 + 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) + end + defp pop_action_queue(%State{action_queue: queue} = state_data) do {next_actions, new_queue} = case Queue.out(queue) do @@ -384,10 +405,10 @@ defmodule Tres.SecureChannel do end defp handle_hello_handshake_1(hello, state_data) do - maybe_cancel_timer(state_data.timer_ref) State.set_transaction_id(state_data.xid, hello.xid) if Openflow.Hello.supported_version?(hello) do + :ok = maybe_cancel_timer(state_data.timer_ref) {:next_state, :CONNECTING, %{state_data | timer_ref: nil}} else close_connection(:failed_version_negotiation, state_data) @@ -513,13 +534,36 @@ defmodule Tres.SecureChannel do send_message(messages, state_data) end + defp xactional_send_message({from, %{xid: 0} = message}, state_data) do + xid = State.increment_transaction_id(state_data.xid) + + messages = [ + %{message | xid: xid}, + %{Openflow.Barrier.Request.new() | xid: xid} + ] + + XACT_KV.insert(state_data.xact_kv_ref, xid, message, from) + send_message(messages, state_data) + end + + defp xactional_send_message({from, %{xid: xid} = message}, state_data) do + _ = State.set_transaction_id(state_data.xid, xid) + + messages = [ + %{message | xid: xid}, + %{Openflow.Barrier.Request.new() | xid: xid} + ] + + XACT_KV.insert(state_data.xact_kv_ref, xid, message, from) + send_message(messages, state_data) + end + defp send_message(message, %State{socket: socket, transport: transport}) do if is_list(message) do for message <- message, - do: - debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + do: debug("Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") else - debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + debug("Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") end Tres.Utils.send_message(message, socket, transport) @@ -532,6 +576,11 @@ defmodule Tres.SecureChannel do :ok end + @spec schedule_xactdb_ageout() :: reference() + defp schedule_xactdb_ageout do + Process.send_after(self(), :xactdb_ageout, 1_000) + end + defp handle_signal( {:DOWN, mon_ref, :process, _main_pid, reason}, %State{main_monitor_ref: mon_ref} = state_data @@ -546,69 +595,53 @@ defmodule Tres.SecureChannel 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") + debug("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") + debug("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") + defp close_connection(:features_handshake_timeout, state_data) do + debug("connection terminated: Features handshake timed out") {:stop, :normal, %{state_data | socket: nil}} end - defp close_connection(:handler_error = disconnected_reason, state_data) do - warn("[#{__MODULE__}] connection terminated: Got handler error") - %State{handler_pid: handler_pid} = state_data - send(handler_pid, {:switch_disconnected, disconnected_reason}) + defp close_connection(:handler_error, state_data) do + debug("connection terminated: Got handler error") {:stop, :normal, %{state_data | socket: nil}} end - defp close_connection(:ping_failed = disconnected_reason, state_data) do - warn("[#{__MODULE__}] connection terminated: Exceeded to max_ping_fail_count") - %State{handler_pid: handler_pid} = state_data - send(handler_pid, {:switch_disconnected, disconnected_reason}) + defp close_connection(:ping_failed, state_data) do + debug("connection terminated: Exceeded to max_ping_fail_count") + _ = send(state_data.handler_pid, {:switch_disconnected, :ping_failed}) {:stop, :normal, %{state_data | socket: nil}} end - defp close_connection({:main_closed = disconnected_reason, reason}, state_data) do - warn("[#{__MODULE__}] connection terminated: Main connection down by #{reason}") - %State{handler_pid: handler_pid} = state_data - send(handler_pid, {:switch_disconnected, disconnected_reason}) + defp close_connection({:main_closed, reason}, state_data) do + debug("connection terminated: Main connection down by #{inspect(reason)}") + _ = send(state_data.handler_pid, {:switch_disconnected, :main_closed}) {:stop, :normal, %{state_data | socket: nil}} end - defp close_connection({:handler_down = _disconnected_reason, reason}, state_data) do - warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}") + defp close_connection({:handler_down, reason}, state_data) do + debug("connection terminated: Handler process down by #{inspect(reason)}") + _ = send(state_data.handler_pid, {:switch_disconnected, :handler_error}) {:stop, :normal, %{state_data | socket: nil}} end - defp close_connection({:trap_detected = disconnected_reason, reason}, state_data) do - warn("[#{__MODULE__}] connection terminated: Trapped by #{reason}") - %State{handler_pid: handler_pid} = state_data - send(handler_pid, {:switch_disconnected, disconnected_reason}) + defp close_connection(:tcp_closed, state_data) do + debug("connection terminated: TCP Closed by peer") + _ = send(state_data.handler_pid, {:switch_disconnected, :tcp_closed}) {:stop, :normal, %{state_data | socket: nil}} end - defp close_connection(:tcp_closed = disconnected_reason, state_data) do - warn("[#{__MODULE__}] connection terminated: TCP Closed by peer") - %State{handler_pid: handler_pid} = state_data - send(handler_pid, {:switch_disconnected, disconnected_reason}) - {:stop, :normal, %{state_data | socket: nil}} - end - - defp close_connection({:tcp_error, reason} = disconnected_reason, state_data) do - warn("[#{__MODULE__}] connection terminated: TCP Error occured: #{reason}") - %State{handler_pid: handler_pid} = state_data - send(handler_pid, {:switch_disconnected, disconnected_reason}) + defp close_connection(close_reason, state_data) do + debug("connection terminated: reason: #{inspect(close_reason)}") + _ = send(state_data.handler_pid, {:switch_disconnected, close_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 index 6ab8283..483b5cd 100644 --- a/lib/tres/secure_channel_state.ex +++ b/lib/tres/secure_channel_state.ex @@ -1,4 +1,6 @@ defmodule Tres.SecureChannelState do + use Bitwise + defstruct( handler_pid: nil, handler_ref: nil, @@ -43,26 +45,27 @@ defmodule Tres.SecureChannelState do } end - def increment_transaction_id(table_ref) do - :ets.update_counter(table_ref, :datapath_xid, {2, 1, 0xFFFFFFFF, 0}) + @spec increment_transaction_id(:counters.counters_ref()) :: integer() + def increment_transaction_id(counter_ref) do + :ok = :counters.add(counter_ref, 1, 1) + get_transaction_id(counter_ref) end - def set_transaction_id(table_ref, xid) do - :ets.insert(table_ref, {:datapath_xid, xid}) + @spec set_transaction_id(:counters.counters_ref(), integer()) :: integer() + def set_transaction_id(counter_ref, xid) do + :ok = :counters.put(counter_ref, 1, xid) + get_transaction_id(counter_ref) end - def get_transaction_id(table_ref) do - case :ets.lookup(table_ref, :datapath_xid) do - [{_, xid} | _] -> xid - end + @spec get_transaction_id(:counters.counters_ref()) :: integer() + def get_transaction_id(counter_ref) do + :counters.get(counter_ref, 1) &&& 0xFFFFFFFF end # private functions @spec create_counter() :: reference() defp create_counter do - table_ref = :ets.new(:xid_counter, [:set, :private]) - _ = :ets.insert(table_ref, {:datapath_xid, 0}) - {:ok, table_ref} + {:ok, :counters.new(1, [])} end end diff --git a/lib/tres/switch_registry.ex b/lib/tres/switch_registry.ex index e901fe5..848f4a0 100644 --- a/lib/tres/switch_registry.ex +++ b/lib/tres/switch_registry.ex @@ -1,4 +1,32 @@ defmodule Tres.SwitchRegistry do + @moduledoc """ + Dispatcher + """ + + # For DatapathHandler + + def register_handler_pid({_dpid, _aux_id} = datapath_id, pid) do + case Registry.register(Tres.HandlerRegistry, datapath_id, pid) do + {:ok, _owner} -> + :ok + + {:error, {:already_registered, _owner}} -> + :ok + end + end + + def lookup_handler_pid({_dpid, _aux_id} = datapath_id) do + case Registry.lookup(Tres.HandlerRegistry, datapath_id) do + [{_owner, pid}] -> pid + [] -> nil + end + end + + def lookup_handler_pid(datapath_id) when is_binary(datapath_id), + do: lookup_handler_pid({datapath_id, 0}) + + # For Datapath + def register({_dpid, _aux_id} = datapath_id) do {:ok, _} = Registry.register(__MODULE__, datapath_id, []) end @@ -18,6 +46,14 @@ defmodule Tres.SwitchRegistry do lookup_pid({datapath_id, 0}) end + 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 + def send_message(message, {_dpid, _aux_id} = datapath_id) do Registry.dispatch(__MODULE__, datapath_id, &do_send_message(&1, message)) end @@ -26,9 +62,20 @@ defmodule Tres.SwitchRegistry do send_message(message, {dpid, 0}) end + def blocking_send_message(message, {_dpid, _aux_id} = datapath_id) do + datapath_id + |> lookup_pid + |> call({:send_message, message}, 5_000) + end + + def blocking_send_message(message, dpid) when is_binary(dpid) do + blocking_send_message(message, {dpid, 0}) + end + def get_current_xid({_dpid, _aux_id} = datapath_id) do - [{pid, _} | _] = Registry.lookup(__MODULE__, datapath_id) - :gen_statem.call(pid, :get_xid, 1000) + datapath_id + |> lookup_pid + |> call(:get_xid, 1_000) end def get_current_xid(datapath_id) do @@ -43,6 +90,17 @@ defmodule Tres.SwitchRegistry do # private function + defp call(nil, _, _) do + {:error, :not_found} + end + + defp call(pid, msg, timeout) when is_pid(pid) do + :gen_statem.call(pid, msg, timeout) + catch + :exit, {:timeout, _} -> + {:error, :timeout} + end + defp do_send_message(entries, message) do for {pid, _} <- entries, do: :gen_statem.cast(pid, {:send_message, message}) end diff --git a/lib/tres/utils.ex b/lib/tres/utils.ex index 5bc6c12..75af3b9 100644 --- a/lib/tres/utils.ex +++ b/lib/tres/utils.ex @@ -13,21 +13,21 @@ defmodule Tres.Utils do end def start_openflow_listener do - max_connections = get_config(:max_connections, @default_max_connections) - num_acceptors = get_config(:num_acceptors, @default_num_acceptors) - port = get_config(:port, @default_openflow_port) - options = [max_connections: max_connections, num_acceptors: num_acceptors, port: port] - :ranch.start_listener(Tres, :ranch_tcp, options, @connection_manager, []) + :ranch.start_listener( + _ref = Tres, + _trasport = :ranch_tcp, + _transport_opts = transport_options(), + _protocol = @connection_manager, + _protocol_opts = [] + ) end def send_message(message, socket, transport) do - try do - packet = Openflow.to_binary(message) - transport.send(socket, packet) - catch - _class, _reason -> - error("[#{__MODULE__}] Unencodable error: #{inspect(message)}") - end + packet = Openflow.to_binary(message) + transport.send(socket, packet) + catch + _class, _reason -> + error("[#{__MODULE__}] Unencodable error: #{inspect(message)}") end def is_multipart?(message) do @@ -54,7 +54,32 @@ defmodule Tres.Utils do end # private functions + defp get_config(item, default) do Application.get_env(:tres, item, default) end + + defp transport_options do + %{ + max_connections: max_connections(), + num_acceptors: num_acceptors(), + logger: :logger, + socket_opts: socket_opts() + } + end + + defp socket_opts do + [ + port: port() + ] + end + + defp port, + do: get_config(:port, @default_openflow_port) + + defp max_connections, + do: get_config(:max_connections, @default_max_connections) + + defp num_acceptors, + do: get_config(:num_acceptors, @default_num_acceptors) end diff --git a/mix.exs b/mix.exs index a2a0160..cf375e3 100644 --- a/mix.exs +++ b/mix.exs @@ -1,30 +1,175 @@ defmodule Tres.Mixfile do use Mix.Project + @description """ + OpenFlow controller library for Elixir + """ + def project do [ app: :tres, - version: "0.1.0", - elixir: "~> 1.5", + version: "0.1.1", + description: @description, + elixir: "~> 1.8", + package: package(), start_permanent: Mix.env() == :prod, - escript: [main_module: Openflow.EnumGen, name: :enum_gen, path: "bin/enum_gen"], compilers: [:erlang] ++ Mix.compilers(), deps: deps(), - aliases: [test: "test --no-start", compile: ["escript.build"]] + aliases: [ + test: [ + "test" + ], + generate_enum: [ + "run priv/openflow_enum_gen.exs", + "format" + ] + ], + docs: docs(), + test_coverage: [tool: ExCoveralls], + preferred_cli_env: [ + coveralls: :test, + "coveralls.detail": :test, + "coveralls.post": :test, + "coveralls.html": :test + ] ] end # Run "mix help compile.app" to learn about applications. def application do - [extra_applications: [:logger, :ranch, :eovsdb], mod: {Tres.Application, []}] + [extra_applications: [:logger], mod: {Tres.Application, []}] end # Run "mix help deps" to learn about dependencies. defp deps do [ - {:ranch, "~> 1.4.0"}, - {:eovsdb, github: "shun159/eovsdb", branch: "master"}, - {:epcap, github: "msantos/epcap", branch: "master", only: :test} + {:ranch, "~> 1.7.1"}, + {:excoveralls, "~> 0.10", only: :test}, + # Document + {:earmark, "~> 1.2.6", only: :dev, runtime: false}, + {:ex_doc, "~> 0.19", only: :dev, runtime: false} + ] + end + + defp docs do + [ + groups_for_modules: groups_for_modules(), + formatters: ["html"] + ] + end + + defp groups_for_modules do + [ + Actions: [ + Openflow.Action.Output, + Openflow.Action.CopyTtlOut, + Openflow.Action.CopyTtlIn, + Openflow.Action.SetMplsTtl, + Openflow.Action.DecMplsTtl, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.PushMpls, + Openflow.Action.PopMpls, + Openflow.Action.SetQueue, + Openflow.Action.Group, + Openflow.Action.SetNwTtl, + Openflow.Action.DecNwTtl, + Openflow.Action.SetField, + Openflow.Action.PushPbb, + Openflow.Action.PopPbb, + Openflow.Action.NxResubmit, + Openflow.Action.NxSetTunnel, + Openflow.Action.NxRegMove, + Openflow.Action.NxRegLoad, + Openflow.Action.NxNote, + Openflow.Action.NxSetTunnel64, + Openflow.Action.NxMultipath, + Openflow.Action.NxBundle, + Openflow.Action.NxBundleLoad, + Openflow.Action.NxResubmitTable, + Openflow.Action.NxOutputReg, + Openflow.Action.NxLearn, + Openflow.Action.NxExit, + Openflow.Action.NxDecTtl, + Openflow.Action.NxFinTimeout, + Openflow.Action.NxController, + Openflow.Action.NxDecTtlCntIds, + Openflow.Action.NxWriteMetadata, + Openflow.Action.NxPushMpls, + Openflow.Action.NxPopMpls, + Openflow.Action.NxStackPush, + Openflow.Action.NxStackPop, + Openflow.Action.NxSample, + Openflow.Action.NxOutputReg2, + Openflow.Action.NxRegLoad2, + Openflow.Action.NxConjunction, + Openflow.Action.NxConntrack, + Openflow.Action.NxNat, + Openflow.Action.NxController2, + Openflow.Action.NxSample2, + Openflow.Action.NxOutputTrunc, + Openflow.Action.NxGroup, + Openflow.Action.NxSample3, + Openflow.Action.NxClone, + Openflow.Action.NxCtClear, + Openflow.Action.NxResubmitTableCt, + Openflow.Action.NxLearn2 + ], + OpenflowStandardMessages: [ + Openflow.Hello, + Openflow.ErrorMsg, + Openflow.Echo.Request, + Openflow.Echo.Reply, + Openflow.Experimenter, + Openflow.Features.Request, + Openflow.Features.Reply, + Openflow.GetConfig.Request, + Openflow.GetConfig.Reply, + Openflow.SetConfig, + Openflow.PacketIn, + Openflow.FlowRemoved, + Openflow.PortStatus, + Openflow.PacketOut, + Openflow.FlowMod, + Openflow.GroupMod, + Openflow.PortMod, + Openflow.TableMod, + Openflow.Multipart.Request, + Openflow.Multipart.Reply, + Openflow.Barrier.Request, + Openflow.Barrier.Reply, + Openflow.Role.Request, + Openflow.Role.Reply, + Openflow.GetAsync.Request, + Openflow.GetAsync.Reply, + Openflow.SetAsync, + Openflow.MeterMod + ], + ExtendedOpenflowMessages: [ + Openflow.NxSetPacketInFormat, + Openflow.NxSetControllerId, + Openflow.NxFlowMonitor.Cancel, + Openflow.NxFlowMonitor.Paused, + Openflow.NxFlowMonitor.Resumed, + Openflow.NxTLVTableMod, + Openflow.NxTLVTable.Request, + Openflow.NxTLVTable.Reply, + Openflow.NxSetAsyncConfig2, + Openflow.NxResume, + Openflow.NxCtFlushZone, + Openflow.NxPacketIn2, + Openflow.OnfBundleControl, + Openflow.OnfBundleAddMessage + ] + ] + end + + defp package do + [ + maintainers: ["Eishun Kondoh"], + licenses: ["SUSHI-WARE", "Apache 2.0"], + links: %{"GitHub" => "https://github.com/shun159/tres"}, + files: ~w(.formatter.exs mix.exs README.md lib priv src) ] end end diff --git a/mix.lock b/mix.lock index 7fb1000..43875ca 100644 --- a/mix.lock +++ b/mix.lock @@ -1,10 +1,27 @@ -%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, +%{ + "binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, + "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"}, "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, "epcap": {:git, "https://github.com/msantos/epcap.git", "9566f0420a4dcf1292c1a1afd9339c35dbdfd041", [branch: "master"]}, + "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.10.6", "e2b9718c9d8e3ef90bc22278c3f76c850a9f9116faf4ebe9678063310742edc2", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "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"}, - "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, - "pkt": {:git, "https://github.com/msantos/pkt.git", "3afb1967f34324c1dec5035a6e36232da815c2e6", [ref: "3afb196"]}, - "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"}, - "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}} + "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "jsone": {:git, "https://github.com/sile/jsone.git", "b23d312a5ed051ea7ad0989a9f2cb1a9c3f9a502", [tag: "1.4.6"]}, + "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, + "pkt": {:git, "https://github.com/msantos/pkt.git", "ff0e9a7d28cdae941bce935602cd252cad1ea296", [branch: "master"]}, + "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, + "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}, +} diff --git a/priv/openflow_enum_gen.exs b/priv/openflow_enum_gen.exs new file mode 100644 index 0000000..6d1da7a --- /dev/null +++ b/priv/openflow_enum_gen.exs @@ -0,0 +1,1199 @@ +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.Port.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.Port.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, + multipart_request_timeout: 14, + multipart_reply_timeout: 15, + nxm_invalid: 256, + nxm_bad_type: 257, + must_be_zero: 515, + bad_reason: 516, + flow_monitor_bad_event: 520, + undecodable_error: 521, + resume_not_supported: 533, + resume_stale: 534 + ], + 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, + must_be_zero: 256, + conntrack_datapath_support: 265, + bad_conjunction: 526 + ], + 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, + dup_inst: 256 + ], + 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, + conntrack_datapath_support: 264 + ], + 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, + unknown_bucket: 15, + bucket_exists: 16 + ], + 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: [ + fragment_drop: 1 <<< 0, + fragment_reassemble: 1 <<< 1 + ], + controller_max_len: [ + max: 0xFFE5, + no_buffer: 0xFFFF + ], + experimenter_oxm_vendors: [ + nicira_ext_match: 0x00002320, + hp_ext_match: 0x00002428, + 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: [ + # Beginning of a new connection. + new: 1 <<< 0, + # Part of an existing connection. + est: 1 <<< 1, + # Related to an established connection. + rel: 1 <<< 2, + # Flow is in the reply direction. + rep: 1 <<< 3, + # Could not track connection. + inv: 1 <<< 4, + # Conntrack has occurred. + trk: 1 <<< 5, + # Packet's source address/port was mangled by NAT. + snat: 1 <<< 6, + # Packet's destination address/port was mangled by NAT. + dnat: 1 <<< 7 + ], + 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 + ], + hp_ext_match: [ + hp_udp_src_port_range: 0, + hp_udp_dst_port_range: 1, + hp_tcp_src_port_range: 2, + hp_tcp_dst_port_range: 3, + hp_tcp_flags: 4, + hp_custom_1: 5, + hp_custom_2: 6, + hp_custom_3: 7, + hp_custom_4: 8 + ], + hp_custom_match_type: [ + l2_start: 1, + l3_start: 2, + l4_start: 3 + ], + onf_ext_match: [ + onf_tcp_flags: 42, + onf_actset_output: 43, + onf_pbb_uca: 2560 + ], + 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 + ], + packet_in_reason_mask: [ + no_match: 1 <<< 0, + action: 1 <<< 1, + invalid_ttl: 1 <<< 2, + action_set: 1 <<< 3, + group: 1 <<< 4, + packet_out: 1 <<< 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 + ], + flow_removed_reason_mask: [ + idle_timeout: 1 <<< 0, + hard_timeout: 1 <<< 1, + delete: 1 <<< 2, + group_delete: 1 <<< 3, + meter_delete: 1 <<< 4, + eviction: 1 <<< 5 + ], + port_reason: [ + add: 0, + delete: 1, + modify: 2 + ], + port_reason_mask: [ + add: 1 <<< 0, + delete: 1 <<< 1, + modify: 1 <<< 2 + ], + group_mod_command: [ + add: 0, + modify: 1, + delete: 2 + ], + group_type: [ + all: 0, + select: 1, + indirect: 2, + fast_failover: 3 + ], + group_type_flags: [ + all: 1 <<< 0, + select: 1 <<< 1, + indirect: 1 <<< 2, + fast_failover: 1 <<< 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_flags: [ + {Openflow.Action.Output, 1 <<< 0}, + {Openflow.Action.CopyTtlOut, 1 <<< 11}, + {Openflow.Action.CopyTtlIn, 1 <<< 12}, + {Openflow.Action.SetMplsTtl, 1 <<< 15}, + {Openflow.Action.DecMplsTtl, 1 <<< 16}, + {Openflow.Action.PushVlan, 1 <<< 17}, + {Openflow.Action.PopVlan, 1 <<< 18}, + {Openflow.Action.PushMpls, 1 <<< 19}, + {Openflow.Action.PopMpls, 1 <<< 20}, + {Openflow.Action.SetQueue, 1 <<< 21}, + {Openflow.Action.Group, 1 <<< 22}, + {Openflow.Action.SetNwTtl, 1 <<< 23}, + {Openflow.Action.DecNwTtl, 1 <<< 24}, + {Openflow.Action.SetField, 1 <<< 25}, + {Openflow.Action.PushPbb, 1 <<< 26}, + {Openflow.Action.PopPbb, 1 <<< 27}, + {Openflow.Action.Encap, 1 <<< 28}, + {Openflow.Action.Decap, 1 <<< 29}, + {Openflow.Action.SetSequence, 1 <<< 30}, + {Openflow.Action.ValidateSequence, 1 <<< 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}, Deprecated + # {Openflow.Action.NxPopQueue, 5}, Deprecated + {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}, # Deprecated + # {Openflow.Action.NxPopMpls, 24}, # Deprecated + # {Openflow.Action.NxSetMplsTtl, 25}, Deprecated + # {Openflow.Action.NxDecMplsTtl, 26}, Deprecated + {Openflow.Action.NxStackPush, 27}, + {Openflow.Action.NxStackPop, 28}, + {Openflow.Action.NxSample, 29}, + # {Openflow.Action.NxSetMplsLabel, 30}, Deprecated + # {Openflow.Action.NxSetMplsTc, 31}, Deprecated + {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 + ], + table_feature_prop_type: [ + instructions: 0, + instructions_miss: 1, + next_tables: 2, + next_tables_miss: 3, + write_actions: 4, + write_actions_miss: 5, + apply_actions: 6, + apply_actions_miss: 7, + match: 8, + wildcards: 10, + write_setfield: 12, + write_setfield_miss: 13, + apply_setfield: 14, + apply_setfield_miss: 15, + experimenter: 0xFFFE, + experimenter_miss: 0xFFFF + ], + bundle_ctrl_type: [ + open_request: 0, + open_reply: 1, + close_request: 2, + close_reply: 3, + commit_request: 4, + commit_reply: 5, + discard_request: 6, + discard_reply: 7 + ], + bundle_flags: [ + atomic: 1 <<< 0, + ordered: 1 <<< 1 + ] +] + +File.write( + "lib/openflow/enums.ex", + """ + defmodule Openflow.Enums do + @moduledoc "auto generated code"\n + """, + [:binary] +) + +for {enum_name, enum_def} <- enums do + to_int_fn_name = :"#{enum_name}_to_int" + + for {key, _value} <- enum_def do + File.write( + "lib/openflow/enums.ex", + """ + def to_int(#{inspect(key)}, :#{enum_name}) do + #{to_int_fn_name}(#{inspect(key)}) + catch + _class, _reason -> #{inspect(key)} + end\n + """, + [:append, :binary] + ) + end + + File.write( + "lib/openflow/enums.ex", + """ + def to_int(_int, :#{enum_name}) do + throw(:bad_enum) + end\n + """, + [:append, :binary] + ) +end + +for {enum_name, enum_def} <- enums do + to_atom_fn_name = :"#{enum_name}_to_atom" + + for {_key, value} <- enum_def do + File.write( + "lib/openflow/enums.ex", + """ + def to_atom(#{inspect(value, base: :hex)}, :#{enum_name}) do + #{to_atom_fn_name}(#{inspect(value, base: :hex)}) + catch + _class, _reason -> #{value} + end\n + """, + [:append, :binary] + ) + end + + File.write( + "lib/openflow/enums.ex", + """ + def to_atom(_, :#{enum_name}) do + throw(:bad_enum) + end\n + """, + [:append, :binary] + ) +end + +for {enum_name, enum_def} <- enums do + to_int_fn_name = :"#{enum_name}_to_int" + to_atom_fn_name = :"#{enum_name}_to_atom" + + for {key, value} <- enum_def do + File.write( + "lib/openflow/enums.ex", + " def #{to_int_fn_name}(#{inspect(key)}), do: #{inspect(value, base: :hex)}\n", + [ + :append, + :binary + ] + ) + end + + File.write("lib/openflow/enums.ex", " def #{to_int_fn_name}(_), do: throw(:bad_enum)\n", [ + :append, + :binary + ]) + + for {key, value} <- enum_def do + File.write( + "lib/openflow/enums.ex", + " def #{to_atom_fn_name}(#{inspect(value, base: :hex)}), do: #{inspect(key)}\n", + [ + :append, + :binary + ] + ) + end + + File.write("lib/openflow/enums.ex", " def #{to_atom_fn_name}(_), do: throw(:bad_enum)\n", [ + :append, + :binary + ]) +end + +for {enum_name, _enum_def} <- enums do + File.write( + "lib/openflow/enums.ex", + """ + def int_to_flags(int, :#{enum_name}) do + Openflow.Utils.int_to_flags([], int, enum_of(:#{enum_name})) + end\n + """, + [:append, :binary] + ) +end + +for {enum_name, _enum_def} <- enums do + File.write( + "lib/openflow/enums.ex", + """ + def flags_to_int(flags, :#{enum_name}) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:#{enum_name})) + end\n + """, + [:append, :binary] + ) +end + +for {enum_name, enum_def} <- enums do + File.write( + "lib/openflow/enums.ex", + " defp enum_of(:#{enum_name}), do: #{inspect(enum_def, pretty: true, limit: 100_000)}\n", + [:append, :binary] + ) +end + +File.write("lib/openflow/enums.ex", "end", [:append, :binary]) diff --git a/src/tres_xact_kv.erl b/src/tres_xact_kv.erl index fdbf225..d4b6835 100644 --- a/src/tres_xact_kv.erl +++ b/src/tres_xact_kv.erl @@ -3,13 +3,23 @@ -include_lib("stdlib/include/ms_transform.hrl"). -export([create/0, drop/1]). --export([insert/3, update/3, get/2, delete/2, is_exists/2, is_empty/1]). +-export([insert/3, insert/4, + update/3, get/2, + delete/2, is_exists/2, + is_empty/1, aged_out/1]). +-define(AGE_TIME_MS, 1000). -define(TABLE, xact_kv). -define(ENTRY, xact_entry). -define(TABLE_OPTS, [set, protected, {keypos, #?ENTRY.xid}]). --record(?ENTRY, {xid = 0, pending = nil, orig = nil}). +-record(?ENTRY, { + xid = 0, + pending = nil, + orig = nil, + from = nil, + inserted_at = 0 + }). -spec create() -> reference(). create() -> @@ -21,7 +31,15 @@ drop(Tid) -> -spec insert(reference(), integer(), map()) -> true. insert(Tid, Xid, Orig) -> - ets:insert(Tid, #?ENTRY{xid = Xid, orig = Orig}). + ets:insert(Tid, #?ENTRY{ + xid = Xid, + orig = Orig, + inserted_at = os:system_time(milli_seconds) + }). + +-spec insert(reference(), integer(), map(), term()) -> true. +insert(Tid, Xid, Orig, From) -> + ets:insert(Tid, #?ENTRY{xid = Xid, orig = Orig, from = From}). -spec update(reference(), integer(), map()) -> integer(). update(Tid, Xid, #{'__struct__' := 'Elixir.Openflow.ErrorMsg'} = Error) -> @@ -51,8 +69,17 @@ is_empty(Tid) -> _ -> false end. +-spec aged_out(reference()) -> integer(). +aged_out(Tid) -> + MatchSpec = ms_for_aged_entries(), + ets:select_delete(Tid, MatchSpec). + %% Private functions +ms_for_aged_entries() -> + Now = os:system_time(milli_seconds), + ets:fun2ms(fun(#?ENTRY{inserted_at = T1}) -> Now - T1 > ?AGE_TIME_MS end). + ms_for_exists(Xid) -> ets:fun2ms(fun(#?ENTRY{xid = TXid}) when TXid == Xid -> true end). diff --git a/test/lib/openflow/ofp_action_test.exs b/test/lib/openflow/ofp_action_test.exs new file mode 100644 index 0000000..f2c6296 --- /dev/null +++ b/test/lib/openflow/ofp_action_test.exs @@ -0,0 +1,1335 @@ +defmodule OfpActionTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.Action.Output" do + test "with output:1" do + output = Openflow.Action.Output.new(1) + + output + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(output) + |> assert() + end + + test "with CONTROLLER:65509" do + output = Openflow.Action.Output.new(port_number: :controller, max_len: :max) + + output + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(output) + |> assert() + end + + test "with no port_number option" do + assert_raise RuntimeError, "port_number must be specified", fn -> + Openflow.Action.Output.new() + end + end + end + + describe "Openflow.Action.Experimenter" do + test "with exp_id and data" do + experimenter = Openflow.Action.Experimenter.new(0xDEADBEEF) + assert experimenter.exp_id == 0xDEADBEEF + assert experimenter.data == "" + end + end + + describe "Openflow.Action.DecMplsTtl" do + test "with no options" do + dec_mpls_ttl = Openflow.Action.DecMplsTtl.new() + + dec_mpls_ttl + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(dec_mpls_ttl) + |> assert() + end + end + + describe "Openflow.Action.PushVlan" do + test "with no options" do + push_vlan = Openflow.Action.PushVlan.new() + + push_vlan + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(push_vlan) + |> assert() + end + + test "with an ethertype" do + push_vlan = Openflow.Action.PushVlan.new(0x8100) + + push_vlan + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(push_vlan) + |> assert() + end + end + + describe "Openflow.Action.PopVlan" do + test "with no options" do + pop_vlan = Openflow.Action.PopVlan.new() + + pop_vlan + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(pop_vlan) + |> assert() + end + end + + describe "Openflow.Action.PushMpls" do + test "with no options" do + push_vlan = Openflow.Action.PushMpls.new() + + push_vlan + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(push_vlan) + |> assert() + end + + test "with an ethertype" do + push_vlan = Openflow.Action.PushMpls.new(0x8847) + + push_vlan + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(push_vlan) + |> assert() + end + end + + describe "Openflow.Action.PopMpls" do + test "with no options" do + pop_mpls = Openflow.Action.PopMpls.new() + + pop_mpls + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(pop_mpls) + |> assert() + end + + test "with an ethertype" do + pop_mpls = Openflow.Action.PopMpls.new(0x8847) + + pop_mpls + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(pop_mpls) + |> assert() + end + end + + describe "Openflow.Action.SetQueue" do + test "with set_queue:1" do + set_queue = Openflow.Action.SetQueue.new(1) + + set_queue + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(set_queue) + |> assert() + end + end + + describe "Openflow.Action.Group" do + test "with group:1" do + group = Openflow.Action.Group.new(1) + + group + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(group) + |> assert() + end + end + + describe "Openflow.Action.DecNwTtl" do + test "with dec_ttl" do + dec_nw_ttl = Openflow.Action.DecNwTtl.new() + + dec_nw_ttl + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(dec_nw_ttl) + |> assert() + end + end + + describe "Openflow.Action.NxBundle" do + test "with a binary" 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, + hash_field: :eth_src, + basis: 0, + slaves: [4, 8] + ) + + actions_bin = Openflow.Action.to_binary(bundle) + assert actions_bin == packet + assert actions == [bundle] + end + + test "with no option" do + bundle = Openflow.Action.NxBundle.new() + assert bundle.algorithm == :active_backup + assert bundle.hash_field == :eth_src + assert bundle.basis == 0 + assert bundle.n_slaves == 0 + assert bundle.slaves == [] + end + end + + describe "Openflow.Action.NxBundleLoad" do + test "with a binary" 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, + hash_field: :eth_src, + basis: 0, + slaves: [4, 8], + offset: 0, + dst_field: :reg0 + ) + + actions_bin = Openflow.Action.to_binary(bundle_load) + assert actions_bin == packet + assert actions == [bundle_load] + end + + test "with no option" do + assert_raise RuntimeError, "dst_field must be specified", fn -> + Openflow.Action.NxBundleLoad.new([]) + end + end + end + + describe "Openflow.Action.NxController" do + test "with packet_data" 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 "with no options" do + controller = Openflow.Action.NxController.new() + assert controller.max_len == :no_buffer + assert controller.id == 0 + assert controller.reason == :action + end + end + + describe "Openflow.Action.NxController2" do + test "with packet data" 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, + id: 5678, + reason: :invalid_ttl, + userdata: <<1, 2, 3, 4, 5>>, + pause: true, + meter_id: 0 + ) + + assert actions == [controller2] + + controller2 + |> Openflow.Action.NxController2.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(controller2) + |> assert() + end + + test "with meter_id" do + controller2 = + [meter_id: 0xFACE] + |> Openflow.Action.NxController2.new() + |> Openflow.Action.NxController2.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + + assert controller2.max_len == :no_buffer + assert controller2.id == 0 + assert controller2.reason == :action + assert controller2.userdata == "" + assert controller2.pause == false + assert controller2.meter_id == 0xFACE + end + + test "with no options" do + controller2 = + Openflow.Action.NxController2.new() + |> Openflow.Action.NxController2.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + + assert controller2.max_len == :no_buffer + assert controller2.id == 0 + assert controller2.reason == :action + assert controller2.userdata == "" + assert controller2.pause == false + assert controller2.meter_id == 0 + end + 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 + end + + describe "Openflow.Action.NxCtClear" do + 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 + end + + describe "Openflow.Action.NxDecTtl" do + 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 + end + + describe "Openflow.Action.NxDecTtlCntIds" do + 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 + end + + describe "Openflow.Action.NxExit" do + 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 + end + + describe "Openflow.Action.NxFinTimeout" do + 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 + end + + describe "Openflow.Action.NxLearn" do + 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 + end + + describe "Openflow.Action.NxLearn2" do + test "with learn2 packet" 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 no options" do + learn2 = Openflow.Action.NxLearn2.new() + + learn2 + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(learn2) + |> assert() + end + end + + describe "Openflow.Action.NxFlowSpecOutput" do + test "with src = :in_port" do + flow_spec = Openflow.Action.NxFlowSpecOutput.new(src: :in_port) + + flow_spec + |> Openflow.Action.NxFlowSpec.to_binary() + |> Openflow.Action.NxFlowSpec.read() + |> Enum.at(0) + |> Kernel.==(flow_spec) + |> assert() + end + + test "with no options" do + assert_raise RuntimeError, ":src must be specified", fn -> + Openflow.Action.NxFlowSpecOutput.new() + end + end + end + + describe "Openflow.Action.NxFlowSpecMatch" do + test "with src = 0xdeadbeef and dst: :reg0" do + flow_spec = Openflow.Action.NxFlowSpecMatch.new(src: 0xDEADBEEF, dst: :reg0) + + flow_spec + |> Openflow.Action.NxFlowSpec.to_binary() + |> Openflow.Action.NxFlowSpec.read() + |> Enum.at(0) + |> Kernel.==(flow_spec) + |> assert() + end + + test "with src = :in_port and dst: :reg0" do + flow_spec = + Openflow.Action.NxFlowSpecMatch.new( + src: :in_port, + dst: :reg0, + src_offset: 0, + dst_offset: 0, + n_bits: 16 + ) + + flow_spec + |> Openflow.Action.NxFlowSpec.to_binary() + |> Openflow.Action.NxFlowSpec.read() + |> Enum.at(0) + |> Kernel.==(flow_spec) + |> assert() + end + + test "with no option" do + assert_raise RuntimeError, ":dst must be specified", fn -> + Openflow.Action.NxFlowSpecMatch.new() + end + end + + test "with no dst option" do + assert_raise RuntimeError, ":dst must be specified", fn -> + Openflow.Action.NxFlowSpecMatch.new(src: :in_port) + end + end + + test "with no src option" do + assert_raise RuntimeError, ":src must be specified", fn -> + Openflow.Action.NxFlowSpecMatch.new(dst: :reg0) + end + end + end + + describe "Openflow.Action.NxFlowSpecLoad" do + test "with src = 0xdeadbeef and dst: :reg0" do + flow_spec = Openflow.Action.NxFlowSpecLoad.new(src: 0xDEADBEEF, dst: :reg0) + + flow_spec + |> Openflow.Action.NxFlowSpec.to_binary() + |> Openflow.Action.NxFlowSpec.read() + |> Enum.at(0) + |> Kernel.==(flow_spec) + |> assert() + end + + test "with src = :in_port and dst: :reg0" do + flow_spec = + Openflow.Action.NxFlowSpecLoad.new( + src: :in_port, + dst: :reg0, + src_offset: 0, + dst_offset: 0, + n_bits: 16 + ) + + flow_spec + |> Openflow.Action.NxFlowSpec.to_binary() + |> Openflow.Action.NxFlowSpec.read() + |> Enum.at(0) + |> Kernel.==(flow_spec) + |> assert() + end + + test "with no dst option" do + assert_raise RuntimeError, ":dst must be specified", fn -> + Openflow.Action.NxFlowSpecLoad.new(src: :in_port) + end + end + + test "with no src option" do + assert_raise RuntimeError, ":src must be specified", fn -> + Openflow.Action.NxFlowSpecLoad.new(dst: :reg0) + end + end + end + + describe "Openflow.Action.NxMultipath" do + 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( + hash_field: :eth_src, + algorithm: :modulo_n, + basis: 50, + max_link: 0, + offset: 0, + argument: 0, + dst_field: :reg0 + ) + + actions_bin = Openflow.Action.to_binary(multipath) + assert actions_bin == packet + assert actions == [multipath] + end + end + + describe "Openflow.Action.NxNote" do + test "with note binary" 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 + end + + describe "Openflow.Action.NxOutputReg" do + test "with output_reg options src_field = reg1, n_bits = 6, offset = 5" 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, + max_len: :no_buffer + ) + + actions_bin = Openflow.Action.to_binary(output_reg) + assert actions_bin == packet + assert actions == [output_reg] + end + + test "with no src_field option" do + assert_raise RuntimeError, "src_field must be specified", fn -> + Openflow.Action.NxOutputReg.new(n_bits: 6) + end + end + end + + describe "Openflow.Action.NxOutputReg2" do + test "with output_reg options src_field = reg1, n_bits = 6, offset = 5" do + output_reg = + Openflow.Action.NxOutputReg2.new( + src_field: :reg1, + n_bits: 6, + offset: 5, + max_len: :no_buffer + ) + + output_reg + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(output_reg) + |> assert() + end + end + + describe "Openflow.Action.NxOutputTrunc" do + test "with output_trunc options: packets output to port1 trunc in 100bytes" 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_no: 1, max_len: 100) + actions_bin = Openflow.Action.to_binary(output_trunc) + assert actions_bin == packet + assert actions == [output_trunc] + end + end + + describe "Openflow.Action.NxRegLoad" do + test "with options: load 0xf008 to vlan_tci" 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 no options" do + assert_raise RuntimeError, "dst_field must be specified", fn -> + Openflow.Action.NxRegLoad.new() + end + end + end + + describe "Openflow.Action.NxRegMove" do + test "with options: move in_port value to vlan_tci field" 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, + src_offset: 0, + dst_offset: 0 + ) + + actions_bin = Openflow.Action.to_binary(reg_move) + assert actions_bin == packet + assert actions == [reg_move] + end + + test "with no options" do + assert_raise RuntimeError, "src_field must be specified", fn -> + Openflow.Action.NxRegMove.new() + end + end + + test "with no dst_field options" do + assert_raise RuntimeError, "dst_field must be specified", fn -> + Openflow.Action.NxRegMove.new(src_field: :reg0) + end + end + end + + describe "Openflow.Action.NxResubmit" do + test "with options: resubmit to table, search with the port_no" do + test_file = "test/packet_data/nx_resubmit.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + resubmit = Openflow.Action.NxResubmit.new(_port_no = 5) + actions_bin = Openflow.Action.to_binary(resubmit) + assert actions_bin == packet + assert actions == [resubmit] + end + end + + describe "Openflow.Action.NxResubmitTable" do + 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 + end + + describe "Openflow.Action.NxResubmitTableCt" do + 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 + + describe "Openflow.Action.NxClone" do + test "with clone(push_vlan:0x8100,set_field:5->vlan_vid,output:10)" 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) + ]) + + actions_bin = Openflow.Action.to_binary(clone) + assert actions_bin == packet + assert actions == [clone] + end + end + + describe "Openflow.Action.NxRegLoad2" do + test "with set_field:0x1/0x1->reg1" do + reg_load2 = Openflow.Action.NxRegLoad2.new(reg1: {1, 1}) + + reg_load2 + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(reg_load2) + |> assert() + end + + test "with set_field:0x1->reg1" do + reg_load2 = Openflow.Action.NxRegLoad2.new(reg1: 1) + + reg_load2 + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(reg_load2) + |> assert() + end + end + + describe "Openflow.Action.NxStackPop" do + test "with pop:NXM_NX_REG0[]" do + pop = Openflow.Action.NxStackPop.new(field: :reg0) + + assert pop.n_bits == 32 + assert pop.offset == 0 + + pop + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(pop) + |> assert() + end + + test "with pop:NXM_NX_REG0[1..31]" do + pop = Openflow.Action.NxStackPop.new(field: :reg0, offset: 1, n_bits: 31) + + assert pop.n_bits == 31 + assert pop.offset == 1 + + pop + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(pop) + |> assert() + end + end + + describe "Openflow.Action.NxStackPush" do + test "with push:NXM_NX_REG0[]" do + push = Openflow.Action.NxStackPush.new(field: :reg0) + + assert push.n_bits == 32 + assert push.offset == 0 + + push + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(push) + |> assert() + end + + test "with push:NXM_NX_REG0[1..31]" do + push = Openflow.Action.NxStackPush.new(field: :reg0, offset: 1, n_bits: 31) + + assert push.n_bits == 31 + assert push.offset == 1 + + push + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(push) + |> assert() + end + end + + describe "Openflow.Action.NxSetTunnel" do + test "with set_tunnel:0x1" do + set_tunnel = Openflow.Action.NxSetTunnel.new(1) + + set_tunnel + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(set_tunnel) + |> assert() + end + end + + describe "Openflow.Action.NxSetTunnel64" do + test "with set_tunnel64:0x1" do + set_tunnel = Openflow.Action.NxSetTunnel64.new(1) + + set_tunnel + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(set_tunnel) + |> assert() + end + end + + describe "Openflow.Action.NxWriteMetadata" do + test "with write_metadata:0x1" do + write_metadata = Openflow.Action.NxWriteMetadata.new(1) + + write_metadata + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(write_metadata) + |> assert() + end + + test "with write_metadata:0x1/0x1" do + write_metadata = Openflow.Action.NxWriteMetadata.new(metadata: 0x1, metadata_mask: 0x1) + + write_metadata + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(write_metadata) + |> assert() + end + + test "with no options" do + assert_raise RuntimeError, "metadata must be specified", fn -> + Openflow.Action.NxWriteMetadata.new() + end + end + end + + describe "Openflow.Action.NxConjunction" do + test "with conjunction(0,1/2)" do + conjunction = Openflow.Action.NxConjunction.new(clause: 0, n_clauses: 2) + + assert conjunction.id == 0 + assert conjunction.clause == 1 + assert conjunction.n_clauses == 2 + + conjunction + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(conjunction) + |> assert() + end + end + + test "with no n_clauses option" do + assert_raise RuntimeError, "n_clauses must be specified", fn -> + Openflow.Action.NxConjunction.new(id: 1, clause: 0) + end + end + + test "with n_clauses option less than 2" do + assert_raise RuntimeError, "n_clauses must be greater than 1", fn -> + Openflow.Action.NxConjunction.new(id: 1, clause: 0, n_clauses: 1) + end + end + + describe "Opefnlow.Action.NxSample" do + test "with sample(probability=1,collector_set_id=0,obs_domain_id=0,obs_point_id=0)" do + sample = + Openflow.Action.NxSample.new( + probability: 1, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0 + ) + + sample + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(sample) + |> assert() + end + end + + describe "Opefnlow.Action.NxSample2" do + test "with sample(probability=1,collector_set_id=0,obs_domain_id=0,obs_point_id=0, sampling_port=100)" do + sample = + Openflow.Action.NxSample2.new( + probability: 1, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0, + sampling_port: 100 + ) + + sample + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(sample) + |> assert() + end + end + + describe "Opefnlow.Action.NxSample3" do + test "with sample(probability=1,collector_set_id=0,obs_domain_id=0,obs_point_id=0, sampling_port=100,egress)" do + sample = + Openflow.Action.NxSample3.new( + probability: 1, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0, + sampling_port: 100, + direction: :egress + ) + + sample + |> Openflow.Action.to_binary() + |> Openflow.Action.read() + |> Enum.at(0) + |> Kernel.==(sample) + |> assert() + end + end +end diff --git a/test/lib/openflow/ofp_aggregate_test.exs b/test/lib/openflow/ofp_aggregate_test.exs new file mode 100644 index 0000000..a6c7a08 --- /dev/null +++ b/test/lib/openflow/ofp_aggregate_test.exs @@ -0,0 +1,40 @@ +defmodule OfpAggregateTest do + use ExUnit.Case + + describe "Openflow.Multipart.Aggregate.Request" do + test "with default values" do + aggregate = + %Openflow.Multipart.Aggregate.Request{} + |> Map.to_list() + |> Openflow.Multipart.Aggregate.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert aggregate.xid == 0 + assert aggregate.table_id == :all + assert aggregate.out_port == :any + assert aggregate.out_group == :any + assert aggregate.cookie == 0 + assert aggregate.cookie_mask == 0 + assert aggregate.match == [] + end + end + + describe "Openflow.Multipart.Aggregate.Reply" do + test "with default values" do + aggregate = + %Openflow.Multipart.Aggregate.Reply{} + |> Map.to_list() + |> Openflow.Multipart.Aggregate.Reply.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert aggregate.xid == 0 + assert aggregate.packet_count == 0 + assert aggregate.byte_count == 0 + assert aggregate.flow_count == 0 + end + end +end diff --git a/test/lib/openflow/ofp_barrier_test.exs b/test/lib/openflow/ofp_barrier_test.exs new file mode 100644 index 0000000..4d0b114 --- /dev/null +++ b/test/lib/openflow/ofp_barrier_test.exs @@ -0,0 +1,52 @@ +defmodule OfpBarrierTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.Barrier.Request" do + test "generate and parse without argument" do + barrier = Openflow.Barrier.Request.new() + + barrier + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(barrier) + |> assert() + end + + test "generate and parse with argument" do + barrier = Openflow.Barrier.Request.new(1) + + barrier + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(barrier) + |> assert() + end + end + + describe "Openflow.Barrier.Reply" do + test "generate and parse without argument" do + barrier = Openflow.Barrier.Reply.new() + + barrier + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(barrier) + |> assert() + end + + test "generate and parse with argument" do + barrier = Openflow.Barrier.Reply.new(1) + + barrier + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(barrier) + |> assert() + end + end +end diff --git a/test/lib/openflow/ofp_desc_stats_test.exs b/test/lib/openflow/ofp_desc_stats_test.exs new file mode 100644 index 0000000..f732979 --- /dev/null +++ b/test/lib/openflow/ofp_desc_stats_test.exs @@ -0,0 +1,41 @@ +defmodule OfpDescTest do + use ExUnit.Case + + describe "Openflow.Multipart.Desc.Request" do + test "with default values" do + desc = Openflow.Multipart.Desc.Request.new(0) + + desc + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(desc) + |> assert() + end + end + + describe "Openflow.Multipart.Desc.Reply" do + test "with default values" do + desc = + %Openflow.Multipart.Desc.Reply{ + dp_desc: "None", + hw_desc: "Open vSwitch", + mfr_desc: "Nicira, Inc.", + serial_num: "None", + sw_desc: "2.11.0" + } + |> Map.to_list() + |> Openflow.Multipart.Desc.Reply.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert desc.xid == 0 + assert desc.mfr_desc == "Nicira, Inc." + assert desc.dp_desc == "None" + assert desc.hw_desc == "Open vSwitch" + assert desc.sw_desc == "2.11.0" + assert desc.serial_num == "None" + end + end +end diff --git a/test/lib/openflow/ofp_echo_test.exs b/test/lib/openflow/ofp_echo_test.exs new file mode 100644 index 0000000..75d2683 --- /dev/null +++ b/test/lib/openflow/ofp_echo_test.exs @@ -0,0 +1,110 @@ +defmodule OfpEchoTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.Echo.Request" do + test "with xid and data options" do + echo = Openflow.Echo.Request.new(xid: 1, data: "echo") + + echo + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(echo) + |> assert() + end + + test "with data options" do + echo = Openflow.Echo.Request.new("echo") + + echo + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(echo) + |> assert() + end + end + + describe "Openflow.Echo.Reply" do + test "with xid and data options" do + echo = Openflow.Echo.Reply.new(xid: 1, data: "echo") + + echo + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(echo) + |> assert() + end + + test "with data options" do + echo = Openflow.Echo.Reply.new("echo") + + echo + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(echo) + |> assert() + end + end + + 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() + + expect = Openflow.Echo.Request.new() + + assert echo.version == expect.version + assert echo.xid == expect.xid + assert echo.data == expect.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() + + expect = Openflow.Echo.Reply.new() + + assert echo.version == expect.version + assert echo.xid == expect.xid + assert echo.data == expect.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/lib/openflow/ofp_error_test.exs similarity index 100% rename from test/ofp_error_test.exs rename to test/lib/openflow/ofp_error_test.exs diff --git a/test/ofp_features_test.exs b/test/lib/openflow/ofp_features_test.exs similarity index 92% rename from test/ofp_features_test.exs rename to test/lib/openflow/ofp_features_test.exs index 5d38572..6cb9430 100644 --- a/test/ofp_features_test.exs +++ b/test/lib/openflow/ofp_features_test.exs @@ -9,8 +9,10 @@ defmodule OfpFeaturesTest do |> File.read!() |> Openflow.read() - assert features.version == 4 - assert features.xid == 0 + expect = Openflow.Features.Request.new(0) + + assert features.version == expect.version + assert features.xid == expect.xid end test "with OFP_FEATURES_REPLY packet" do diff --git a/test/ofp_flow_mod_test.exs b/test/lib/openflow/ofp_flow_mod_test.exs similarity index 94% rename from test/ofp_flow_mod_test.exs rename to test/lib/openflow/ofp_flow_mod_test.exs index 9671698..ea8a5cd 100644 --- a/test/ofp_flow_mod_test.exs +++ b/test/lib/openflow/ofp_flow_mod_test.exs @@ -14,7 +14,16 @@ defmodule OfpFlowModTest do describe "Openflow.read/1" do test "with OFP_FLOW_MOD packet(1)" do binary = File.read!(@flow_mod1) - {:ok, fm, ""} = Openflow.read(binary) + + fm = + binary + |> Openflow.read() + |> elem(1) + |> Map.to_list() + |> Openflow.FlowMod.new() + |> Openflow.to_binary() + |> Openflow.read() + |> elem(1) assert fm.cookie == 0 assert fm.cookie_mask == 0 @@ -31,7 +40,7 @@ defmodule OfpFlowModTest do assert fm.instructions == [ Openflow.Instruction.WriteActions.new([ - Openflow.Action.SetField.new({:vlan_vid, 258}), + Openflow.Action.SetField.new(vlan_vid: 258), Openflow.Action.CopyTtlOut.new(), Openflow.Action.CopyTtlIn.new(), Openflow.Action.CopyTtlIn.new(), @@ -51,8 +60,8 @@ defmodule OfpFlowModTest do Openflow.Action.Output.new(6) ]), Openflow.Instruction.ApplyActions.new([ - Openflow.Action.SetField.new({:eth_src, "010203040506"}), - Openflow.Action.SetField.new({:onf_pbb_uca, 1}) + Openflow.Action.SetField.new(eth_src: "010203040506"), + Openflow.Action.SetField.new(onf_pbb_uca: 1) ]) ] @@ -202,7 +211,7 @@ defmodule OfpFlowModTest do assert fm.instructions == [ Openflow.Instruction.ApplyActions.new([ Openflow.Action.PopVlan.new(), - Openflow.Action.SetField.new({:ipv4_dst, {192, 168, 2, 9}}), + Openflow.Action.SetField.new(ipv4_dst: {192, 168, 2, 9}), Openflow.Action.NxLearn.new( hard_timeout: 300, priority: 1, @@ -263,7 +272,7 @@ defmodule OfpFlowModTest do assert fm.instructions == [ Openflow.Instruction.ApplyActions.new([ - Openflow.Action.NxConjunction.new(clause: 1, id: 0xABCDEF, n_clauses: 2) + Openflow.Action.NxConjunction.new(clause: 0, id: 0xABCDEF, n_clauses: 2) ]) ] @@ -290,7 +299,7 @@ defmodule OfpFlowModTest do assert fm.instructions == [ Openflow.Instruction.ApplyActions.new([ Openflow.Action.PopVlan.new(), - Openflow.Action.SetField.new({:ipv4_dst, {192, 168, 2, 9}}) + Openflow.Action.SetField.new(ipv4_dst: {192, 168, 2, 9}) ]), Openflow.Instruction.GotoTable.new(100) ] diff --git a/test/ofp_flow_removed_test.exs b/test/lib/openflow/ofp_flow_removed_test.exs similarity index 100% rename from test/ofp_flow_removed_test.exs rename to test/lib/openflow/ofp_flow_removed_test.exs diff --git a/test/lib/openflow/ofp_flow_stats_test.exs b/test/lib/openflow/ofp_flow_stats_test.exs new file mode 100644 index 0000000..0eecc19 --- /dev/null +++ b/test/lib/openflow/ofp_flow_stats_test.exs @@ -0,0 +1,141 @@ +defmodule OfpFlowStatsTest do + use ExUnit.Case + + describe "Openflow.Multipart.Flow.Request" do + test "with default values" do + flow_stats = + %Openflow.Multipart.Flow.Request{} + |> Map.to_list() + |> Openflow.Multipart.Flow.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert flow_stats.xid == 0 + assert flow_stats.table_id == :all + assert flow_stats.out_port == :any + assert flow_stats.out_group == :any + assert flow_stats.cookie == 0 + assert flow_stats.cookie_mask == 0 + assert flow_stats.match == [] + end + end + + describe "Openflow.Multipart.Flow.Reply" do + test "with test packet_data" do + flow_stats = + "test/packet_data/4-12-ofp_flow_stats_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.Flow.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + flows: [ + %Openflow.Multipart.FlowStats{ + byte_count: 0, + cookie: 0, + duration_nsec: 115_277_000, + duration_sec: 358, + flags: [], + hard_timeout: 0, + idle_timeout: 0, + instructions: [], + match: [], + packet_count: 0, + priority: 65535, + table_id: 0 + }, + %Openflow.Multipart.FlowStats{ + byte_count: 0, + cookie: 0, + duration_nsec: 115_055_000, + duration_sec: 358, + flags: [], + hard_timeout: 0, + idle_timeout: 0, + instructions: [ + %Openflow.Instruction.ApplyActions{ + actions: [%Openflow.Action.Output{max_len: 0, port_number: :normal}] + } + ], + match: [eth_type: 2054], + packet_count: 0, + priority: 65534, + table_id: 0 + }, + %Openflow.Multipart.FlowStats{ + byte_count: 238, + cookie: 0, + duration_nsec: 511_582_000, + duration_sec: 316_220, + flags: [], + hard_timeout: 0, + idle_timeout: 0, + instructions: [%Openflow.Instruction.GotoTable{table_id: 1}], + match: [in_port: 6, eth_src: "f20ba47df8ea"], + packet_count: 3, + priority: 123, + table_id: 0 + }, + %Openflow.Multipart.FlowStats{ + byte_count: 98, + cookie: 0, + duration_nsec: 980_901_000, + duration_sec: 313_499, + flags: [], + hard_timeout: 0, + idle_timeout: 0, + instructions: [ + %Openflow.Instruction.WriteActions{ + actions: [ + %Openflow.Action.SetField{field: [vlan_vid: 258]}, + %Openflow.Action.CopyTtlOut{}, + %Openflow.Action.CopyTtlIn{}, + %Openflow.Action.CopyTtlIn{}, + %Openflow.Action.PopPbb{}, + %Openflow.Action.PushPbb{ethertype: 4660}, + %Openflow.Action.PopMpls{ethertype: 39030}, + %Openflow.Action.PushMpls{ethertype: 34887}, + %Openflow.Action.PopVlan{}, + %Openflow.Action.PushVlan{ethertype: 33024}, + %Openflow.Action.DecMplsTtl{}, + %Openflow.Action.SetMplsTtl{ttl: 10}, + %Openflow.Action.DecNwTtl{}, + %Openflow.Action.SetNwTtl{ttl: 10}, + %Openflow.Action.SetQueue{id: 3}, + %Openflow.Action.Group{id: 99}, + %Openflow.Action.Output{max_len: :no_buffer, port_number: 6}, + %Openflow.Action.Experimenter{data: "exp_data", exp_id: 98_765_432}, + %Openflow.Action.Experimenter{data: "exp_data", exp_id: 8992} + ] + }, + %Openflow.Instruction.ApplyActions{ + actions: [ + %Openflow.Action.SetField{field: [eth_src: "010203040506"]}, + %Openflow.Action.SetField{field: [onf_pbb_uca: 1]} + ] + }, + %Openflow.Instruction.WriteActions{ + actions: [ + %Openflow.Action.Output{ + max_len: :no_buffer, + port_number: :controller + } + ] + } + ], + match: [], + packet_count: 1, + priority: 0, + table_id: 0 + } + ], + version: 4, + xid: 0 + } = flow_stats + end + end +end diff --git a/test/lib/openflow/ofp_get_async_test.exs b/test/lib/openflow/ofp_get_async_test.exs new file mode 100644 index 0000000..9fd7352 --- /dev/null +++ b/test/lib/openflow/ofp_get_async_test.exs @@ -0,0 +1,60 @@ +defmodule OfpGetAsyncTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.GetAsync.Request" do + test "generate and parse without argument" do + get_async = Openflow.GetAsync.Request.new() + + get_async + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(get_async) + |> assert() + end + + test "generate and parse with argument" do + get_async = Openflow.GetAsync.Request.new(1) + + get_async + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(get_async) + |> assert() + end + end + + describe "Openflow.GetAsync.Reply" do + test "generate and parse without argument" do + get_async = Openflow.GetAsync.Reply.new() + + get_async + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(get_async) + |> assert() + end + + test "generate and parse with argument" do + get_async = + Openflow.GetAsync.Reply.new( + flow_removed_mask_master: [:idle_timeout, :hard_timeout, :delete, :group_delete], + flow_removed_mask_slave: [], + packet_in_mask_master: [:no_match, :action], + packet_in_mask_slave: [], + port_status_mask_master: [:add, :delete, :modify], + port_status_mask_slave: [:add, :delete, :modify] + ) + + get_async + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(get_async) + |> assert() + end + end +end diff --git a/test/ofp_get_config_test.exs b/test/lib/openflow/ofp_get_config_test.exs similarity index 93% rename from test/ofp_get_config_test.exs rename to test/lib/openflow/ofp_get_config_test.exs index 9c51812..720ebe1 100644 --- a/test/ofp_get_config_test.exs +++ b/test/lib/openflow/ofp_get_config_test.exs @@ -28,10 +28,7 @@ defmodule OfpGetConfigTest do describe "Openflow.to_binary/1" do test "with %Openflow.GetConfig.Request{}" do - config = %Openflow.GetConfig.Request{ - version: 4, - xid: 0 - } + config = Openflow.GetConfig.Request.new(0) expect = "test/packet_data/ofp_get_config_request.raw" diff --git a/test/lib/openflow/ofp_group_desc_stats_test.exs b/test/lib/openflow/ofp_group_desc_stats_test.exs new file mode 100644 index 0000000..f8f2a12 --- /dev/null +++ b/test/lib/openflow/ofp_group_desc_stats_test.exs @@ -0,0 +1,50 @@ +defmodule OfpGroupDescTest do + use ExUnit.Case + + describe "Openflow.Multipart.GroupDesc.Request" do + test "with default values" do + desc = Openflow.Multipart.GroupDesc.Request.new(0) + + desc + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(desc) + |> assert() + end + end + + describe "Openflow.Multipart.GroupDesc.Reply" do + test "with test packet_data" do + group_desc_stats = + "test/packet_data/4-34-ofp_group_desc_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.GroupDesc.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + groups: [ + %Openflow.Multipart.GroupDescStats{ + buckets: [ + %Openflow.Bucket{ + actions: [ + %Openflow.Action.Output{max_len: :no_buffer, port_number: 2} + ], + watch_group: 1, + watch_port: 1, + weight: 1 + } + ], + group_id: 1, + type: :all + } + ], + version: 4, + xid: 0 + } = group_desc_stats + end + end +end diff --git a/test/lib/openflow/ofp_group_features_stats_test.exs b/test/lib/openflow/ofp_group_features_stats_test.exs new file mode 100644 index 0000000..695b377 --- /dev/null +++ b/test/lib/openflow/ofp_group_features_stats_test.exs @@ -0,0 +1,104 @@ +defmodule OfpGroupFeaturesTest do + use ExUnit.Case + + describe "Openflow.Multipart.GroupFeatures.Request" do + test "with default values" do + features = Openflow.Multipart.GroupFeatures.Request.new(0) + + features + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(features) + |> assert() + end + end + + describe "Openflow.Multipart.GroupFeatures.Reply" do + test "with test packet_data" do + group_features_stats = + "test/packet_data/4-32-ofp_group_features_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.GroupFeatures.Reply{ + actions_for_all: [ + Openflow.Action.Output, + Openflow.Action.CopyTtlOut, + Openflow.Action.CopyTtlIn, + Openflow.Action.SetMplsTtl, + Openflow.Action.DecMplsTtl, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.PushMpls, + Openflow.Action.PopMpls, + Openflow.Action.SetQueue, + Openflow.Action.Group, + Openflow.Action.SetNwTtl, + Openflow.Action.DecNwTtl, + Openflow.Action.SetField + ], + actions_for_fast_failover: [ + Openflow.Action.Output, + Openflow.Action.CopyTtlOut, + Openflow.Action.CopyTtlIn, + Openflow.Action.SetMplsTtl, + Openflow.Action.DecMplsTtl, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.PushMpls, + Openflow.Action.PopMpls, + Openflow.Action.SetQueue, + Openflow.Action.Group, + Openflow.Action.SetNwTtl, + Openflow.Action.DecNwTtl, + Openflow.Action.SetField + ], + actions_for_indirect: [ + Openflow.Action.Output, + Openflow.Action.CopyTtlOut, + Openflow.Action.CopyTtlIn, + Openflow.Action.SetMplsTtl, + Openflow.Action.DecMplsTtl, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.PushMpls, + Openflow.Action.PopMpls, + Openflow.Action.SetQueue, + Openflow.Action.Group, + Openflow.Action.SetNwTtl, + Openflow.Action.DecNwTtl, + Openflow.Action.SetField + ], + actions_for_select: [ + Openflow.Action.Output, + Openflow.Action.CopyTtlOut, + Openflow.Action.CopyTtlIn, + Openflow.Action.SetMplsTtl, + Openflow.Action.DecMplsTtl, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.PushMpls, + Openflow.Action.PopMpls, + Openflow.Action.SetQueue, + Openflow.Action.Group, + Openflow.Action.SetNwTtl, + Openflow.Action.DecNwTtl, + Openflow.Action.SetField + ], + aux_id: nil, + capabilities: [:select_weight, :chaining], + datapath_id: nil, + flags: [], + max_groups_for_all: 16_777_216, + max_groups_for_fast_failover: 16_777_216, + max_groups_for_indirect: 16_777_216, + max_groups_for_select: 16_777_216, + types: [:all, :select, :indirect, :fast_failover], + version: 4, + xid: 0 + } = group_features_stats + end + end +end diff --git a/test/ofp_group_mod_test.exs b/test/lib/openflow/ofp_group_mod_test.exs similarity index 72% rename from test/ofp_group_mod_test.exs rename to test/lib/openflow/ofp_group_mod_test.exs index 7081095..c40ce8f 100644 --- a/test/ofp_group_mod_test.exs +++ b/test/lib/openflow/ofp_group_mod_test.exs @@ -5,7 +5,17 @@ defmodule OfpGroupModTest do 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) + {:ok, _group_mod, ""} = Openflow.read(binary) + + group_mod = + binary + |> Openflow.read() + |> elem(1) + |> Map.to_list() + |> Openflow.GroupMod.new() + |> Openflow.to_binary() + |> Openflow.read() + |> elem(1) assert group_mod.version == 4 assert group_mod.xid == 0 diff --git a/test/lib/openflow/ofp_group_stats_test.exs b/test/lib/openflow/ofp_group_stats_test.exs new file mode 100644 index 0000000..a02496f --- /dev/null +++ b/test/lib/openflow/ofp_group_stats_test.exs @@ -0,0 +1,47 @@ +defmodule OfpGroupStatsTest do + use ExUnit.Case + + describe "Openflow.Multipart.Group.Request" do + test "with default values" do + group_stats = + %Openflow.Multipart.Group.Request{} + |> Map.to_list() + |> Openflow.Multipart.Group.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert group_stats.xid == 0 + assert group_stats.group_id == :all + end + end + + describe "Openflow.Multipart.Group.Reply" do + test "with test packet_data" do + group_stats = + "test/packet_data/4-58-ofp_group_stats_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.Group.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + groups: [ + %Openflow.Multipart.GroupStats{ + bucket_stats: [%{byte_count: 2345, packet_count: 234}], + byte_count: 12345, + duration_nsec: 609_036_000, + duration_sec: 9, + group_id: 1, + packet_count: 123, + ref_count: 2 + } + ], + version: 4, + xid: 0 + } = group_stats + end + end +end diff --git a/test/ofp_hello_test.exs b/test/lib/openflow/ofp_hello_test.exs similarity index 100% rename from test/ofp_hello_test.exs rename to test/lib/openflow/ofp_hello_test.exs diff --git a/test/lib/openflow/ofp_instruction_test.exs b/test/lib/openflow/ofp_instruction_test.exs new file mode 100644 index 0000000..b23bbe8 --- /dev/null +++ b/test/lib/openflow/ofp_instruction_test.exs @@ -0,0 +1,35 @@ +defmodule OfpInstructionTest do + use ExUnit.Case + doctest Openflow + + alias Openflow.Instruction.{ + ApplyActions, + WriteActions, + ClearActions, + GotoTable, + WriteMetadata, + Meter, + Experimenter + } + + describe "Openflow.Instruction" do + test "with all instructions parse and generate" do + instructions = [ + ApplyActions.new([Openflow.Action.Output.new(:controller)]), + WriteActions.new([Openflow.Action.Output.new(5)]), + ClearActions.new(), + GotoTable.new(10), + WriteMetadata.new(value: 100, mask: 0xFFFFFFFFFFFFFFFF), + Meter.new(100), + Experimenter.new(0xCAFEBABE, "hogehoge"), + Experimenter.new(0xCAFEBABE) + ] + + instructions + |> Openflow.Instruction.to_binary() + |> Openflow.Instruction.read() + |> Kernel.==(instructions) + |> assert() + end + end +end diff --git a/test/lib/openflow/ofp_meter_mod_test.exs b/test/lib/openflow/ofp_meter_mod_test.exs new file mode 100644 index 0000000..685836e --- /dev/null +++ b/test/lib/openflow/ofp_meter_mod_test.exs @@ -0,0 +1,44 @@ +defmodule OfpMeterModTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.MeterMod" do + test "with packet_data" do + meter_mod = + "test/packet_data/libofproto-OFP13-meter_mod.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + |> Map.to_list() + |> Openflow.MeterMod.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + expect = + Openflow.MeterMod.new( + xid: 0, + meter_id: 100, + command: :add, + flags: [:pktps, :burst, :stats], + bands: [ + Openflow.MeterBand.Drop.new( + burst_size: 10, + rate: 1000 + ), + Openflow.MeterBand.Remark.new( + burst_size: 10, + prec_level: 1, + rate: 1000 + ) + ] + ) + + assert expect.xid == meter_mod.xid + assert expect.meter_id == meter_mod.meter_id + assert expect.command == meter_mod.command + assert expect.flags == meter_mod.flags + assert expect.bands == meter_mod.bands + end + end +end diff --git a/test/lib/openflow/ofp_meter_stats_test.exs b/test/lib/openflow/ofp_meter_stats_test.exs new file mode 100644 index 0000000..1637d2b --- /dev/null +++ b/test/lib/openflow/ofp_meter_stats_test.exs @@ -0,0 +1,47 @@ +defmodule OfpMeterStatsTest do + use ExUnit.Case + + describe "Openflow.Multipart.Meter.Request" do + test "with default values" do + meter_stats = + %Openflow.Multipart.Meter.Request{} + |> Map.to_list() + |> Openflow.Multipart.Meter.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert meter_stats.xid == 0 + assert meter_stats.meter_id == :all + end + end + + describe "Openflow.Multipart.Meter.Reply" do + test "with test packet_data" do + meter_stats = + "test/packet_data/4-50-ofp_meter_stats_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.Meter.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + meters: [ + %Openflow.Multipart.Meter{ + band_stats: [%{byte_band_count: 0, packet_band_count: 0}], + byte_in_count: 0, + duration_nsec: 480_000, + duration_sec: 0, + flow_count: 0, + meter_id: 100, + packet_in_count: 0 + } + ], + version: 4, + xid: 0 + } = meter_stats + end + end +end diff --git a/test/lib/openflow/ofp_packet_in2_test.exs b/test/lib/openflow/ofp_packet_in2_test.exs new file mode 100644 index 0000000..addb797 --- /dev/null +++ b/test/lib/openflow/ofp_packet_in2_test.exs @@ -0,0 +1,53 @@ +defmodule OfpPacketIn2Test do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with NX_PACKET_IN2 packet(without continuations)" do + {:ok, pktin, ""} = + "test/packet_data/nx_packet_in2.raw" + |> File.read!() + |> Openflow.read() + + assert pktin.version == 1 + assert pktin.xid == 0 + assert pktin.full_len == 64 + assert pktin.table_id == 7 + assert pktin.buffer_id == 0x114 + assert pktin.cookie == 0xFEDCBA9876543210 + assert pktin.reason == :action + assert pktin.metadata == [metadata: 0x5A5A5A5A5A5A5A5A] + assert pktin.userdata == <<1, 2, 3, 4, 5>> + end + + test "with NX_PACKET_IN2 packet(with continuations)" do + pktin = + "test/packet_data/4-64-ofp_nx_packet_in2.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + assert pktin.continuation_action_set == nil + + assert pktin.continuation_actions == [ + %Openflow.Action.NxResubmitTable{in_port: :in_port, table_id: 5} + ] + + assert pktin.continuation_bridge == + <<8, 137, 105, 58, 5, 77, 183, 237, 163, 58, 25, 166, 212, 167, 209, 11>> + + assert pktin.continuation_conntracked == nil + assert pktin.continuation_cookie == nil + assert pktin.continuation_mirrors == nil + assert pktin.continuation_stack == nil + assert pktin.continuation_table_id == nil + assert pktin.cookie == 0 + assert pktin.full_len == nil + assert pktin.metadata == [in_port: 2, reg0: 3_232_235_778, reg1: 1] + assert byte_size(pktin.packet) == 98 + assert pktin.reason == :action + assert pktin.table_id == 0 + assert byte_size(pktin.userdata) == 49 + end + end +end diff --git a/test/ofp_packet_in_test.exs b/test/lib/openflow/ofp_packet_in_test.exs similarity index 100% rename from test/ofp_packet_in_test.exs rename to test/lib/openflow/ofp_packet_in_test.exs diff --git a/test/ofp_packet_out_test.exs b/test/lib/openflow/ofp_packet_out_test.exs similarity index 100% rename from test/ofp_packet_out_test.exs rename to test/lib/openflow/ofp_packet_out_test.exs diff --git a/test/lib/openflow/ofp_port_desc_stats_test.exs b/test/lib/openflow/ofp_port_desc_stats_test.exs new file mode 100644 index 0000000..80bdff2 --- /dev/null +++ b/test/lib/openflow/ofp_port_desc_stats_test.exs @@ -0,0 +1,62 @@ +defmodule OfpPortDescTest do + use ExUnit.Case + + describe "Openflow.Multipart.PortDesc.Request" do + test "with default values" do + desc = Openflow.Multipart.PortDesc.Request.new(0) + + desc + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(desc) + |> assert() + end + end + + describe "Openflow.Multipart.PortDesc.Reply" do + test "with test packet_data" do + port_desc_stats = + "test/packet_data/4-54-ofp_port_desc_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.PortDesc.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + ports: [ + %Openflow.Port{ + advertised_features: [:copper, :autoneg], + config: [], + current_features: [:"100mb_fd", :copper, :autoneg], + current_speed: 5000, + hw_addr: "f20ba47df8ea", + max_speed: 5000, + name: "Port6", + number: 6, + peer_features: [:"100mb_fd", :copper, :autoneg], + state: [:live], + supported_features: [:"100mb_fd", :copper, :autoneg] + }, + %Openflow.Port{ + advertised_features: [:copper, :autoneg], + config: [], + current_features: [:"100mb_fd", :copper, :autoneg], + current_speed: 5000, + hw_addr: "f20ba4d03f70", + max_speed: 5000, + name: "Port7", + number: 7, + peer_features: [:"100mb_fd", :copper, :autoneg], + state: [:live], + supported_features: [:"100mb_fd", :copper, :autoneg] + } + ], + version: 4, + xid: 0 + } = port_desc_stats + end + end +end diff --git a/test/ofp_port_mod_test.exs b/test/lib/openflow/ofp_port_mod_test.exs similarity index 100% rename from test/ofp_port_mod_test.exs rename to test/lib/openflow/ofp_port_mod_test.exs diff --git a/test/lib/openflow/ofp_port_stats_test.exs b/test/lib/openflow/ofp_port_stats_test.exs new file mode 100644 index 0000000..ef3fddf --- /dev/null +++ b/test/lib/openflow/ofp_port_stats_test.exs @@ -0,0 +1,72 @@ +defmodule OfpPortStatsTest do + use ExUnit.Case + + describe "Openflow.Multipart.Port.Request" do + test "with default values" do + port_stats = + %Openflow.Multipart.Port.Request{} + |> Map.to_list() + |> Openflow.Multipart.Port.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert port_stats.xid == 0 + assert port_stats.port_number == :any + end + end + + describe "Openflow.Multipart.Port.Reply" do + test "with test packet_data" do + port_stats = + "test/packet_data/4-30-ofp_port_stats_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.Port.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + ports: [ + %Openflow.Multipart.PortStats{ + collisions: 0, + duration_nsec: 0, + duration_sec: 0, + port_number: 7, + rx_bytes: 0, + rx_crc_err: 0, + rx_dropped: 0, + rx_errors: 0, + rx_frame_err: 0, + rx_over_err: 0, + rx_packets: 0, + tx_bytes: 336, + tx_dropped: 0, + tx_errors: 0, + tx_packets: 4 + }, + %Openflow.Multipart.PortStats{ + collisions: 0, + duration_nsec: 0, + duration_sec: 0, + port_number: 6, + rx_bytes: 336, + rx_crc_err: 0, + rx_dropped: 0, + rx_errors: 0, + rx_frame_err: 0, + rx_over_err: 0, + rx_packets: 4, + tx_bytes: 336, + tx_dropped: 0, + tx_errors: 0, + tx_packets: 4 + } + ], + version: 4, + xid: 0 + } = port_stats + end + end +end diff --git a/test/ofp_port_status_test.exs b/test/lib/openflow/ofp_port_status_test.exs similarity index 100% rename from test/ofp_port_status_test.exs rename to test/lib/openflow/ofp_port_status_test.exs diff --git a/test/lib/openflow/ofp_queue_stats_test.exs b/test/lib/openflow/ofp_queue_stats_test.exs new file mode 100644 index 0000000..71c0944 --- /dev/null +++ b/test/lib/openflow/ofp_queue_stats_test.exs @@ -0,0 +1,66 @@ +defmodule OfpQueueStatsTest do + use ExUnit.Case + + describe "Openflow.Multipart.Queue.Request" do + test "with default values" do + queue_stats = + %Openflow.Multipart.Queue.Request{} + |> Map.to_list() + |> Openflow.Multipart.Queue.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert queue_stats.xid == 0 + assert queue_stats.port_number == :any + assert queue_stats.queue_id == :all + end + end + + describe "Openflow.Multipart.Queue.Reply" do + test "with test packet_data" do + queue_stats = + "test/packet_data/4-38-ofp_queue_stats_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.Queue.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + queues: [ + %Openflow.Multipart.QueueStats{ + duration_nsec: 0, + duration_sec: 0, + port_number: 7, + queue_id: 1, + tx_bytes: 0, + tx_errors: 0, + tx_packets: 0 + }, + %Openflow.Multipart.QueueStats{ + duration_nsec: 0, + duration_sec: 0, + port_number: 6, + queue_id: 1, + tx_bytes: 0, + tx_errors: 0, + tx_packets: 0 + }, + %Openflow.Multipart.QueueStats{ + duration_nsec: 0, + duration_sec: 0, + port_number: 7, + queue_id: 2, + tx_bytes: 0, + tx_errors: 0, + tx_packets: 0 + } + ], + version: 4, + xid: 0 + } = queue_stats + end + end +end diff --git a/test/lib/openflow/ofp_resume_test.exs b/test/lib/openflow/ofp_resume_test.exs new file mode 100644 index 0000000..494d30e --- /dev/null +++ b/test/lib/openflow/ofp_resume_test.exs @@ -0,0 +1,36 @@ +defmodule OfpResumeTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with NX_RESUME packet(with continuations)" do + pktin = + "test/packet_data/4-65-ofp_nx_resume.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + assert pktin.continuation_action_set == nil + + assert pktin.continuation_actions == [ + %Openflow.Action.NxResubmitTable{in_port: :in_port, table_id: 5} + ] + + assert pktin.continuation_bridge == + <<8, 137, 105, 58, 5, 77, 183, 237, 163, 58, 25, 166, 212, 167, 209, 11>> + + assert pktin.continuation_conntracked == nil + assert pktin.continuation_cookie == nil + assert pktin.continuation_mirrors == nil + assert pktin.continuation_stack == nil + assert pktin.continuation_table_id == nil + assert pktin.cookie == 0 + assert pktin.full_len == nil + assert pktin.metadata == [in_port: :controller, reg0: 3_232_235_778, reg1: 1] + assert byte_size(pktin.packet) == 42 + assert pktin.reason == nil + assert pktin.table_id == 0 + assert byte_size(pktin.userdata) == 49 + end + end +end diff --git a/test/ofp_set_config_test.exs b/test/lib/openflow/ofp_set_config_test.exs similarity index 53% rename from test/ofp_set_config_test.exs rename to test/lib/openflow/ofp_set_config_test.exs index fd7b7e6..d917ff6 100644 --- a/test/ofp_set_config_test.exs +++ b/test/lib/openflow/ofp_set_config_test.exs @@ -14,6 +14,20 @@ defmodule OfpSetConfigTest do assert config.flags == [] assert config.miss_send_len == 128 end + + test "with a flag option" do + binary = <<0x04, 0x09, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x02, 0xFF, 0xE5>> + + set_config = + binary + |> Openflow.read() + |> Kernel.elem(1) + + assert set_config.version == 4 + assert set_config.xid == 13 + assert set_config.flags == [:fragment_reassemble] + assert set_config.miss_send_len == :max + end end describe "Openflow.to_binary/1" do @@ -31,5 +45,14 @@ defmodule OfpSetConfigTest do assert Openflow.to_binary(config) == expect end + + test "with a flag option" do + binary = <<0x04, 0x09, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x02, 0xFF, 0xE5>> + + set_config = + Openflow.SetConfig.new(xid: 13, flags: [:fragment_reassemble], miss_send_len: :max) + + ^binary = Openflow.to_binary(set_config) + end end end diff --git a/test/lib/openflow/ofp_set_packet_in_format_test.exs b/test/lib/openflow/ofp_set_packet_in_format_test.exs new file mode 100644 index 0000000..356f5c9 --- /dev/null +++ b/test/lib/openflow/ofp_set_packet_in_format_test.exs @@ -0,0 +1,23 @@ +defmodule OfpSetPacketInFormatTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.to_binary/1" do + test "with Openflow.SetPacket_In_Format.new/1" do + packet_in_format = + "test/packet_data/nx_set_packet_in_format.raw" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + packet_in_format + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Map.to_list() + |> Openflow.NxSetPacketInFormat.new() + |> Kernel.==(packet_in_format) + |> assert() + end + end +end diff --git a/test/ofp_table_mod_test.exs b/test/lib/openflow/ofp_table_mod_test.exs similarity index 100% rename from test/ofp_table_mod_test.exs rename to test/lib/openflow/ofp_table_mod_test.exs diff --git a/test/lib/openflow/ofp_table_stats_test.exs b/test/lib/openflow/ofp_table_stats_test.exs new file mode 100644 index 0000000..5ad4228 --- /dev/null +++ b/test/lib/openflow/ofp_table_stats_test.exs @@ -0,0 +1,49 @@ +defmodule OfpTableStatsTest do + use ExUnit.Case + + describe "Openflow.Multipart.Table.Request" do + test "with default values" do + table_stats = + %Openflow.Multipart.Table.Request{} + |> Map.to_list() + |> Openflow.Multipart.Table.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + + assert table_stats.xid == 0 + end + end + + describe "Openflow.Multipart.Table.Reply" do + test "with test packet_data" do + table_stats = + "test/packet_data/4-28-ofp_table_stats_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + %Openflow.Multipart.Table.Reply{ + aux_id: nil, + datapath_id: nil, + flags: [], + tables: [ + %Openflow.Multipart.TableStats{ + active_count: 4, + lookup_count: 4, + matched_count: 4, + table_id: 0 + }, + %Openflow.Multipart.TableStats{ + active_count: 4, + lookup_count: 4, + matched_count: 4, + table_id: 1 + } + ], + version: 4, + xid: 0 + } = table_stats + end + end +end diff --git a/test/lib/openflow/ofp_tables_features_test.exs b/test/lib/openflow/ofp_tables_features_test.exs new file mode 100644 index 0000000..46157f8 --- /dev/null +++ b/test/lib/openflow/ofp_tables_features_test.exs @@ -0,0 +1,31 @@ +defmodule OfpTableFeaturesTest do + use ExUnit.Case + + describe "Openflow.Multipart.Table.Request" do + test "with default values" do + table_features = + "test/packet_data/4-55-ofp_table_features_request.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + + table_features + |> Map.to_list() + |> Openflow.Multipart.TableFeatures.Request.new() + |> Openflow.to_binary() + |> Openflow.read() + |> Kernel.elem(1) + |> Kernel.==(table_features) + |> assert() + end + end + + describe "Openflow.Multipart.Table.Reply" do + test "with default values" do + "test/packet_data/4-56-ofp_table_features_reply.packet" + |> File.read!() + |> Openflow.read() + |> Kernel.elem(1) + end + end +end diff --git a/test/lib/tres/handler_test.exs b/test/lib/tres/handler_test.exs new file mode 100644 index 0000000..d138a9f --- /dev/null +++ b/test/lib/tres/handler_test.exs @@ -0,0 +1,238 @@ +defmodule Tres.HanderTest do + use ExUnit.Case, async: false + + @datapath_id "0000000000000001" + + setup_all do + :ok = wait_datapath_is_connected() + :ok = send_message(Openflow.FlowMod.new(command: :delete, table_id: :all)) + :ok = send_message(Openflow.GroupMod.new(command: :delete, group_id: :all)) + :ok = send_message(Openflow.MeterMod.new(command: :delete, meter_id: :all)) + end + + describe "Openflow RoleRequest message" do + test "with role and generation_id" do + {:ok, %Openflow.Role.Reply{}} = + sync_send_message(Openflow.Role.Request.new(role: :nochange, generation_id: 1)) + end + end + + describe "Openflow TableMod message" do + test "with table_id" do + {:ok, :noreply} = sync_send_message(Openflow.TableMod.new(table_id: :all)) + end + end + + describe "Openflow NxSetControllerId message" do + test "with controller_id" do + {:ok, :noreply} = sync_send_message(Openflow.NxSetControllerId.new(0)) + end + end + + describe "Openflow Error message" do + test "with a flow" do + send_message(Openflow.FlowMod.new(match: Openflow.Match.new(arp_tpa: {10, 10, 10, 10}))) + %Openflow.ErrorMsg{} = get_message() + end + end + + describe "Openflow AsyncConfig message" do + test "with master and slave config" do + send_message( + Openflow.SetAsync.new( + flow_removed_mask_master: [:idle_timeout, :hard_timeout, :delete, :group_delete], + flow_removed_mask_slave: [], + packet_in_mask_master: [:no_match, :action], + packet_in_mask_slave: [], + port_status_mask_master: [:add, :delete, :modify], + port_status_mask_slave: [] + ) + ) + + {:ok, + %Openflow.GetAsync.Reply{ + flow_removed_mask_master: [:idle_timeout, :hard_timeout, :delete, :group_delete], + flow_removed_mask_slave: [], + packet_in_mask_master: [:no_match, :action], + packet_in_mask_slave: [], + port_status_mask_master: [:add, :delete, :modify], + port_status_mask_slave: [] + }} = sync_send_message(Openflow.GetAsync.Request.new()) + end + end + + describe "Openflow standard PacketIn message" do + test "with arp_packet" do + send_message( + Openflow.PacketOut.new( + buffer_id: :no_buffer, + in_port: :controller, + actions: [Openflow.Action.Output.new(:controller)], + data: File.read!("test/packet_data/arp_packet.raw") + ) + ) + + %Openflow.PacketIn{} = get_message() + end + end + + describe "Openflow Nicira PacketIn2 message" do + test "with arp_packet" do + send_message(Openflow.NxSetPacketInFormat.new(:nxt_packet_in2)) + + send_message( + Openflow.FlowMod.new( + priority: 0xFFFF, + match: Openflow.Match.new(in_port: 0xF, reg0: 99, eth_type: 0x0806), + instructions: + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.NxController2.new(pause: true), + Openflow.Action.NxResubmitTable.new() + ]) + ) + ) + + send_message( + Openflow.PacketOut.new( + buffer_id: :no_buffer, + in_port: 0xF, + actions: [ + Openflow.Action.SetField.new(reg0: 99), + Openflow.Action.Output.new(:table) + ], + data: File.read!("test/packet_data/arp_packet.raw") + ) + ) + + %Openflow.NxPacketIn2{} = packet_in2 = get_message() + {:ok, :noreply} = sync_send_message(Openflow.NxResume.new(packet_in: packet_in2)) + %Openflow.NxPacketIn2{} = get_message() + send_message(Openflow.NxSetPacketInFormat.new(:standard)) + end + end + + describe "Openflow FlowRemoved message" do + test "with a flow" do + send_message( + Openflow.FlowMod.new(flags: [:send_flow_rem], match: Openflow.Match.new(reg0: 99)) + ) + + send_message(Openflow.FlowMod.new(command: :delete, match: Openflow.Match.new(reg0: 99))) + %Openflow.FlowRemoved{} = get_message() + end + end + + describe "Openflow AggregateStats Reply message" do + test "with no option" do + send_message(Openflow.Multipart.Aggregate.Request.new()) + %Openflow.Multipart.Aggregate.Reply{} = get_message() + end + end + + describe "Openflow DescStats Reply message" do + test "with no option" do + send_message(Openflow.Multipart.Desc.Request.new()) + %Openflow.Multipart.Desc.Reply{} = get_message() + end + end + + describe "Openflow FlowStats Reply message" do + test "with no option" do + send_message(Openflow.Multipart.Flow.Request.new()) + %Openflow.Multipart.Flow.Reply{} = get_message() + end + + test "with 3000 flows" do + Enum.each(1..3000, fn n -> + send_message(Openflow.FlowMod.new(match: Openflow.Match.new(reg0: n))) + end) + + send_message(Openflow.Multipart.Flow.Request.new()) + %Openflow.Multipart.Flow.Reply{flags: [:more]} = get_message() + end + end + + describe "Openflow GroupStats Reply message" do + test "with no option" do + send_message(Openflow.Multipart.Group.Request.new()) + %Openflow.Multipart.Group.Reply{} = get_message() + end + + test "with 3000 groups" do + Enum.each(1..3000, fn n -> send_message(Openflow.GroupMod.new(group_id: n)) end) + send_message(Openflow.Multipart.Group.Request.new()) + %Openflow.Multipart.Group.Reply{flags: [:more]} = get_message() + end + end + + describe "Openflow GroupDesc Reply message" do + test "with no option" do + send_message(Openflow.Multipart.GroupDesc.Request.new()) + %Openflow.Multipart.GroupDesc.Reply{} = get_message() + end + end + + describe "Openflow GroupFeatures Reply message" do + test "with no option" do + send_message(Openflow.Multipart.GroupFeatures.Request.new()) + %Openflow.Multipart.GroupFeatures.Reply{} = get_message() + end + end + + describe "Openflow MeterStats Reply message" do + test "with no option" do + send_message(Openflow.Multipart.Meter.Request.new()) + %Openflow.Multipart.Meter.Reply{} = get_message() + end + + test "with 3000 meters" do + Enum.each(1..3000, fn n -> + send_message( + Openflow.MeterMod.new( + meter_id: n, + flags: [:pktps, :burst, :stats], + bands: [Openflow.MeterBand.Drop.new(rate: 1000, burst_size: 10)] + ) + ) + end) + + send_message(Openflow.Multipart.Meter.Request.new()) + %Openflow.Multipart.Meter.Reply{flags: [:more]} = get_message() + end + end + + describe "Openflow PortDescStats Reply message" do + test "with no option" do + send_message(Openflow.Multipart.PortDesc.Request.new()) + %Openflow.Multipart.PortDesc.Reply{} = get_message() + end + end + + describe "Openflow PortStats Reply message" do + test "with no option" do + send_message(Openflow.Multipart.Port.Request.new()) + %Openflow.Multipart.Port.Reply{} = get_message() + end + end + + # helper + + def wait_datapath_is_connected do + case Tres.SwitchRegistry.lookup_handler_pid(@datapath_id) do + nil -> wait_datapath_is_connected() + pid when is_pid(pid) -> :ok + end + end + + def sync_send_message(msg) do + Tres.SwitchRegistry.send_message(msg, @datapath_id, true) + end + + def send_message(msg) do + :ok = Tres.ExampleHandler.send(@datapath_id, msg) + end + + def get_message do + Tres.ExampleHandler.get(@datapath_id) + end +end diff --git a/test/lib/tres/secure_channel_test.exs b/test/lib/tres/secure_channel_test.exs new file mode 100644 index 0000000..8e3a9c7 --- /dev/null +++ b/test/lib/tres/secure_channel_test.exs @@ -0,0 +1,98 @@ +defmodule Tres.SecureChannelTest do + use ExUnit.Case, async: false + + import ExUnit.CaptureLog + + @datapath_id "0000000000000004" + + @host 'localhost' + @port 6653 + @client_opts [:binary, {:packet, 0}, {:active, true}] + + @ofp_hello 0 + @ofp_echo_request 2 + @ofp_features_request 5 + + describe "Tres.SecureChannel" do + test "if hello message sending is slow" do + {:ok, socket} = connect() + assert_receive {:tcp, ^socket, <<4::8, @ofp_hello::8, _::binary>>}, 4000 + assert_receive {:tcp_closed, ^socket}, 4000 + end + + test "if ofp_version in the hello message is mismatched" do + {:ok, socket} = connect() + assert_receive {:tcp, ^socket, <<4::8, @ofp_hello::8, _::binary>>}, 4000 + :ok = send_msg(socket, Openflow.Hello.new(0x05)) + assert_receive {:tcp_closed, ^socket}, 4000 + end + + test "if feature_reply message sending is slow" do + {:ok, socket} = connect() + assert_receive {:tcp, ^socket, <<4::8, @ofp_hello::8, _::binary>>}, 4000 + :ok = send_msg(socket, Openflow.Hello.new(0x04)) + assert_receive {:tcp, ^socket, <<4::8, @ofp_features_request::8, _::binary>>}, 4000 + assert_receive {:tcp_closed, ^socket}, 4000 + end + + test "if the datapath didn't reply to the ping from controller" do + {:ok, socket} = connect() + assert_receive {:tcp, ^socket, <<4::8, @ofp_hello::8, _::binary>>}, 4000 + :ok = send_msg(socket, Openflow.Hello.new(0x04)) + assert_receive {:tcp, ^socket, <<4::8, @ofp_features_request::8, _::binary>>}, 4000 + :ok = send_msg(socket, %Openflow.Features.Reply{datapath_id: @datapath_id}) + + # Idle check running after 5sec + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + + # Controller tries ping to the datapath per five seconds + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp, ^socket, <<4::8, @ofp_echo_request::8, _::binary>>}, 6000 + assert_receive {:tcp_closed, ^socket}, 60_000 + end + + test "if the main connection down" do + # Main connection + {:ok, socket_main} = connect() + assert_receive {:tcp, ^socket_main, <<4::8, @ofp_hello::8, _::binary>>}, 4000 + :ok = send_msg(socket_main, Openflow.Hello.new(0x04)) + assert_receive {:tcp, ^socket_main, <<4::8, @ofp_features_request::8, _::binary>>}, 4000 + :ok = send_msg(socket_main, %Openflow.Features.Reply{datapath_id: @datapath_id}) + + # Aux connection(id: 1) + {:ok, socket_aux} = connect() + assert_receive {:tcp, ^socket_aux, <<4::8, @ofp_hello::8, _::binary>>}, 4000 + :ok = send_msg(socket_aux, Openflow.Hello.new(0x04)) + assert_receive {:tcp, ^socket_aux, <<4::8, @ofp_features_request::8, _::binary>>}, 4000 + :ok = send_msg(socket_aux, %Openflow.Features.Reply{datapath_id: @datapath_id, aux_id: 1}) + + # Close the main connection + :ok = :gen_tcp.close(socket_main) + + # We expect that the aux connection was closed + assert_receive {:tcp_closed, ^socket_aux}, 60_000 + end + end + + # Helper + + defp connect do + {:ok, _socket} = :gen_tcp.connect(@host, @port, @client_opts) + end + + defp close(socket) do + :ok = :gen_tcp.close(socket) + end + + defp send_msg(socket, msg) do + :ok = :gen_tcp.send(socket, Openflow.to_binary(msg)) + end +end diff --git a/test/ofp_action_test.exs b/test/ofp_action_test.exs deleted file mode 100644 index 146b27d..0000000 --- a/test/ofp_action_test.exs +++ /dev/null @@ -1,630 +0,0 @@ -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 deleted file mode 100644 index 5e21fed..0000000 --- a/test/ofp_echo_test.exs +++ /dev/null @@ -1,58 +0,0 @@ -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/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 deleted file mode 120000 index 07c9d43..0000000 --- a/test/packet_data/.#nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw +++ /dev/null @@ -1 +0,0 @@ -shun159@shun159.19402:1510242034 \ No newline at end of file diff --git a/test/packet_data/4-64-ofp_nx_packet_in2.packet b/test/packet_data/4-64-ofp_nx_packet_in2.packet new file mode 100644 index 0000000..6ab95f5 Binary files /dev/null and b/test/packet_data/4-64-ofp_nx_packet_in2.packet differ diff --git a/test/packet_data/4-65-ofp_nx_resume.packet b/test/packet_data/4-65-ofp_nx_resume.packet new file mode 100644 index 0000000..5ae14b3 Binary files /dev/null and b/test/packet_data/4-65-ofp_nx_resume.packet differ diff --git a/test/packet_data/arp_packet.raw b/test/packet_data/arp_packet.raw new file mode 100644 index 0000000..390a556 Binary files /dev/null and b/test/packet_data/arp_packet.raw differ diff --git a/test/packet_data/nx_clone.raw b/test/packet_data/nx_clone.raw index 47e62dd..2af69bc 100644 Binary files a/test/packet_data/nx_clone.raw and b/test/packet_data/nx_clone.raw differ diff --git a/test/packet_data/nx_reg_load2.raw b/test/packet_data/nx_reg_load2.raw new file mode 100644 index 0000000..08a2b89 Binary files /dev/null and b/test/packet_data/nx_reg_load2.raw differ diff --git a/test/packet_data/onf_bundle_control.raw b/test/packet_data/onf_bundle_control.raw new file mode 100644 index 0000000..1d97178 Binary files /dev/null and b/test/packet_data/onf_bundle_control.raw differ diff --git a/test/test_helper.exs b/test/test_helper.exs index 135c19a..5a9a105 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,2 +1,2 @@ ExUnit.configure(seed: 0) -ExUnit.start(trace: true) +ExUnit.start(trace: true, capture_log: true) diff --git a/test/tres_test.exs b/test/tres_test.exs deleted file mode 100644 index 24ae030..0000000 --- a/test/tres_test.exs +++ /dev/null @@ -1,4 +0,0 @@ -defmodule TresTest do - use ExUnit.Case - doctest Tres -end