Merge branch 'release/0.1.0'

This commit is contained in:
Eishun Kondoh 2018-03-29 23:34:55 +09:00
commit 1fe53969d5
389 changed files with 25797 additions and 0 deletions

18
.formatter.exs Normal file
View file

@ -0,0 +1,18 @@
[
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"],
locals_without_parens: [
# Formatter tests
assert_format: 2,
assert_format: 3,
assert_same: 1,
assert_same: 2,
# Errors tests
assert_eval_raise: 3,
# Mix tests
in_fixture: 2,
in_tmp: 2
]
]

23
.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
# 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
# Examples
*/polaris/*

13
LICENSE Normal file
View file

@ -0,0 +1,13 @@
Copyright [2018] [Eishun Kondoh]
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
http://www.apache.org/licenses/LICENSE-2.0
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.

78
README.md Normal file
View file

@ -0,0 +1,78 @@
# Tres - an Elixir OpenFlow development platform
## Overview
Tres is a framework and set of helper libraries to develop OpenFlow controllers in Elixir.
## How to build your own controller application
### Installation
```elixir
def deps do
[
{:tres, github: "shun159/tres", branch: "develop"}
]
end
```
### callbacks
```elixir
config :tres,
callback_module: Tres.ExampleHandler,
callback_args: []
```
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`.
Set the callback_args to the terms you want pass to the `start_link/2` callback function.
```
% cat lib/sample.ex
defmodule Sample do
use GenServer
use Tres.Controller
def start_link(datapath_id, _start_args) do
GenServer.start_link(__MODULE__, [datapath_id])
end
def init([datapath_id]) do
# As an example create a flow to receive all packet_ins from port 1
redirect_action = Output.new(:controller)
apply_ins = ApplyActions.new(redirect_action)
match = Match.new(in_port: 1)
:ok = send_flow_mod_add(datapath_id,
priority: 1,
match: match,
instructions: [apply_ins])
{:ok, datapath_id}
end
def handle_info(%PacketIn{} = packet_in, datapath_id) do
# print the packet_in message struct
IO.inspect(packet_in)
{:noreply, datapath_id}
end
end
```
## To run the controller
```bash
$ iex -S mix
```
## Examples
- learning-switch: Simple Layer2 switch
- leader-example: Simple election based multiple controller using Ulf Wiger's Locks Leader
- patch\_panel: inteligent patch\_panel example
License
-------
Tres is released under the Apache license Version 2.0:
* https://www.apache.org/licenses/LICENSE-2.0

BIN
bin/enum_gen Executable file

Binary file not shown.

17
config/config.exs Normal file
View file

@ -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: Tres.ExampleHandler,
callback_args: []
config :logger,
level: :info,
format: "$date $time [$level] $metadata$message\n",
metadata: [:application],
handle_otp_reports: true

View file

@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

24
examples/leader_example/.gitignore vendored Normal file
View file

@ -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").
leader_example-*.tar

View file

@ -0,0 +1,21 @@
# LeaderExample
**TODO: Add description**
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `leader_example` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:leader_example, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/leader_example](https://hexdocs.pm/leader_example).

View file

@ -0,0 +1,21 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
config :libcluster,
topologies: [
gossip_example: [
strategy: Cluster.Strategy.Gossip,
config: [
port: 45_892,
if_addr: {0, 0, 0, 0},
multicast_addr: {230, 1, 1, 251},
multicast_ttl: 1]]]
config :logger,
level: :debug,
format: "$date $time [$level] $metadata$message\n",
metadata: [:application],
handle_otp_reports: true
import_config "#{Mix.env}.exs"

View file

@ -0,0 +1,3 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

View file

@ -0,0 +1,9 @@
use Mix.Config
config :tres,
protocol: :tcp,
port: 6653,
max_connections: 10,
num_acceptors: 10,
callback_module: LeaderExample.Leader,
callback_args: []

View file

@ -0,0 +1,9 @@
use Mix.Config
config :tres,
protocol: :tcp,
port: 6633,
max_connections: 10,
num_acceptors: 10,
callback_module: LeaderExample.Leader,
callback_args: []

View file

@ -0,0 +1,20 @@
defmodule LeaderExample.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
# Starts a worker by calling: LeaderExample.Worker.start_link(arg)
# {LeaderExample.Worker, arg},
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: LeaderExample.Supervisor]
Supervisor.start_link(children, opts)
end
end

View file

@ -0,0 +1,77 @@
defmodule LeaderExample.Leader do
use Tres.Controller
@behaviour :locks_leader
require Logger
def start_link(datapath_id, args) do
:locks_leader.start_link(__MODULE__, [datapath_id, args], [])
end
def init([{datapath_id, _aux_id}, _args]) do
:ok = Logger.debug("Switch Ready: datapath_id: #{inspect(datapath_id)}")
:ok = send_role_request(datapath_id, role: :slave, generation_id: 0)
{:ok, %{am_leader: false, gen_id: 0, datapath_id: datapath_id}}
end
def elected(state, _info, _cand) do
:ok = Logger.info("Elected: #{inspect(Node.self)} for #{state.datapath_id} gen_id: #{state.gen_id}")
:ok = send_role_request(state.datapath_id, role: :master, generation_id: state.gen_id)
{:ok, {:elected, {Node.self(), state.gen_id + 1}}, %{state|am_leader: true}}
end
def surrendered(%{am_leader: true} = state, {:elected, {_node, next_gen_id}}, _info) do
:ok = Logger.info("network split possible detected")
:ok = send_role_request(state.datapath_id, role: :slave, generation_id: state.gen_id)
{:ok, %{state|am_leader: false, gen_id: next_gen_id}}
end
def surrendered(state, {:elected, {node, next_gen_id}}, _info) do
:ok = Logger.info("Surrendered: elected node is #{node} for #{state.datapath_id}")
:ok = send_role_request(state.datapath_id, role: :slave, generation_id: state.gen_id)
{:ok, %{state|am_leader: false, gen_id: next_gen_id}}
end
def handle_DOWN(_pid, state, _info) do
:ok = Logger.warn("DOWN detected")
{:ok, state}
end
def handle_leader_call(msg, from, state, _info) do
:ok = Logger.info("leader call with: #{inspect(msg)} from: #{inspect(from)} on #{Node.self()}")
{:reply, :ok, state}
end
def handle_leader_cast(_msg, state, _info) do
{:ok, state}
end
def from_leader(_from_leader, state, _info) do
{:ok, state}
end
def handle_call(_msg, _from, state, _info) do
{:reply, :ok, state}
end
def handle_cast(_msg, state, _info) do
{:noreply, state}
end
def handle_info(%Role.Reply{datapath_id: dpid} = role, %{datapath_id: dpid} = state, _info) do
:ok = Logger.info("#{Node.self()} is on #{role.role} for #{dpid}")
{:noreply, state}
end
def handle_info(_msg, state, _info) do
{:noreply, state}
end
def code_change(_from_vsn, state, _info, _extra) do
{:ok, state}
end
def terminate(_reason, _state) do
:ok
end
end

View file

@ -0,0 +1,25 @@
defmodule LeaderExample.MixProject do
use Mix.Project
def project do
[
app: :leader_example,
version: "0.1.0",
elixir: "~> 1.6",
deps: deps()
]
end
def application do
[
extra_applications: [:logger, :libcluster, :locks, :tres],
mod: {LeaderExample.Application, []}
]
end
defp deps do
[{:locks, "~> 0.2.0"},
{:libcluster, "~> 2.3.0"},
{:tres, path: "../../../tres"}]
end
end

View file

@ -0,0 +1,10 @@
%{
"eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]},
"jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]},
"libcluster": {:hex, :libcluster, "2.3.0", "d2fb8e8b2054a4a0c7faf7ced7c41dd13f7b49cce689590d37ecf30098e1b344", [:mix], [{:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"locks": {:hex, :locks, "0.2.0", "c8a5db52bdbfacfcc979b38920adb2ab8909fa7fb36015ddff6b753d1c950b92", [:rebar3], [{:plain_fsm, "1.4.1", [hex: :plain_fsm, repo: "hexpm", optional: false]}], "hexpm"},
"plain_fsm": {:hex, :plain_fsm, "1.4.1", "47e9bf6ac9322fc7586fb6df8de7198391e93764571c75165f2c45b27acde1d0", [:rebar3], [], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"},
"uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]},
}

View file

@ -0,0 +1,8 @@
defmodule LeaderExampleTest do
use ExUnit.Case
doctest LeaderExample
test "greets the world" do
assert LeaderExample.hello() == :world
end
end

View file

@ -0,0 +1 @@
ExUnit.start()

20
examples/learning_switch/.gitignore vendored Normal file
View file

@ -0,0 +1,20 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez

View file

@ -0,0 +1,21 @@
# LearningSwitch
**TODO: Add description**
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `learning_switch` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:learning_switch, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/learning_switch](https://hexdocs.pm/learning_switch).

View file

@ -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: LearningSwitch.Ofctl,
callback_args: []
config :logger,
level: :debug,
format: "$date $time [$level] $metadata$message\n",
metadata: [:application],
handle_otp_reports: true

View file

@ -0,0 +1,18 @@
defmodule LearningSwitch do
@moduledoc """
Documentation for LearningSwitch.
"""
@doc """
Hello world.
## Examples
iex> LearningSwitch.hello
:world
"""
def hello do
:world
end
end

View file

@ -0,0 +1,20 @@
defmodule LearningSwitch.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
# Starts a worker by calling: LearningSwitch.Worker.start_link(arg)
# {LearningSwitch.Worker, arg},
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: LearningSwitch.Supervisor]
Supervisor.start_link(children, opts)
end
end

View file

@ -0,0 +1,66 @@
defmodule LearningSwitch.FDB do
use Agent
defmodule Entry do
defstruct [
mac: nil,
port_no: nil,
age_max: 0,
last_update: 0
]
def new(mac, port_no, age_max \\ 180) do
%Entry{
mac: mac,
port_no: port_no,
age_max: age_max,
last_update: :os.timestamp
}
end
def update(self, port_no) do
%{self|port_no: port_no, last_update: :os.timestamp}
end
def aged_out?(self) do
:timer.now_diff(:os.timestamp, self.last_update) > (1000 * self.age_max)
end
end
def start_link do
Agent.start_link(&Map.new/0)
end
def lookup(self, mac) do
entry = Agent.get(self, &Map.get(&1, mac))
entry && entry.port_no
end
def learn(self, mac, port_no) do
entry = Agent.get(self, &Map.get(&1, mac))
entry = if entry do
Entry.update(entry, port_no)
else
Entry.new(mac, port_no)
end
Agent.update(self, &Map.put(&1, mac, entry))
end
def age(self) do
mac_addrs = Agent.get(self, &find_aged_entries(&1))
for mac <- mac_addrs do
Agent.update(self, &Map.delete(&1, mac))
end
end
# private function
defp find_aged_entries(map) do
Enum.flat_map(map, fn({mac, entry}) ->
case Entry.aged_out?(entry) do
true -> [mac]
false -> []
end
end)
end
end

View file

@ -0,0 +1,137 @@
defmodule LearningSwitch.Ofctl do
use GenServer
use Tres.Controller
import Logger
alias LearningSwitch.FDB
@ingress_filtering_table_id 0
@forwarding_table_id 1
@aging_time 180
@mcast {"010000000000", "110000000000"}
@bcast "ffffffffffff"
@ipv6_mcast {"333300000000", "ffff00000000"}
defmodule State do
defstruct [
datapath_id: nil,
fdb_pid: nil
]
end
def start_link(datapath_id, args) do
GenServer.start_link(__MODULE__, [datapath_id, args])
end
def init([{datapath_id, _aux_id}, _args]) do
:ok = debug("Switch Ready: datapath_id: #{inspect(datapath_id)}")
{:ok, pid} = FDB.start_link
init_datapath(datapath_id)
state = %State{
datapath_id: datapath_id,
fdb_pid: pid
}
{:ok, state}
end
def handle_info(%PacketIn{} = packet_in, state) do
<<_dhost::6-bytes, shost::6-bytes, _rest::bytes>> = packet_in.data
eth_src = Openflow.Utils.to_hex_string(shost)
FDB.learn(state.fdb_pid, eth_src, packet_in.in_port)
add_forwarding_flow_and_packet_out(packet_in, state)
:ok = debug("PacketIn: eth_src: #{eth_src} datapath_id: #{inspect(state.datapath_id)}")
{:noreply, state}
end
def handle_info({:switch_disconnected, reason}, state) do
:ok = warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id} by #{reason}")
{:stop, :normal, state}
end
def handle_info(info, state) do
:ok = warn("Unhandled message #{inspect(info)}: #{inspect(state.datapath_id)}")
{:noreply, state}
end
# private functions
defp init_datapath(datapath_id) do
SetConfig.new(miss_send_len: :no_buffer)
|> send_message(datapath_id)
init_flow_tables(datapath_id)
end
defp init_flow_tables(datapath_id) do
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)
end
end
defp add_forwarding_flow_and_packet_out(packet_in, state) do
<<dhost::6-bytes, _shost::6-bytes, _rest::bytes>> = packet_in.data
eth_dst = Openflow.Utils.to_hex_string(dhost)
port_no = FDB.lookup(state.fdb_pid, eth_dst)
add_forwarding_flow_entry(packet_in, port_no)
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)]
)
end
defp add_forwarding_flow_entry(_packet_in, nil), do: :noop
defp add_forwarding_flow_entry(%PacketIn{datapath_id: datapath_id, data: data} = packet_in, port_no) do
<<dhost::6-bytes, shost::6-bytes, _rest::bytes>> = data
send_flow_mod_add(
datapath_id,
idle_timeout: @aging_time,
priority: 2,
match: Match.new(
in_port: packet_in.in_port,
eth_dst: Openflow.Utils.to_hex_string(dhost),
eth_src: Openflow.Utils.to_hex_string(shost)),
instructions: [ApplyActions.new(Output.new(port_no))]
)
end
defp add_default_broadcast_flow_entry do
[table_id: @forwarding_table_id,
priority: 3,
match: Match.new(eth_dst: @bcast),
instructions: [ApplyActions.new(Output.new(:flood))]]
end
defp add_default_flooding_flow_entry do
[table_id: @forwarding_table_id,
priority: 1,
instructions: [ApplyActions.new(Output.new(:controller))]]
end
defp add_multicast_mac_drop_flow_entry do
[table_id: @ingress_filtering_table_id,
priority: 2,
match: Match.new(eth_dst: @mcast)]
end
defp add_ipv6_multicast_mac_drop_flow_entry do
[table_id: @ingress_filtering_table_id,
priority: 2,
match: Match.new(eth_dst: @ipv6_mcast)]
end
defp add_default_forwarding_flow_entry do
[table_id: @ingress_filtering_table_id,
priority: 1,
instructions: [GotoTable.new(@forwarding_table_id)]]
end
end

View file

@ -0,0 +1,23 @@
defmodule LearningSwitch.Mixfile do
use Mix.Project
def project do
[
app: :learning_switch,
version: "0.1.0",
elixir: "~> 1.5",
start_permanent: Mix.env == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[extra_applications: [:logger, :tres],
mod: {LearningSwitch.Application, []}]
end
defp deps do
[{:tres, path: "../../../tres"}]
end
end

View file

@ -0,0 +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"},
"uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]},
}

View file

@ -0,0 +1,8 @@
defmodule LearningSwitchTest do
use ExUnit.Case
doctest LearningSwitch
test "greets the world" do
assert LearningSwitch.hello() == :world
end
end

View file

@ -0,0 +1 @@
ExUnit.start()

View file

@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

24
examples/patch_panel/.gitignore vendored Normal file
View file

@ -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").
patch_panel-*.tar

View file

@ -0,0 +1,16 @@
# PatchPanel
openflow controller that emulates a software patch panel
## prerequisites
- Erlang 20 or higher
- Elixir 1.6.1 or higher
- OpenFlow switch supports version 1.3
```iex
# To add a patch link
iex > :ok = PatchPanel.Openflow.Controller.create_patch("de780562fb45, 1, 2)
# To delete a patch link
iex > :ok = PatchPanel.Openflow.Controller.delete_patch("de780562fb45, 1, 2)
```

View file

@ -0,0 +1,7 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
config :tres,
callback_module: PatchPanel.Openflow.Controller,
callback_args: []

View file

@ -0,0 +1,18 @@
defmodule PatchPanel do
@moduledoc """
Documentation for PatchPanel.
"""
@doc """
Hello world.
## Examples
iex> PatchPanel.hello
:world
"""
def hello do
:world
end
end

View file

@ -0,0 +1,20 @@
defmodule PatchPanel.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
alias PatchPanel.Openflow
def start(_type, _args) do
import Supervisor.Spec
children = [
worker(Registry, [[keys: :unique, name: Openflow.Registry]], id: Openflow.Registry),
]
opts = [strategy: :one_for_one, name: PatchPanel.Supervisor]
Supervisor.start_link(children, opts)
end
end

View file

@ -0,0 +1,81 @@
defmodule PatchPanel.Openflow.Controller do
use GenServer
use Tres.Controller
import Logger
defmodule State do
defstruct [:datapath_id]
end
def create_patch(datapath_id, port_a, port_b) do
case PatchPanel.Openflow.Registry.lookup_pid(datapath_id) do
nil ->
{:error, :not_found}
pid when is_pid(pid) ->
GenServer.call(pid, {:create, port_a, port_b})
end
end
def delete_patch(datapath_id, port_a, port_b) do
case PatchPanel.Openflow.Registry.lookup_pid(datapath_id) do
nil ->
{:error, :not_found}
pid when is_pid(pid) ->
GenServer.call(pid, {:delete, port_a, port_b})
end
end
def start_link({datapath_id, _aux_id}, _start_args) do
GenServer.start_link(__MODULE__, [datapath_id])
end
def init([datapath_id]) do
:ok = info("Switch Connected: datapath_id = #{datapath_id}")
{:ok, _} = PatchPanel.Openflow.Registry.register(datapath_id)
{:ok, %State{datapath_id: datapath_id}}
end
def handle_call({:create, port_a, port_b}, _from, %State{datapath_id: datapath_id} = state) do
:ok = add_flow_entries(datapath_id, port_a, port_b)
{:reply, :ok, state}
end
def handle_call({:delete, port_a, port_b}, _from, %State{datapath_id: datapath_id} = state) do
:ok = del_flow_entries(datapath_id, port_a, port_b)
{:reply, :ok, state}
end
def handle_info({:switch_disconnected, reason}, %State{datapath_id: datapath_id} = state) do
:ok = warn("#{datapath_id} disconnected: reason = #{inspect(reason)}")
:ok = PatchPanel.Openflow.Registry.unregister(datapath_id)
{:stop, :normal, state}
end
def handle_info(_info, state) do
{:noreply, state}
end
def terminate(reason, state) do
{reason, state}
end
# private functions
defp add_flow_entries(datapath_id, port_a, port_b) do
:ok = send_flow_mod_add(
datapath_id,
match: Match.new(in_port: port_a),
instructions: [ApplyActions.new(Output.new(port_b))]
)
:ok = send_flow_mod_add(
datapath_id,
match: Match.new(in_port: port_b),
instructions: [ApplyActions.new(Output.new(port_a))]
)
end
defp del_flow_entries(datapath_id, port_a, port_b) do
:ok = send_flow_mod_delete(datapath_id, match: Match.new(in_port: port_a))
:ok = send_flow_mod_delete(datapath_id, match: Match.new(in_port: port_b))
end
end

View file

@ -0,0 +1,16 @@
defmodule PatchPanel.Openflow.Registry do
def register(datapath_id) do
{:ok, _} = Registry.register(__MODULE__, datapath_id, [])
end
def unregister(datapath_id) do
:ok = Registry.unregister(__MODULE__, datapath_id)
end
def lookup_pid(datapath_id) do
case Registry.lookup(__MODULE__, datapath_id) do
[{pid, _}] -> pid
[] -> nil
end
end
end

View file

@ -0,0 +1,26 @@
defmodule PatchPanel.MixProject do
use Mix.Project
def project do
[
app: :patch_panel,
version: "0.1.0",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
mod: {PatchPanel.Application, []}
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[{:tres, path: "../../../tres"}]
end
end

View file

@ -0,0 +1,6 @@
%{
"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", [:rebar3], [], "hexpm"},
"uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]},
}

View file

@ -0,0 +1,8 @@
defmodule PatchPanelTest do
use ExUnit.Case
doctest PatchPanel
test "greets the world" do
assert PatchPanel.hello() == :world
end
end

View file

@ -0,0 +1 @@
ExUnit.start()

BIN
images/tres_inside.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

72
lib/openflow.ex Normal file
View file

@ -0,0 +1,72 @@
defmodule Openflow do
@moduledoc "OpenFlow Protocol format codec"
@ofp_header_size 8
@spec read(binary()) :: {:ok, map()} | {:error, :binary_too_small}
def read(binary)
when byte_size(binary) < @ofp_header_size do
{:error, :binary_too_small}
end
def read(<<_v::8, _t::8, len::16, _x::32, _r::bytes>> = binary)
when byte_size(binary) < len do
{:error, :binary_too_small}
end
def read(<<ver::8, type::8, len::16, xid::32, binary2::bytes>>) do
body_len = len - @ofp_header_size
<<body_bin::bytes-size(body_len), rest::bytes>> = binary2
result =
type
|> Openflow.Enums.to_atom(:openflow_codec)
|> do_read(body_bin)
case result do
{:ok, struct} -> {:ok, %{struct | version: ver, xid: xid}, rest}
{:error, reason} -> {:error, reason}
end
end
def to_binary(messages) when is_list(messages) do
binaries = for message <- messages, do: to_binary(message)
Enum.join(binaries, "")
end
def to_binary(%{__struct__: encoder, version: version, xid: xid} = msg) do
case encoder.to_binary(msg) do
body_bin when is_binary(body_bin) ->
length = @ofp_header_size + byte_size(body_bin)
<<version::8, encoder.ofp_type::8, length::16, xid::32, body_bin::bytes>>
{:error, reason} ->
{:error, reason}
end
end
def append_body(nil, message), do: message
def append_body(message, continue) do
mod = message.__struct__
if function_exported?(mod, :append_body, 2) do
mod.append_body(message, continue)
else
message
end
end
# private functions
defp do_read({:error, reason}, _) do
{:error, reason}
end
defp do_read(decoder, body_bin) do
case decoder.read(body_bin) do
{:error, reason} -> {:error, reason}
result when is_map(result) -> {:ok, result}
end
end
end

31
lib/openflow/action.ex Normal file
View file

@ -0,0 +1,31 @@
defmodule Openflow.Action do
def read(action_bin) do
do_read([], action_bin)
end
def to_binary(actions) when is_list(actions) do
to_binary(<<>>, actions)
end
def to_binary(action) do
to_binary([action])
end
# private functions
defp do_read(acc, <<>>), do: Enum.reverse(acc)
defp do_read(acc, <<0::32, _::bytes>>), do: Enum.reverse(acc)
defp do_read(acc, <<type::16, length::16, _::bytes>> = binary) do
<<action_bin::size(length)-bytes, rest::bytes>> = binary
codec = Openflow.Enums.to_atom(type, :action_type)
do_read([codec.read(action_bin) | acc], rest)
end
defp to_binary(acc, []), do: acc
defp to_binary(acc, [action | rest]) do
codec = action.__struct__
to_binary(<<acc::bytes, codec.to_binary(action)::bytes>>, rest)
end
end

View file

@ -0,0 +1,19 @@
defmodule Openflow.Action.CopyTtlIn do
defstruct([])
alias __MODULE__
def ofpat, do: 12
def new do
%CopyTtlIn{}
end
def to_binary(%CopyTtlIn{}) do
<<12::16, 8::16, 0::size(4)-unit(8)>>
end
def read(<<12::16, 8::16, _::size(4)-unit(8)>>) do
%CopyTtlIn{}
end
end

View file

@ -0,0 +1,19 @@
defmodule Openflow.Action.CopyTtlOut do
defstruct([])
alias __MODULE__
def ofpat, do: 11
def new do
%CopyTtlOut{}
end
def to_binary(%CopyTtlOut{}) do
<<11::16, 8::16, 0::size(4)-unit(8)>>
end
def read(<<11::16, 8::16, _::size(4)-unit(8)>>) do
%CopyTtlOut{}
end
end

View file

@ -0,0 +1,19 @@
defmodule Openflow.Action.DecMplsTtl do
defstruct([])
alias __MODULE__
def ofpat, do: 16
def new do
%DecMplsTtl{}
end
def to_binary(%DecMplsTtl{}) do
<<16::16, 8::16, 0::size(4)-unit(8)>>
end
def read(<<16::16, 8::16, _::size(4)-unit(8)>>) do
%DecMplsTtl{}
end
end

View file

@ -0,0 +1,19 @@
defmodule Openflow.Action.DecNwTtl do
defstruct([])
alias __MODULE__
def ofpat, do: 24
def new do
%DecNwTtl{}
end
def to_binary(%DecNwTtl{}) do
<<24::16, 8::16, 0::size(4)-unit(8)>>
end
def read(<<24::16, 8::16, _::size(4)-unit(8)>>) do
%DecNwTtl{}
end
end

View file

@ -0,0 +1,34 @@
defmodule Openflow.Action.Experimenter do
defstruct(exp_id: 0, data: "")
alias __MODULE__
@experimter_size 8
def ofpat, do: 0xFFFF
def new(exp_id, data \\ "") do
%Experimenter{exp_id: exp_id, data: data}
end
def to_binary(%Experimenter{exp_id: exp_id, data: data}) do
length = @experimter_size + byte_size(data)
<<0xFFFF::16, length::16, exp_id::32, data::bytes>>
end
def read(<<0xFFFF::16, _length::16, exp_id::32, exp_type::16, data::bytes>>) do
case Openflow.Utils.get_enum(exp_id, :action_vendor) do
vendor_id when is_integer(vendor_id) ->
%Experimenter{exp_id: exp_id, data: <<exp_type::16, data::bytes>>}
vendor when is_atom(vendor) ->
case Openflow.Utils.get_enum(exp_type, vendor) do
codec when is_atom(codec) ->
codec.read(<<exp_id::32, exp_type::16, data::bytes>>)
exp_type when is_integer(exp_type) ->
%Experimenter{exp_id: exp_id, data: <<exp_type::16, data::bytes>>}
end
end
end
end

View file

@ -0,0 +1,19 @@
defmodule Openflow.Action.Group do
defstruct(id: 0)
alias __MODULE__
def ofpat, do: 22
def new(id) do
%Group{id: id}
end
def to_binary(%Group{id: id}) do
<<22::16, 8::16, id::32>>
end
def read(<<22::16, 8::16, id::32>>) do
%Group{id: id}
end
end

View file

@ -0,0 +1,91 @@
defmodule Openflow.Action.NxBundle do
defstruct(
algorithm: :active_backup,
hash_field: :eth_src,
basis: 0,
slave_type: :nx_in_port,
n_slaves: 0,
slaves: []
)
@experimenter 0x00002320
@nxast 12
alias __MODULE__
def new(options) do
slaves = options[:slaves] || []
%NxBundle{
algorithm: options[:algorithm] || :active_backup,
hash_field: options[:hash_field] || :eth_src,
basis: options[:basis] || 0,
n_slaves: length(slaves),
slaves: slaves
}
end
def to_binary(%NxBundle{
algorithm: alg,
hash_field: hash_field,
basis: basis,
slave_type: slave_type,
n_slaves: n_slaves,
slaves: slaves
}) do
hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields)
alg_int = Openflow.Enums.to_int(alg, :nx_bd_algorithm)
slave_type_bin = Openflow.Match.codec_header(slave_type)
slaves_bin = codec_slaves(slaves)
body =
<<alg_int::16, hash_field_int::16, basis::16, slave_type_bin::4-bytes, n_slaves::16, 0::16,
0::size(4)-unit(8), 0::32, slaves_bin::bytes>>
exp_body = <<@experimenter::32, @nxast::16, body::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<alg_int::16, hash_field_int::16, basis::16, slave_type_bin::4-bytes, n_slaves::16, _::16,
_::32, _::32, rest::bytes>> = body
slave_len = n_slaves * 2
<<slaves_bin::size(slave_len)-bytes, _::bytes>> = rest
alg = Openflow.Enums.to_atom(alg_int, :nx_bd_algorithm)
hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields)
slave_type = Openflow.Match.codec_header(slave_type_bin)
slaves = codec_slaves(slaves_bin)
n_slaves = length(slaves)
%NxBundle{
algorithm: alg,
hash_field: hash_field,
basis: basis,
slave_type: slave_type,
n_slaves: n_slaves,
slaves: slaves
}
end
# private functions
defp codec_slaves(slaves) when is_list(slaves) do
slaves1 =
for slave <- slaves do
slave_int = Openflow.Utils.get_enum(slave, :openflow10_port_no)
<<slave_int::16>>
end
Enum.join(slaves1, "")
end
defp codec_slaves(slaves) when is_binary(slaves) do
for <<slave_int::16 <- slaves>> do
Openflow.Utils.get_enum(slave_int, :openflow10_port_no)
end
end
end

View file

@ -0,0 +1,110 @@
defmodule Openflow.Action.NxBundleLoad do
import Bitwise
defstruct(
algorithm: :active_backup,
hash_field: :eth_src,
basis: 0,
slave_type: :nx_in_port,
n_slaves: 0,
slaves: [],
offset: 0,
n_bits: 0,
dst_field: nil
)
@experimenter 0x00002320
@nxast 13
alias __MODULE__
def new(options) do
dst_field = options[:dst_field]
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
slaves = options[:slaves] || []
%NxBundleLoad{
algorithm: options[:algorithm] || :active_backup,
hash_field: options[:hash_field] || :eth_src,
basis: options[:basis] || 0,
n_slaves: length(slaves),
slaves: slaves,
offset: options[:offset] || 0,
n_bits: options[:n_bits] || default_n_bits,
dst_field: options[:dst_field]
}
end
def to_binary(%NxBundleLoad{
algorithm: alg,
hash_field: hash_field,
basis: basis,
slave_type: slave_type,
n_slaves: n_slaves,
slaves: slaves,
offset: ofs,
n_bits: n_bits,
dst_field: dst_field
}) do
hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields)
alg_int = Openflow.Enums.to_int(alg, :nx_bd_algorithm)
slave_type_bin = Openflow.Match.codec_header(slave_type)
slaves_bin = codec_slaves(slaves)
ofs_nbits = ofs <<< 6 ||| n_bits - 1
dst_field_bin = Openflow.Match.codec_header(dst_field)
body =
<<alg_int::16, hash_field_int::16, basis::16, slave_type_bin::4-bytes, n_slaves::16,
ofs_nbits::16, dst_field_bin::4-bytes, 0::32, slaves_bin::bytes>>
exp_body = <<@experimenter::32, @nxast::16, body::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<alg_int::16, hash_field_int::16, basis::16, slave_type_bin::4-bytes, n_slaves::16, ofs::10,
n_bits::6, dst_field_bin::4-bytes, _reserved::32, rest::bytes>> = body
slave_len = n_slaves * 2
<<slaves_bin::size(slave_len)-bytes, _::bytes>> = rest
alg = Openflow.Enums.to_atom(alg_int, :nx_bd_algorithm)
hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields)
slave_type = Openflow.Match.codec_header(slave_type_bin)
slaves = codec_slaves(slaves_bin)
n_slaves = length(slaves)
dst_field = Openflow.Match.codec_header(dst_field_bin)
%NxBundleLoad{
algorithm: alg,
hash_field: hash_field,
basis: basis,
slave_type: slave_type,
n_slaves: n_slaves,
slaves: slaves,
offset: ofs,
n_bits: n_bits + 1,
dst_field: dst_field
}
end
# private functions
defp codec_slaves(slaves) when is_list(slaves) do
slaves1 =
for slave <- slaves do
slave_int = Openflow.Utils.get_enum(slave, :openflow10_port_no)
<<slave_int::16>>
end
Enum.join(slaves1, "")
end
defp codec_slaves(slaves) when is_binary(slaves) do
for <<slave_int::16 <- slaves>> do
Openflow.Utils.get_enum(slave_int, :openflow10_port_no)
end
end
end

View file

@ -0,0 +1,26 @@
defmodule Openflow.Action.NxClone do
defstruct(actions: [])
@experimenter 0x00002320
@nxast 42
alias __MODULE__
def new(actions \\ []) do
%NxClone{actions: actions}
end
def to_binary(%NxClone{actions: actions}) do
actions_bin = Openflow.Action.to_binary(actions)
exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>>) do
actions = Openflow.Action.read(actions_bin)
%NxClone{actions: actions}
end
end

View file

@ -0,0 +1,29 @@
defmodule Openflow.Action.NxConjunction do
defstruct(
clause: 0,
n_clauses: 0,
id: 0
)
@experimenter 0x00002320
@nxast 34
alias __MODULE__
def new(options) do
%NxConjunction{
clause: options[:clause] || 0,
n_clauses: options[:n_clauses] || 0,
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>>
end
def read(<<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>>) do
%NxConjunction{clause: clause, n_clauses: n_clauses, id: id}
end
end

View file

@ -0,0 +1,90 @@
defmodule Openflow.Action.NxConntrack do
import Bitwise
defstruct(
flags: [],
zone_src: nil,
zone_imm: 0,
zone_offset: nil,
zone_n_bits: nil,
recirc_table: 255,
alg: 0,
exec: []
)
@experimenter 0x00002320
@nxast 35
alias __MODULE__
def new(options \\ []) do
%NxConntrack{
flags: options[:flags] || [],
zone_src: options[:zone_src],
zone_imm: options[:zone_imm] || 0,
zone_offset: options[:zone_offset],
zone_n_bits: options[:zone_n_bits],
recirc_table: options[:recirc_table] || 255,
alg: options[:alg] || 0,
exec: options[:exec] || []
}
end
def to_binary(%NxConntrack{
flags: flags,
zone_src: zone_src,
zone_offset: zone_ofs,
zone_n_bits: zone_n_bits,
zone_imm: zone_imm,
recirc_table: recirc_table,
alg: alg,
exec: exec
}) do
flags_int = Openflow.Enums.flags_to_int(flags, :nx_conntrack_flags)
{src_bin, ofs_nbits} =
if not is_nil(zone_src) do
zone_src_bin = Openflow.Match.codec_header(zone_src)
{zone_src_bin, zone_ofs <<< 6 ||| zone_n_bits - 1}
else
{<<0::32>>, zone_imm}
end
exec_bin = Openflow.Action.to_binary(exec)
exp_body =
<<@experimenter::32, @nxast::16, flags_int::16, src_bin::bytes, ofs_nbits::16,
recirc_table::8, 0::size(3)-unit(8), alg::16, exec_bin::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(
<<@experimenter::32, @nxast::16, flags_int::16, src_bin::4-bytes, ofs_nbits::16-bits,
recirc_table::8, _::size(3)-unit(8), alg::16, exec_bin::bytes>>
) do
flags = Openflow.Enums.int_to_flags(flags_int, :nx_conntrack_flags)
exec = Openflow.Action.read(exec_bin)
ct = %NxConntrack{
flags: flags,
recirc_table: recirc_table,
alg: alg,
exec: exec
}
case src_bin do
<<0::32>> ->
<<zone_imm::16>> = ofs_nbits
%{ct | zone_imm: zone_imm}
binary when is_binary(binary) ->
zone_src = Openflow.Match.codec_header(binary)
<<ofs::10, n_bits::6>> = ofs_nbits
%{ct | zone_src: zone_src, zone_offset: ofs, zone_n_bits: n_bits + 1}
end
end
end

View file

@ -0,0 +1,39 @@
defmodule Openflow.Action.NxController do
defstruct(
max_len: :no_buffer,
id: 0,
reason: :action
)
@experimenter 0x00002320
@nxast 20
alias __MODULE__
def new(options) do
%NxController{
max_len: options[:max_len] || :no_buffer,
id: options[:id] || 0,
reason: options[:reason] || :action
}
end
def to_binary(%NxController{max_len: max_len, id: controller_id, reason: reason}) do
max_len_int = Openflow.Utils.get_enum(max_len, :controller_max_len)
reason_int = Openflow.Enums.to_int(reason, :packet_in_reason)
exp_body =
<<@experimenter::32, @nxast::16, max_len_int::16, controller_id::16, reason_int::8, 0::8>>
<<0xFFFF::16, 16::16, exp_body::bytes>>
end
def read(
<<@experimenter::32, @nxast::16, max_len_int::16, controller_id::16, reason_int::8,
_::bytes>>
) do
max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len)
reason = Openflow.Enums.to_atom(reason_int, :packet_in_reason)
%NxController{max_len: max_len, id: controller_id, reason: reason}
end
end

View file

@ -0,0 +1,140 @@
defmodule Openflow.Action.NxController2 do
defstruct(
max_len: :no_buffer,
id: 0,
reason: :action,
userdata: "",
pause: false
)
@experimenter 0x00002320
@nxast 37
@prop_header_size 4
@prop_max_len 0
@prop_ctl_id 1
@prop_reason 2
@prop_userdata 3
@prop_pause 4
alias __MODULE__
def new(options) do
%NxController2{
max_len: options[:max_len] || :no_buffer,
id: options[:id] || 0,
reason: options[:reason] || :action,
userdata: options[:userdata],
pause: options[:pause] || false
}
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 = <<ext_header::bytes, props_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>>
end
def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), body::bytes>>) do
%NxController2{}
|> decode_prop(body)
end
# private functions
defp get_prop_key(ctl) do
ctl
|> Map.from_struct()
|> Enum.map(fn {k, v} -> if(not is_nil(v), do: k, else: nil) end)
|> Enum.filter(fn v -> not is_nil(v) end)
end
defp encode_prop(acc, [], _ctl), do: acc
defp encode_prop(acc, [prop | rest], ctl) do
value = Map.get(ctl, prop)
prop_bin =
cond do
prop == :max_len and (value != :no_buffer or value < 0xFFFF) ->
padding_length = 2
prop_length = @prop_header_size + 2 + padding_length
max_len_int = Openflow.Utils.get_enum(value, :controller_max_len)
<<@prop_max_len::16, prop_length::16, max_len_int::16, 0::size(padding_length)-unit(8)>>
prop == :id ->
padding_length = 2
prop_length = @prop_header_size + 2 + padding_length
<<@prop_ctl_id::16, prop_length::16, value::16, 0::size(padding_length)-unit(8)>>
prop == :reason and value != :action ->
padding_length = 3
prop_length = @prop_header_size + 1 + padding_length
reason_int = Openflow.Utils.get_enum(value, :packet_in_reason)
<<@prop_reason::16, prop_length::16, reason_int::8, 0::size(padding_length)-unit(8)>>
prop == :userdata and byte_size(value) > 0 ->
prop_length = @prop_header_size + byte_size(value)
padding_length = Openflow.Utils.padding(prop_length, 8)
<<@prop_userdata::16, prop_length::16, value::bytes, 0::size(padding_length)-unit(8)>>
prop == :pause and value == true ->
padding_length = 4
prop_length = @prop_header_size + padding_length
<<@prop_pause::16, prop_length::16, 0::size(padding_length)-unit(8)>>
true ->
""
end
encode_prop(<<acc::bytes, prop_bin::bytes>>, rest, ctl)
end
defp decode_prop(ctl, ""), do: ctl
defp decode_prop(ctl, <<prop_type_int::16, _::bytes>> = bin) do
prop_type = Openflow.Enums.to_atom(prop_type_int, :nx_action_controller2_prop_type)
case prop_type do
:max_len ->
<<@prop_max_len::16, _prop_length::16, max_len_int::16, _::size(2)-unit(8), rest::bytes>> =
bin
max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len)
decode_prop(struct(ctl, %{max_len: max_len}), rest)
:controller_id ->
<<@prop_ctl_id::16, _prop_length::16, controller_id::16, _::size(2)-unit(8), rest::bytes>> =
bin
decode_prop(struct(ctl, %{controller_id: controller_id}), rest)
:reason ->
<<@prop_reason::16, _prop_length::16, reason_int::8, _::size(3)-unit(8), rest::bytes>> =
bin
reason = Openflow.Utils.get_enum(reason_int, :packet_in_reason)
decode_prop(struct(ctl, %{reason: reason}), rest)
:userdata ->
<<@prop_userdata::16, prop_length::16, remains::bytes>> = bin
userdata_len = prop_length - 4
padding_length = Openflow.Utils.padding(prop_length, 8)
<<userdata::size(userdata_len)-bytes, _::size(padding_length)-unit(8), rest::bytes>> =
remains
decode_prop(struct(ctl, %{userdata: userdata}), rest)
:pause ->
<<@prop_pause::16, _::16, 0::size(4)-unit(8), rest::bytes>> = bin
decode_prop(struct(ctl, %{pause: true}), rest)
end
end
end

View file

@ -0,0 +1,21 @@
defmodule Openflow.Action.NxCtClear do
defstruct([])
@experimenter 0x00002320
@nxast 43
alias __MODULE__
def new do
%NxCtClear{}
end
def to_binary(%NxCtClear{}) do
exp_body = <<@experimenter::32, @nxast::16, 0::16, 0::size(4)-unit(8)>>
<<0xFFFF::16, 16::16, exp_body::bytes>>
end
def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do
%NxCtClear{}
end
end

View file

@ -0,0 +1,21 @@
defmodule Openflow.Action.NxDecMplsTtl do
defstruct([])
@experimenter 0x00002320
@nxast 26
alias __MODULE__
def new do
%NxDecMplsTtl{}
end
def to_binary(%NxDecMplsTtl{}) do
exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>
<<0xFFFF::16, 16::16, exp_body::bytes>>
end
def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8)>>) do
%NxDecMplsTtl{}
end
end

View file

@ -0,0 +1,21 @@
defmodule Openflow.Action.NxDecTtl do
defstruct([])
@experimenter 0x00002320
@nxast 18
alias __MODULE__
def new do
%NxDecTtl{}
end
def to_binary(%NxDecTtl{}) do
exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>
<<0xFFFF::16, 16::16, exp_body::bytes>>
end
def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do
%NxDecTtl{}
end
end

View file

@ -0,0 +1,34 @@
defmodule Openflow.Action.NxDecTtlCntIds do
defstruct(ids: [])
@experimenter 0x00002320
@nxast 21
alias __MODULE__
def new(ids) do
%NxDecTtlCntIds{ids: ids}
end
def to_binary(%NxDecTtlCntIds{ids: ids}) do
n_controllers = length(ids)
ids_bin = Enum.join(for(id <- ids, do: <<id::16>>), "")
padding = Openflow.Utils.padding(n_controllers, 8)
exp_body =
<<@experimenter::32, @nxast::16, n_controllers::16, 0::size(4)-unit(8), ids_bin::bytes,
0::size(padding)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, n_controllers::16, body::bitstring>>) do
n_controllers_len = n_controllers * 16
<<0::size(4)-unit(8), ids_bin::size(n_controllers_len)-bits, _::bitstring>> = body
ids = for <<id::16 <- ids_bin>>, do: id
%NxDecTtlCntIds{ids: ids}
end
end

View file

@ -0,0 +1,21 @@
defmodule Openflow.Action.NxExit do
defstruct([])
@experimenter 0x00002320
@nxast 17
alias __MODULE__
def new do
%NxExit{}
end
def to_binary(%NxExit{}) do
exp_body = <<@experimenter::32, @nxast::16, 0::48>>
<<0xFFFF::16, 16::16, exp_body::bytes>>
end
def read(<<@experimenter::32, @nxast::16, 0::48>>) do
%NxExit{}
end
end

View file

@ -0,0 +1,27 @@
defmodule Openflow.Action.NxFinTimeout do
defstruct(
idle_timeout: 0,
hard_timeout: 0
)
@experimenter 0x00002320
@nxast 19
alias __MODULE__
def new(options) do
%NxFinTimeout{
idle_timeout: options[:idle_timeout] || 0,
hard_timeout: options[:hard_timeout] || 0
}
end
def to_binary(%NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard}) do
exp_body = <<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16>>
<<0xFFFF::16, 16::16, exp_body::bytes, 0::size(2)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16, _::size(2)-unit(8)>>) do
%NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard}
end
end

View file

@ -0,0 +1,31 @@
defmodule Openflow.Action.NxFlowSpec do
def read(flow_spec_bin) do
do_read([], flow_spec_bin)
end
def to_binary(flow_specs) when is_list(flow_specs) do
to_binary(<<>>, flow_specs)
end
def to_binary(flow_spec) do
to_binary([flow_spec])
end
# private functions
defp do_read(acc, <<>>), do: Enum.reverse(acc)
defp do_read(acc, <<0::16, _::bitstring>>), do: Enum.reverse(acc)
defp do_read(acc, <<_::2, _::1, type::2, _::bitstring>> = binary) do
codec = Openflow.Enums.to_atom(type, :nx_flow_spec_type)
{flow_spec, rest} = codec.read(binary)
do_read([flow_spec | acc], rest)
end
defp to_binary(acc, []), do: acc
defp to_binary(acc, [flow_spec | rest]) do
codec = flow_spec.__struct__
to_binary(<<acc::bytes, codec.to_binary(flow_spec)::bytes>>, rest)
end
end

View file

@ -0,0 +1,93 @@
defmodule Openflow.Action.NxFlowSpecLoad do
defstruct(
src: nil,
dst: nil,
n_bits: 0,
src_offset: 0,
dst_offset: 0
)
@learn_src_field 0
@learn_src_immediate 1
@learn_dst 1
alias __MODULE__
def new(options) do
dst = options[:dst]
n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst)
%NxFlowSpecLoad{
src: options[:src],
dst: dst,
n_bits: n_bits,
src_offset: options[:src_offset] || 0,
dst_offset: options[:dst_offset] || 0
}
end
def to_binary(%NxFlowSpecLoad{} = fsm) do
%NxFlowSpecLoad{
dst: dst_field,
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs
} = fsm
{src_code, src_bin} = codec_src(fsm)
dst_bin = Openflow.Match.codec_header(dst_field)
case src_code do
@learn_src_immediate ->
<<0::2, src_code::1, @learn_dst::2, n_bits::11, src_bin::bytes, dst_bin::4-bytes,
dst_ofs::16>>
@learn_src_field ->
<<0::2, src_code::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16,
dst_bin::4-bytes, dst_ofs::16>>
end
end
def read(
<<_::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16,
dst_bin::4-bytes, dst_ofs::16, rest::bitstring>>
) do
src = Openflow.Match.codec_header(src_bin)
dst = Openflow.Match.codec_header(dst_bin)
flow_spec = %NxFlowSpecLoad{
src: src,
dst: dst,
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs
}
{flow_spec, rest}
end
def read(<<_::2, @learn_src_immediate::1, @learn_dst::2, n_bits::11, binary::bitstring>>) do
rounded_up_len = Openflow.Utils.pad_length(n_bits, 8)
rounded_up_nbits = n_bits + rounded_up_len
<<src_bin::size(rounded_up_nbits)-bits, dst_bin::4-bytes, dst_ofs::16, rest::bitstring>> =
binary
dst = Openflow.Match.codec_header(dst_bin)
src = Openflow.Match.Field.codec(src_bin, dst)
flow_spec = %NxFlowSpecLoad{src: src, dst: dst, n_bits: n_bits, dst_offset: dst_ofs}
{flow_spec, rest}
end
# private functions
defp codec_src(%NxFlowSpecLoad{src: src_field}) when is_atom(src_field) do
src_bin = Openflow.Match.codec_header(src_field)
{@learn_src_field, src_bin}
end
defp codec_src(%NxFlowSpecLoad{src: src, dst: dst_field}) do
src_bin = Openflow.Match.Field.codec(src, dst_field)
{@learn_src_immediate, src_bin}
end
end

View file

@ -0,0 +1,93 @@
defmodule Openflow.Action.NxFlowSpecMatch do
defstruct(
src: nil,
dst: nil,
n_bits: 0,
src_offset: 0,
dst_offset: 0
)
@learn_src_field 0
@learn_src_immediate 1
@learn_dst 0
alias __MODULE__
def new(options) do
dst = options[:dst]
n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst)
%NxFlowSpecMatch{
src: options[:src],
dst: dst,
n_bits: n_bits,
src_offset: options[:src_offset] || 0,
dst_offset: options[:dst_offset] || 0
}
end
def to_binary(%NxFlowSpecMatch{} = fsm) do
%NxFlowSpecMatch{
dst: dst_field,
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs
} = fsm
{src_code, src_bin} = codec_src(fsm)
dst_bin = Openflow.Match.codec_header(dst_field)
case src_code do
@learn_src_immediate ->
<<0::2, src_code::1, @learn_dst::2, n_bits::11, src_bin::bytes, dst_bin::4-bytes,
dst_ofs::16>>
@learn_src_field ->
<<0::2, src_code::1, @learn_dst::2, n_bits::11, src_bin::bytes, src_ofs::16,
dst_bin::4-bytes, dst_ofs::16>>
end
end
def read(
<<_::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16,
dst_bin::4-bytes, dst_ofs::16, rest::bitstring>>
) do
src = Openflow.Match.codec_header(src_bin)
dst = Openflow.Match.codec_header(dst_bin)
flow_spec = %NxFlowSpecMatch{
src: src,
dst: dst,
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs
}
{flow_spec, rest}
end
def read(<<_::2, @learn_src_immediate::1, @learn_dst::2, n_bits::11, binary::bitstring>>) do
rounded_up_len = Openflow.Utils.pad_length(n_bits, 8)
rounded_up_nbits = n_bits + rounded_up_len
<<src_bin::size(rounded_up_nbits)-bits, dst_bin::4-bytes, dst_ofs::16, rest::bitstring>> =
binary
dst = Openflow.Match.codec_header(dst_bin)
src = Openflow.Match.Field.codec(src_bin, dst)
flow_spec = %NxFlowSpecMatch{src: src, dst: dst, n_bits: n_bits, dst_offset: dst_ofs}
{flow_spec, rest}
end
# private functions
defp codec_src(%NxFlowSpecMatch{src: src_field}) when is_atom(src_field) do
src_bin = Openflow.Match.codec_header(src_field)
{@learn_src_field, src_bin}
end
defp codec_src(%NxFlowSpecMatch{src: src, dst: dst_field}) do
src_bin = Openflow.Match.Field.codec(src, dst_field)
{@learn_src_immediate, src_bin}
end
end

View file

@ -0,0 +1,32 @@
defmodule Openflow.Action.NxFlowSpecOutput do
defstruct(
n_bits: 0,
src: nil,
src_offset: 0
)
@learn_src_field 0
@learn_dst 2
alias __MODULE__
def new(options) do
src = options[:src]
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>>
end
def read(
<<0::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16,
rest::bitstring>>
) do
src = Openflow.Match.codec_header(src_bin)
flow_spec = %NxFlowSpecOutput{n_bits: n_bits, src: src, src_offset: src_ofs}
{flow_spec, rest}
end
end

View file

@ -0,0 +1,76 @@
defmodule Openflow.Action.NxLearn do
defstruct(
idle_timeout: 0,
hard_timeout: 0,
priority: 0,
cookie: 0,
flags: [],
table_id: 0,
fin_idle_timeout: 0,
fin_hard_timeout: 0,
flow_specs: []
)
@experimenter 0x00002320
@nxast 16
alias __MODULE__
def new(options) do
%NxLearn{
idle_timeout: options[:idle_timeout] || 0,
hard_timeout: options[:hard_timeout] || 0,
priority: options[:priority] || 0,
cookie: options[:cookie] || 0,
flags: options[:flags] || [],
table_id: options[:table_id] || 0xFF,
fin_idle_timeout: options[:fin_idle_timeout] || 0,
fin_hard_timeout: options[:fin_hard_timeout] || 0,
flow_specs: options[:flow_specs] || []
}
end
def to_binary(%NxLearn{
idle_timeout: idle,
hard_timeout: hard,
priority: prio,
cookie: cookie,
flags: flags,
table_id: table_id,
fin_idle_timeout: fin_idle,
fin_hard_timeout: fin_hard,
flow_specs: flow_specs
}) do
flags_int = Openflow.Enums.flags_to_int(flags, :nx_learn_flag)
flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(flow_specs)
exp_body =
<<@experimenter::32, @nxast::16, idle::16, hard::16, prio::16, cookie::64, flags_int::16,
table_id::8, 0::size(1)-unit(8), fin_idle::16, fin_hard::16, flow_specs_bin::bitstring>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bitstring>>) do
<<idle::16, hard::16, prio::16, cookie::64, flags_int::16, table_id::8, _pad::size(1)-unit(8),
fin_idle::16, fin_hard::16, flow_specs_bin::bitstring>> = body
flags = Openflow.Enums.int_to_flags(flags_int, :nx_learn_flag)
flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin)
%NxLearn{
idle_timeout: idle,
hard_timeout: hard,
priority: prio,
cookie: cookie,
flags: flags,
table_id: table_id,
fin_idle_timeout: fin_idle,
fin_hard_timeout: fin_hard,
flow_specs: flow_specs
}
end
end

View file

@ -0,0 +1,106 @@
defmodule Openflow.Action.NxLearn2 do
defstruct(
idle_timeout: 0,
hard_timeout: 0,
priority: 0,
cookie: 0,
flags: [],
table_id: 0,
fin_idle_timeout: 0,
fin_hard_timeout: 0,
limit: 0,
result_dst_offset: 0,
result_dst: nil,
flow_specs: []
)
@experimenter 0x00002320
@nxast 45
alias __MODULE__
def new(options) do
%NxLearn2{
idle_timeout: options[:idle_timeout] || 0,
hard_timeout: options[:hard_timeout] || 0,
priority: options[:priority] || 0,
cookie: options[:cookie] || 0,
flags: options[:flags] || [],
table_id: options[:table_id] || 0xFF,
fin_idle_timeout: options[:fin_idle_timeout] || 0,
fin_hard_timeout: options[:fin_hard_timeout] || 0,
limit: options[:limit] || 0,
result_dst_offset: options[:result_dst_offset] || 0,
result_dst: options[:result_dst],
flow_specs: options[:flow_specs] || []
}
end
def to_binary(%NxLearn2{
idle_timeout: idle,
hard_timeout: hard,
priority: prio,
cookie: cookie,
flags: flags,
table_id: table_id,
fin_idle_timeout: fin_idle,
fin_hard_timeout: fin_hard,
limit: limit,
result_dst_offset: result_dst_ofs,
result_dst: result_dst,
flow_specs: flow_specs
}) do
flags_int = Openflow.Enums.flags_to_int(flags, :nx_learn_flag)
result_dst_bin =
if :write_result in flags do
Openflow.Match.codec_header(result_dst)
else
""
end
flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(flow_specs)
exp_body =
<<@experimenter::32, @nxast::16, idle::16, hard::16, prio::16, cookie::64, flags_int::16,
table_id::8, 0::size(1)-unit(8), fin_idle::16, fin_hard::16, limit::32,
result_dst_ofs::16, 0::size(2)-unit(8), result_dst_bin::bytes, flow_specs_bin::bitstring>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<idle::16, hard::16, prio::16, cookie::64, flags_int::16, table_id::8, 0::8, fin_idle::16,
fin_hard::16, limit::32, result_dst_ofs::16, 0::size(2)-unit(8), rest::bytes>> = body
flags = Openflow.Enums.int_to_flags(flags_int, :nx_learn_flag)
learn = %NxLearn2{
idle_timeout: idle,
hard_timeout: hard,
priority: prio,
cookie: cookie,
flags: flags,
table_id: table_id,
fin_idle_timeout: fin_idle,
fin_hard_timeout: fin_hard,
limit: limit,
result_dst_offset: result_dst_ofs
}
if :write_result in flags do
header_size = Openflow.Match.header_size(rest)
<<result_dst_bin::size(header_size)-bytes, flow_specs_bin::bytes>> = rest
result_dst = Openflow.Match.codec_header(result_dst_bin)
flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin)
struct(learn, %{result_dst: result_dst, flow_specs: flow_specs})
else
<<flow_specs_bin::bytes>> = rest
flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin)
struct(learn, %{flow_specs: flow_specs})
end
end
end

View file

@ -0,0 +1,88 @@
defmodule Openflow.Action.NxMultipath do
import Bitwise
defstruct(
hash_field: :eth_src,
basis: 0,
algorithm: :modulo_n,
max_link: 0,
argument: 0,
offset: 0,
n_bits: 0,
dst_field: nil
)
@experimenter 0x00002320
@nxast 10
alias __MODULE__
def new(options) do
hash_field = Keyword.get(options, :hash_field, :eth_src)
basis = Keyword.get(options, :basis, 0)
alg = Keyword.get(options, :algorithm, :modulo_n)
max_link = Keyword.get(options, :max_link, 0)
arg = Keyword.get(options, :argument, 0)
dst_field = Keyword.get(options, :dst_field)
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
ofs = Keyword.get(options, :offset, 0)
%NxMultipath{
hash_field: hash_field,
basis: basis,
algorithm: alg,
max_link: max_link,
offset: ofs,
n_bits: n_bits,
argument: arg,
dst_field: dst_field
}
end
def to_binary(%NxMultipath{
hash_field: hash_field,
basis: basis,
algorithm: alg,
max_link: max_link,
argument: arg,
offset: ofs,
n_bits: n_bits,
dst_field: dst_field
}) do
hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields)
alg_int = Openflow.Enums.to_int(alg, :nx_mp_algorithm)
dst_field_bin = Openflow.Match.codec_header(dst_field)
ofs_nbits = ofs <<< 6 ||| n_bits - 1
body =
<<hash_field_int::16, basis::16, 0::size(2)-unit(8), alg_int::16, max_link::16, arg::32,
0::size(2)-unit(8), ofs_nbits::16, dst_field_bin::4-bytes>>
exp_body = <<@experimenter::32, @nxast::16, body::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<hash_field_int::16, basis::16, _::size(2)-unit(8), alg_int::16, max_link::16, arg::32,
_::size(2)-unit(8), ofs::10, n_bits::6, dst_field_bin::4-bytes>> = body
hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields)
alg = Openflow.Enums.to_atom(alg_int, :nx_mp_algorithm)
dst_field = Openflow.Match.codec_header(dst_field_bin)
%NxMultipath{
hash_field: hash_field,
basis: basis,
algorithm: alg,
max_link: max_link,
argument: arg,
offset: ofs,
n_bits: n_bits + 1,
dst_field: dst_field
}
end
end

View file

@ -0,0 +1,145 @@
defmodule Openflow.Action.NxNat do
defstruct(
flags: [],
ipv4_min: nil,
ipv4_max: nil,
ipv6_min: nil,
ipv6_max: nil,
proto_min: nil,
proto_max: nil
)
@experimenter 0x00002320
@nxast 36
alias __MODULE__
def new(options \\ []) do
flags = Keyword.get(options, :flags, [])
ipv4_min = Keyword.get(options, :ipv4_min)
ipv4_max = Keyword.get(options, :ipv4_max)
ipv6_min = Keyword.get(options, :ipv6_min)
ipv6_max = Keyword.get(options, :ipv6_max)
proto_min = Keyword.get(options, :proto_min)
proto_max = Keyword.get(options, :proto_max)
%NxNat{
flags: flags,
ipv4_min: ipv4_min,
ipv4_max: ipv4_max,
ipv6_min: ipv6_min,
ipv6_max: ipv6_max,
proto_min: proto_min,
proto_max: proto_max
}
end
def to_binary(%NxNat{flags: flags} = nat) do
flags_int = Openflow.Enums.flags_to_int(flags, :nx_nat_flags)
range_flags =
nat
|> get_ranges
|> Openflow.Enums.flags_to_int(:nx_nat_range)
|> Openflow.Enums.int_to_flags(:nx_nat_range)
ranges_bin = encode_ranges("", range_flags, nat)
range_flags_int = Openflow.Enums.flags_to_int(range_flags, :nx_nat_range)
exp_body =
<<@experimenter::32, @nxast::16, 0::size(2)-unit(8), flags_int::16, range_flags_int::16,
ranges_bin::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<0::size(2)-unit(8), flags_int::16, range_flags_int::16, ranges_bin::bytes>> = body
flags = Openflow.Enums.int_to_flags(flags_int, :nx_nat_flags)
range_flags = Openflow.Enums.int_to_flags(range_flags_int, :nx_nat_range)
decode_ranges(%NxNat{flags: flags}, range_flags, ranges_bin)
end
# private functions
defp get_ranges(nat) do
nat
|> Map.from_struct()
|> Map.delete(:flags)
|> Enum.map(fn {k, v} -> if(not is_nil(v), do: k, else: nil) end)
|> Enum.filter(fn v -> not is_nil(v) end)
end
defp encode_ranges(acc, [], _nat), do: acc
defp encode_ranges(acc, [range | rest], nat) do
cond do
range == :ipv4_min or range == :ipv4_max ->
case Map.get(nat, range) do
{a1, a2, a3, a4} ->
encode_ranges(<<acc::bytes, a1, a2, a3, a4>>, rest, nat)
"" ->
encode_ranges(<<acc::bytes>>, 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(
<<acc::bytes, a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16>>,
rest,
nat
)
"" ->
encode_ranges(<<acc::bytes>>, 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(<<acc::bytes, proto::16>>, rest, nat)
_ ->
encode_ranges(<<acc::bytes>>, rest, nat)
end
end
end
defp decode_ranges(nat, [], _), do: nat
defp decode_ranges(nat, [range | ranges], bin) do
cond do
range == :ipv4_min or range == :ipv4_max ->
case bin do
<<a1, a2, a3, a4, rest::bytes>> ->
decode_ranges(struct(nat, %{range => {a1, a2, a3, a4}}), ranges, rest)
rest ->
decode_ranges(struct(nat, %{range => ""}), ranges, rest)
end
range == :ipv6_min or range == :ipv6_max ->
case bin do
<<a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16, rest::bytes>> ->
decode_ranges(struct(nat, %{range => {a1, a2, a3, a4, a5, a6, a7, a8}}), ranges, rest)
rest ->
decode_ranges(struct(nat, %{range => ""}), ranges, rest)
end
range == :proto_min or range == :proto_max ->
case bin do
<<proto::16, rest::bytes>> when proto in 1..0xFFFF ->
decode_ranges(struct(nat, %{range => proto}), ranges, rest)
rest ->
decode_ranges(struct(nat, %{range => ""}), ranges, rest)
end
end
end
end

View file

@ -0,0 +1,26 @@
defmodule Openflow.Action.NxNote do
defstruct(note: "")
@experimenter 0x00002320
@nxast 8
alias __MODULE__
def new(note) do
%NxNote{note: note}
end
def to_binary(%NxNote{note: note}) do
padding = Openflow.Utils.padding(byte_size(note) + 2, 8)
exp_body = <<@experimenter::32, @nxast::16, note::bytes, 0::size(padding)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, note_bin::bytes>>) do
note = Openflow.Utils.decode_string(note_bin)
%NxNote{note: note}
end
end

View file

@ -0,0 +1,43 @@
defmodule Openflow.Action.NxOutputReg do
import Bitwise
defstruct(
n_bits: 0,
offset: 0,
src_field: nil,
max_len: :no_buffer
)
@experimenter 0x00002320
@nxast 15
alias __MODULE__
def new(options) do
src_field = Keyword.get(options, :src_field)
default_n_bits = Openflow.Match.Field.n_bits_of(src_field)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
ofs = Keyword.get(options, :offset, 0)
max_len = Keyword.get(options, :max_len, :no_buffer)
%NxOutputReg{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}
end
def to_binary(%NxOutputReg{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}) do
src_field_bin = Openflow.Match.codec_header(src_field)
ofs_nbits = ofs <<< 6 ||| n_bits - 1
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
body = <<ofs_nbits::16, src_field_bin::4-bytes, max_len::16, 0::size(6)-unit(8)>>
exp_body = <<@experimenter::32, @nxast::16, body::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<ofs::10, n_bits::6, src_field_bin::4-bytes, max_len::16, _::size(6)-unit(8)>> = body
src_field = Openflow.Match.codec_header(src_field_bin)
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
%NxOutputReg{n_bits: n_bits + 1, offset: ofs, src_field: src_field, max_len: max_len}
end
end

View file

@ -0,0 +1,54 @@
defmodule Openflow.Action.NxOutputReg2 do
import Bitwise
defstruct(
n_bits: 0,
offset: 0,
src_field: nil,
max_len: :no_buffer
)
@experimenter 0x00002320
@nxast 32
alias __MODULE__
def new(options) do
src_field = Keyword.get(options, :src_field)
default_n_bits = Openflow.Match.Field.n_bits_of(src_field)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
ofs = Keyword.get(options, :offset, 0)
max_len = Keyword.get(options, :max_len, :no_buffer)
%NxOutputReg2{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}
end
def to_binary(%NxOutputReg2{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}) do
src_field_bin = Openflow.Match.codec_header(src_field)
ofs_nbits = ofs <<< 6 ||| n_bits - 1
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
padding = Openflow.Utils.padding(byte_size(src_field_bin), 10)
body = <<ofs_nbits::16, max_len::16, src_field_bin::bytes, 0::size(padding)-unit(8)>>
exp_body = <<@experimenter::32, @nxast::16, body::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<ofs::10, n_bits::6, max_len::16, oxm_header::4-bytes, rest::bitstring>> = body
exp_size = match_header_size(oxm_header) - 4
<<experimenter_int::size(exp_size)-unit(8), _padding::bytes>> = rest
src_field =
Openflow.Match.codec_header(<<oxm_header::bytes, experimenter_int::size(exp_size)-unit(8)>>)
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
%NxOutputReg2{n_bits: n_bits + 1, offset: ofs, src_field: src_field, max_len: max_len}
end
# private functions
defp match_header_size(<<0xFFFF::16, _::bytes>>), do: 8
defp match_header_size(<<_::16, _::bytes>>), do: 4
end

View file

@ -0,0 +1,31 @@
defmodule Openflow.Action.NxOutputTrunc do
defstruct(
port_number: 0,
max_len: :no_buffer
)
@experimenter 0x00002320
@nxast 39
alias __MODULE__
def new(options) do
port_no = Keyword.get(options, :port_number)
max_len = Keyword.get(options, :max_len)
%NxOutputTrunc{port_number: port_no, max_len: max_len}
end
def to_binary(%NxOutputTrunc{port_number: port_no, max_len: max_len}) do
port_no_int = Openflow.Utils.get_enum(port_no, :openflow10_port_no)
exp_body = <<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>>) do
port_no = Openflow.Utils.get_enum(port_no_int, :openflow10_port_no)
%NxOutputTrunc{port_number: port_no, max_len: max_len}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxPopMpls do
defstruct(ethertype: 0x8847)
@experimenter 0x00002320
@nxast 24
alias __MODULE__
def new(ethertype \\ 0x8847) do
%NxPopMpls{ethertype: ethertype}
end
def to_binary(%NxPopMpls{ethertype: ethertype}) do
exp_body = <<@experimenter::32, @nxast::16, ethertype::16, 0::size(4)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, ethertype::16, _::size(4)-unit(8)>>) do
%NxPopMpls{ethertype: ethertype}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxPopQueue do
defstruct([])
@experimenter 0x00002320
@nxast 5
alias __MODULE__
def new do
%NxPopQueue{}
end
def to_binary(%NxPopQueue{}) do
exp_body = <<@experimenter::32, @nxast::16, 0::48>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>) do
%NxPopQueue{}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxPushMpls do
defstruct(ethertype: 0x8847)
@experimenter 0x00002320
@nxast 23
alias __MODULE__
def new(ethertype \\ 0x8847) do
%NxPushMpls{ethertype: ethertype}
end
def to_binary(%NxPushMpls{ethertype: ethertype}) do
exp_body = <<@experimenter::32, @nxast::16, ethertype::16, 0::size(4)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, ethertype::16, _::size(4)-unit(8)>>) do
%NxPushMpls{ethertype: ethertype}
end
end

View file

@ -0,0 +1,45 @@
defmodule Openflow.Action.NxRegLoad do
import Bitwise
defstruct(
n_bits: 0,
offset: 0,
dst_field: nil,
value: nil
)
@experimenter 0x00002320
@nxast 7
alias __MODULE__
def new(options) do
dst_field = Keyword.get(options, :dst_field)
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
ofs = Keyword.get(options, :offset, 0)
value = Keyword.get(options, :value)
%NxRegLoad{n_bits: n_bits, offset: ofs, dst_field: dst_field, value: value}
end
def to_binary(%NxRegLoad{n_bits: n_bits, offset: ofs, dst_field: dst_field, value: value}) do
dst_field_bin = Openflow.Match.codec_header(dst_field)
value_bin0 = Openflow.Match.Field.codec(value, dst_field)
tmp_value = :binary.decode_unsigned(value_bin0, :big)
value_bin = <<tmp_value::64>>
ofs_nbits = ofs <<< 6 ||| n_bits - 1
body = <<ofs_nbits::16, dst_field_bin::4-bytes, value_bin::bytes>>
exp_body = <<@experimenter::32, @nxast::16, body::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<ofs::10, n_bits::6, dst_field_bin::4-bytes, value_bin::bytes>> = body
dst_field = Openflow.Match.codec_header(dst_field_bin)
value = Openflow.Match.Field.codec(value_bin, dst_field)
%NxRegLoad{n_bits: n_bits + 1, offset: ofs, dst_field: dst_field, value: value}
end
end

View file

@ -0,0 +1,44 @@
defmodule Openflow.Action.NxRegLoad2 do
defstruct(
dst_field: nil,
value: nil
)
@experimenter 0x00002320
@nxast 33
alias __MODULE__
def new(options) do
dst_field = Keyword.get(options, :dst_field)
value = Keyword.get(options, :value)
%NxRegLoad2{dst_field: dst_field, value: value}
end
def to_binary(%NxRegLoad2{dst_field: dst_field, value: value}) do
match_bin =
[{dst_field, value}]
|> Openflow.Match.new()
|> Openflow.Match.to_binary()
<<1::16, _length::16, padded_field::bytes>> = match_bin
patial_len = 4 + 4 + 2 + 6 + byte_size(padded_field)
padding = Openflow.Utils.padding(patial_len, 8)
exp_body =
<<@experimenter::32, @nxast::16, 0::48, padded_field::bytes, 0::size(padding)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, _::48, match_field_bin::bytes>>) do
<<_class::16, _field::7, _hm::1, flen::8, _rest::bytes>> = match_field_bin
match_len = 4 + 4 + flen
match_bin = <<1::16, match_len::16, match_field_bin::bytes, 0::size(4)-unit(8)>>
{[{dst_field, value} | _], _rest} = Openflow.Match.read(match_bin)
%NxRegLoad2{dst_field: dst_field, value: value}
end
end

View file

@ -0,0 +1,67 @@
defmodule Openflow.Action.NxRegMove do
defstruct(
n_bits: 0,
src_offset: 0,
dst_offset: 0,
src_field: nil,
dst_field: nil
)
@experimenter 0x00002320
@nxast 6
alias __MODULE__
def new(options) do
src_field = Keyword.get(options, :src_field)
dst_field = Keyword.get(options, :dst_field)
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
src_ofs = Keyword.get(options, :src_offset, 0)
dst_ofs = Keyword.get(options, :dst_offset, 0)
%NxRegMove{
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs,
src_field: src_field,
dst_field: dst_field
}
end
def to_binary(%NxRegMove{
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs,
src_field: src_field,
dst_field: dst_field
}) do
src_field_bin = Openflow.Match.codec_header(src_field)
dst_field_bin = Openflow.Match.codec_header(dst_field)
body =
<<n_bits::16, src_ofs::16, dst_ofs::16, src_field_bin::4-bytes, dst_field_bin::4-bytes>>
exp_body = <<@experimenter::32, @nxast::16, body::bytes>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<n_bits::16, src_ofs::16, dst_ofs::16, src_field_bin::4-bytes, dst_field_bin::4-bytes>> =
body
src_field = Openflow.Match.codec_header(src_field_bin)
dst_field = Openflow.Match.codec_header(dst_field_bin)
%NxRegMove{
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs,
src_field: src_field,
dst_field: dst_field
}
end
end

View file

@ -0,0 +1,26 @@
defmodule Openflow.Action.NxResubmit do
defstruct(in_port: :in_port)
@experimenter 0x00002320
@nxast 1
alias __MODULE__
def new(in_port \\ :in_port) do
%NxResubmit{in_port: in_port}
end
def to_binary(%NxResubmit{in_port: in_port}) do
in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no)
exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, 0::size(4)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, in_port_int::16, _::size(4)-unit(8)>>) do
in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no)
%NxResubmit{in_port: in_port}
end
end

View file

@ -0,0 +1,36 @@
defmodule Openflow.Action.NxResubmitTable do
defstruct(in_port: :in_port, table_id: :all)
@experimenter 0x00002320
@nxast 14
alias __MODULE__
def new(table_id) when is_atom(table_id) or is_integer(table_id) do
new(table_id: table_id)
end
def new(options) do
in_port = Keyword.get(options, :in_port, :in_port)
table_id = Keyword.get(options, :table_id, :all)
%NxResubmitTable{in_port: in_port, table_id: table_id}
end
def to_binary(%NxResubmitTable{in_port: in_port, table_id: table_id}) do
in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no)
table_id_int = Openflow.Utils.get_enum(table_id, :table_id)
exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::24>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(
<<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::size(3)-unit(8)>>
) do
in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no)
table_id = Openflow.Utils.get_enum(table_id_int, :table_id)
%NxResubmitTable{in_port: in_port, table_id: table_id}
end
end

View file

@ -0,0 +1,32 @@
defmodule Openflow.Action.NxResubmitTableCt do
defstruct(in_port: :in_port, table_id: :all)
@experimenter 0x00002320
@nxast 44
alias __MODULE__
def new(options) do
in_port = Keyword.get(options, :in_port, :in_port)
table_id = Keyword.get(options, :table_id, :all)
%NxResubmitTableCt{in_port: in_port, table_id: table_id}
end
def to_binary(%NxResubmitTableCt{in_port: in_port, table_id: table_id}) do
in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no)
table_id_int = Openflow.Utils.get_enum(table_id, :table_id)
exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::24>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(
<<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::size(3)-unit(8)>>
) do
in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no)
table_id = Openflow.Utils.get_enum(table_id_int, :table_id)
%NxResubmitTableCt{in_port: in_port, table_id: table_id}
end
end

View file

@ -0,0 +1,55 @@
defmodule Openflow.Action.NxSample do
defstruct(
probability: 0,
collector_set_id: 0,
obs_domain_id: 0,
obs_point_id: 0
)
@experimenter 0x00002320
@nxast 29
alias __MODULE__
def new(options) do
probability = Keyword.get(options, :probability, 0)
collector_set_id = Keyword.get(options, :collector_set_id, 0)
obs_domain_id = Keyword.get(options, :obs_domain_id, 0)
obs_point_id = Keyword.get(options, :obs_point_id, 0)
%NxSample{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id
}
end
def to_binary(%NxSample{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id
}) do
exp_body =
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
obs_point_id::32>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
obs_point_id::32>>
) do
%NxSample{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id
}
end
end

View file

@ -0,0 +1,60 @@
defmodule Openflow.Action.NxSample2 do
defstruct(
probability: 0,
collector_set_id: 0,
obs_domain_id: 0,
obs_point_id: 0,
sampling_port: 0
)
@experimenter 0x00002320
@nxast 38
alias __MODULE__
def new(options) do
probability = Keyword.get(options, :probability, 0)
collector_set_id = Keyword.get(options, :collector_set_id, 0)
obs_domain_id = Keyword.get(options, :obs_domain_id, 0)
obs_point_id = Keyword.get(options, :obs_point_id, 0)
sampling_port = Keyword.get(options, :sampling_port, 0)
%NxSample2{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id,
sampling_port: sampling_port
}
end
def to_binary(%NxSample2{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id,
sampling_port: sampling_port
}) do
exp_body =
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
obs_point_id::32, sampling_port::16, 0::size(6)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
obs_point_id::32, sampling_port::16, 0::size(6)-unit(8)>>
) do
%NxSample2{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id,
sampling_port: sampling_port
}
end
end

View file

@ -0,0 +1,69 @@
defmodule Openflow.Action.NxSample3 do
defstruct(
probability: 0,
collector_set_id: 0,
obs_domain_id: 0,
obs_point_id: 0,
sampling_port: 0,
direction: :default
)
@experimenter 0x00002320
@nxast 41
alias __MODULE__
def new(options) do
probability = Keyword.get(options, :probability, 0)
collector_set_id = Keyword.get(options, :collector_set_id, 0)
obs_domain_id = Keyword.get(options, :obs_domain_id, 0)
obs_point_id = Keyword.get(options, :obs_point_id, 0)
sampling_port = Keyword.get(options, :sampling_port, 0)
direction = Keyword.get(options, :direction, :default)
%NxSample3{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id,
sampling_port: sampling_port,
direction: direction
}
end
def to_binary(%NxSample3{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id,
sampling_port: sampling_port,
direction: direction
}) do
direction_int = Openflow.Enums.to_int(direction, :nx_action_sample_direction)
exp_body =
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
obs_point_id::32, sampling_port::16, direction_int::8, 0::size(5)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
obs_point_id::32, sampling_port::16, direction_int::8, 0::size(5)-unit(8)>>
) do
direction = Openflow.Enums.to_atom(direction_int, :nx_action_sample_direction)
%NxSample3{
probability: probability,
collector_set_id: collector_set_id,
obs_domain_id: obs_domain_id,
obs_point_id: obs_point_id,
sampling_port: sampling_port,
direction: direction
}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxSetMplsLabel do
defstruct(label: 0)
@experimenter 0x00002320
@nxast 30
alias __MODULE__
def new(label) do
%NxSetMplsLabel{label: label}
end
def to_binary(%NxSetMplsLabel{label: label}) do
exp_body = <<@experimenter::32, @nxast::16, 0::16, label::32>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, _::16, label::32>>) do
%NxSetMplsLabel{label: label}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxSetMplsTc do
defstruct(tc: 0)
@experimenter 0x00002320
@nxast 31
alias __MODULE__
def new(tc) do
%NxSetMplsTc{tc: tc}
end
def to_binary(%NxSetMplsTc{tc: tc}) do
exp_body = <<@experimenter::32, @nxast::16, tc::8, 0::size(5)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, tc::8, _::size(5)-unit(8)>>) do
%NxSetMplsTc{tc: tc}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxSetMplsTtl do
defstruct(ttl: 0)
@experimenter 0x00002320
@nxast 25
alias __MODULE__
def new(ttl) do
%NxSetMplsTtl{ttl: ttl}
end
def to_binary(%NxSetMplsTtl{ttl: ttl}) do
exp_body = <<@experimenter::32, @nxast::16, ttl::8, 0::size(5)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, ttl::8, _::size(5)-unit(8)>>) do
%NxSetMplsTtl{ttl: ttl}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxSetQueue do
defstruct(queue_id: 0)
@experimenter 0x00002320
@nxast 4
alias __MODULE__
def new(queue_id) do
%NxSetQueue{queue_id: queue_id}
end
def to_binary(%NxSetQueue{queue_id: queue_id}) do
exp_body = <<@experimenter::32, @nxast::16, 0::16, queue_id::32>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, 0::size(2)-unit(8), queue_id::32>>) do
%NxSetQueue{queue_id: queue_id}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxSetTunnel do
defstruct(tunnel_id: 0)
@experimenter 0x00002320
@nxast 2
alias __MODULE__
def new(tunnel_id) do
%NxSetTunnel{tunnel_id: tunnel_id}
end
def to_binary(%NxSetTunnel{tunnel_id: tunnel_id}) do
exp_body = <<@experimenter::32, @nxast::16, 0::16, tunnel_id::32>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, _::16, tunnel_id::32>>) do
%NxSetTunnel{tunnel_id: tunnel_id}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Action.NxSetTunnel64 do
defstruct(tunnel_id: 0)
@experimenter 0x00002320
@nxast 9
alias __MODULE__
def new(tunnel_id) do
%NxSetTunnel64{tunnel_id: tunnel_id}
end
def to_binary(%NxSetTunnel64{tunnel_id: tunnel_id}) do
exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>>) do
%NxSetTunnel64{tunnel_id: tunnel_id}
end
end

View file

@ -0,0 +1,41 @@
defmodule Openflow.Action.NxStackPop do
defstruct(
n_bits: 0,
offset: 0,
field: nil
)
@experimenter 0x00002320
@nxast 28
alias __MODULE__
def new(options) do
field = Keyword.get(options, :field)
default_n_bits = Openflow.Match.Field.n_bits_of(field)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
ofs = Keyword.get(options, :offset, 0)
%NxStackPop{n_bits: n_bits, offset: ofs, field: field}
end
def to_binary(%NxStackPop{n_bits: n_bits, offset: ofs, field: field}) do
field_bin = Openflow.Match.codec_header(field)
exp_body =
<<@experimenter::32, @nxast::16, ofs::16, field_bin::4-bytes, n_bits::16,
0::size(6)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(
<<@experimenter::32, @nxast::16, ofs::16, field_bin::4-bytes, n_bits::16,
_::size(6)-unit(8)>>
) do
field = Openflow.Match.codec_header(field_bin)
%NxStackPop{n_bits: n_bits, offset: ofs, field: field}
end
end

View file

@ -0,0 +1,41 @@
defmodule Openflow.Action.NxStackPush do
defstruct(
n_bits: 0,
offset: 0,
field: nil
)
@experimenter 0x00002320
@nxast 27
alias __MODULE__
def new(options) do
field = Keyword.get(options, :field)
default_n_bits = Openflow.Match.Field.n_bits_of(field)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
ofs = Keyword.get(options, :offset, 0)
%NxStackPush{n_bits: n_bits, offset: ofs, field: field}
end
def to_binary(%NxStackPush{n_bits: n_bits, offset: ofs, field: field}) do
field_bin = Openflow.Match.codec_header(field)
exp_body =
<<@experimenter::32, @nxast::16, ofs::16, field_bin::4-bytes, n_bits::16,
0::size(6)-unit(8)>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(
<<@experimenter::32, @nxast::16, ofs::16, field_bin::4-bytes, n_bits::16,
_::size(6)-unit(8)>>
) do
field = Openflow.Match.codec_header(field_bin)
%NxStackPush{n_bits: n_bits, offset: ofs, field: field}
end
end

View file

@ -0,0 +1,35 @@
defmodule Openflow.Action.NxWriteMetadata do
defstruct(
metadata: 0,
metadata_mask: 0xFFFFFFFFFFFFFFFF
)
@experimenter 0x00002320
@nxast 22
alias __MODULE__
def new(metadata) when is_integer(metadata) do
new(metadata: metadata)
end
def new(options) when is_list(options) do
metadata = Keyword.get(options, :metadata, 0)
metadata_mask = Keyword.get(options, :metadata_mask, 0xFFFFFFFFFFFFFFFF)
%NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask}
end
def to_binary(%NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask}) do
exp_body =
<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), metadata::64, metadata_mask::64>>
exp_body_size = byte_size(exp_body)
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
length = 4 + exp_body_size + padding_length
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
end
def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8), metadata::64, metadata_mask::64>>) do
%NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask}
end
end

View file

@ -0,0 +1,32 @@
defmodule Openflow.Action.Output do
defstruct(
port_number: 0,
max_len: :no_buffer
)
alias __MODULE__
def ofpat, do: 0
def new(port) when not is_list(port) do
new(port_number: port)
end
def new(options) when is_list(options) do
port_no = Keyword.get(options, :port_number)
max_len = Keyword.get(options, :max_len, :no_buffer)
%Output{port_number: port_no, max_len: max_len}
end
def to_binary(%Output{port_number: port_no, max_len: max_len}) do
port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no)
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
<<0::16, 16::16, port_no_int::32, max_len::16, 0::size(6)-unit(8)>>
end
def read(<<0::16, 16::16, port_no_int::32, max_len::16, _pad::size(6)-unit(8)>>) do
port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no)
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
%Output{port_number: port_no, max_len: max_len}
end
end

View file

@ -0,0 +1,19 @@
defmodule Openflow.Action.PopMpls do
defstruct(ethertype: 0x8847)
alias __MODULE__
def ofpat, do: 20
def new(ethertype) do
%PopMpls{ethertype: ethertype}
end
def to_binary(%PopMpls{ethertype: ethertype}) do
<<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>
end
def read(<<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do
%PopMpls{ethertype: ethertype}
end
end

View file

@ -0,0 +1,19 @@
defmodule Openflow.Action.PopPbb do
defstruct([])
alias __MODULE__
def ofpat, do: 27
def new do
%PopPbb{}
end
def to_binary(%PopPbb{}) do
<<27::16, 8::16, 0::size(4)-unit(8)>>
end
def read(<<27::16, 8::16, _::size(4)-unit(8)>>) do
%PopPbb{}
end
end

Some files were not shown because too many files have changed in this diff Show more