Merge branch 'release/0.1.0'
This commit is contained in:
commit
1fe53969d5
389 changed files with 25797 additions and 0 deletions
18
.formatter.exs
Normal file
18
.formatter.exs
Normal 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
23
.gitignore
vendored
Normal 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
13
LICENSE
Normal 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
78
README.md
Normal 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
BIN
bin/enum_gen
Executable file
Binary file not shown.
17
config/config.exs
Normal file
17
config/config.exs
Normal 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
|
||||||
4
examples/leader_example/.formatter.exs
Normal file
4
examples/leader_example/.formatter.exs
Normal 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
24
examples/leader_example/.gitignore
vendored
Normal 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
|
||||||
|
|
||||||
21
examples/leader_example/README.md
Normal file
21
examples/leader_example/README.md
Normal 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).
|
||||||
|
|
||||||
21
examples/leader_example/config/config.exs
Normal file
21
examples/leader_example/config/config.exs
Normal 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"
|
||||||
3
examples/leader_example/config/dev.exs
Normal file
3
examples/leader_example/config/dev.exs
Normal 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
|
||||||
9
examples/leader_example/config/node1.exs
Normal file
9
examples/leader_example/config/node1.exs
Normal 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: []
|
||||||
9
examples/leader_example/config/node2.exs
Normal file
9
examples/leader_example/config/node2.exs
Normal 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: []
|
||||||
20
examples/leader_example/lib/leader_example/application.ex
Normal file
20
examples/leader_example/lib/leader_example/application.ex
Normal 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
|
||||||
77
examples/leader_example/lib/leader_example/leader.ex
Normal file
77
examples/leader_example/lib/leader_example/leader.ex
Normal 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
|
||||||
25
examples/leader_example/mix.exs
Normal file
25
examples/leader_example/mix.exs
Normal 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
|
||||||
10
examples/leader_example/mix.lock
Normal file
10
examples/leader_example/mix.lock
Normal 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"]},
|
||||||
|
}
|
||||||
8
examples/leader_example/test/leader_example_test.exs
Normal file
8
examples/leader_example/test/leader_example_test.exs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule LeaderExampleTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest LeaderExample
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert LeaderExample.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
||||||
1
examples/leader_example/test/test_helper.exs
Normal file
1
examples/leader_example/test/test_helper.exs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
||||||
20
examples/learning_switch/.gitignore
vendored
Normal file
20
examples/learning_switch/.gitignore
vendored
Normal 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
|
||||||
21
examples/learning_switch/README.md
Normal file
21
examples/learning_switch/README.md
Normal 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).
|
||||||
|
|
||||||
17
examples/learning_switch/config/config.exs
Normal file
17
examples/learning_switch/config/config.exs
Normal 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
|
||||||
18
examples/learning_switch/lib/learning_switch.ex
Normal file
18
examples/learning_switch/lib/learning_switch.ex
Normal 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
|
||||||
20
examples/learning_switch/lib/learning_switch/application.ex
Normal file
20
examples/learning_switch/lib/learning_switch/application.ex
Normal 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
|
||||||
66
examples/learning_switch/lib/learning_switch/fdb.ex
Normal file
66
examples/learning_switch/lib/learning_switch/fdb.ex
Normal 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
|
||||||
137
examples/learning_switch/lib/learning_switch/ofctl.ex
Normal file
137
examples/learning_switch/lib/learning_switch/ofctl.ex
Normal 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
|
||||||
23
examples/learning_switch/mix.exs
Normal file
23
examples/learning_switch/mix.exs
Normal 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
|
||||||
7
examples/learning_switch/mix.lock
Normal file
7
examples/learning_switch/mix.lock
Normal 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"]},
|
||||||
|
}
|
||||||
8
examples/learning_switch/test/learning_switch_test.exs
Normal file
8
examples/learning_switch/test/learning_switch_test.exs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule LearningSwitchTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest LearningSwitch
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert LearningSwitch.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
||||||
1
examples/learning_switch/test/test_helper.exs
Normal file
1
examples/learning_switch/test/test_helper.exs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
||||||
4
examples/patch_panel/.formatter.exs
Normal file
4
examples/patch_panel/.formatter.exs
Normal 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
24
examples/patch_panel/.gitignore
vendored
Normal 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
|
||||||
|
|
||||||
16
examples/patch_panel/README.md
Normal file
16
examples/patch_panel/README.md
Normal 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)
|
||||||
|
```
|
||||||
7
examples/patch_panel/config/config.exs
Normal file
7
examples/patch_panel/config/config.exs
Normal 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: []
|
||||||
18
examples/patch_panel/lib/patch_panel.ex
Normal file
18
examples/patch_panel/lib/patch_panel.ex
Normal 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
|
||||||
20
examples/patch_panel/lib/patch_panel/application.ex
Normal file
20
examples/patch_panel/lib/patch_panel/application.ex
Normal 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
|
||||||
81
examples/patch_panel/lib/patch_panel/openflow/controller.ex
Normal file
81
examples/patch_panel/lib/patch_panel/openflow/controller.ex
Normal 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
|
||||||
16
examples/patch_panel/lib/patch_panel/openflow/registry.ex
Normal file
16
examples/patch_panel/lib/patch_panel/openflow/registry.ex
Normal 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
|
||||||
26
examples/patch_panel/mix.exs
Normal file
26
examples/patch_panel/mix.exs
Normal 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
|
||||||
6
examples/patch_panel/mix.lock
Normal file
6
examples/patch_panel/mix.lock
Normal 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"]},
|
||||||
|
}
|
||||||
8
examples/patch_panel/test/patch_panel_test.exs
Normal file
8
examples/patch_panel/test/patch_panel_test.exs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule PatchPanelTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest PatchPanel
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert PatchPanel.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
||||||
1
examples/patch_panel/test/test_helper.exs
Normal file
1
examples/patch_panel/test/test_helper.exs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
||||||
BIN
images/tres_inside.png
Normal file
BIN
images/tres_inside.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
72
lib/openflow.ex
Normal file
72
lib/openflow.ex
Normal 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
31
lib/openflow/action.ex
Normal 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
|
||||||
19
lib/openflow/actions/copy_ttl_in.ex
Normal file
19
lib/openflow/actions/copy_ttl_in.ex
Normal 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
|
||||||
19
lib/openflow/actions/copy_ttl_out.ex
Normal file
19
lib/openflow/actions/copy_ttl_out.ex
Normal 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
|
||||||
19
lib/openflow/actions/dec_mpls_ttl.ex
Normal file
19
lib/openflow/actions/dec_mpls_ttl.ex
Normal 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
|
||||||
19
lib/openflow/actions/dec_nw_ttl.ex
Normal file
19
lib/openflow/actions/dec_nw_ttl.ex
Normal 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
|
||||||
34
lib/openflow/actions/experimenter.ex
Normal file
34
lib/openflow/actions/experimenter.ex
Normal 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
|
||||||
19
lib/openflow/actions/group.ex
Normal file
19
lib/openflow/actions/group.ex
Normal 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
|
||||||
91
lib/openflow/actions/nx_bundle.ex
Normal file
91
lib/openflow/actions/nx_bundle.ex
Normal 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
|
||||||
110
lib/openflow/actions/nx_bundle_load.ex
Normal file
110
lib/openflow/actions/nx_bundle_load.ex
Normal 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
|
||||||
26
lib/openflow/actions/nx_clone.ex
Normal file
26
lib/openflow/actions/nx_clone.ex
Normal 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
|
||||||
29
lib/openflow/actions/nx_conjunction.ex
Normal file
29
lib/openflow/actions/nx_conjunction.ex
Normal 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
|
||||||
90
lib/openflow/actions/nx_conntrack.ex
Normal file
90
lib/openflow/actions/nx_conntrack.ex
Normal 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
|
||||||
39
lib/openflow/actions/nx_controller.ex
Normal file
39
lib/openflow/actions/nx_controller.ex
Normal 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
|
||||||
140
lib/openflow/actions/nx_controller2.ex
Normal file
140
lib/openflow/actions/nx_controller2.ex
Normal 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
|
||||||
21
lib/openflow/actions/nx_ct_clear.ex
Normal file
21
lib/openflow/actions/nx_ct_clear.ex
Normal 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
|
||||||
21
lib/openflow/actions/nx_dec_mpls_ttl.ex
Normal file
21
lib/openflow/actions/nx_dec_mpls_ttl.ex
Normal 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
|
||||||
21
lib/openflow/actions/nx_dec_ttl.ex
Normal file
21
lib/openflow/actions/nx_dec_ttl.ex
Normal 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
|
||||||
34
lib/openflow/actions/nx_dec_ttl_cnt_ids.ex
Normal file
34
lib/openflow/actions/nx_dec_ttl_cnt_ids.ex
Normal 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
|
||||||
21
lib/openflow/actions/nx_exit.ex
Normal file
21
lib/openflow/actions/nx_exit.ex
Normal 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
|
||||||
27
lib/openflow/actions/nx_fin_timeout.ex
Normal file
27
lib/openflow/actions/nx_fin_timeout.ex
Normal 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
|
||||||
31
lib/openflow/actions/nx_flow_spec.ex
Normal file
31
lib/openflow/actions/nx_flow_spec.ex
Normal 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
|
||||||
93
lib/openflow/actions/nx_flow_spec_load.ex
Normal file
93
lib/openflow/actions/nx_flow_spec_load.ex
Normal 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
|
||||||
93
lib/openflow/actions/nx_flow_spec_match.ex
Normal file
93
lib/openflow/actions/nx_flow_spec_match.ex
Normal 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
|
||||||
32
lib/openflow/actions/nx_flow_spec_output.ex
Normal file
32
lib/openflow/actions/nx_flow_spec_output.ex
Normal 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
|
||||||
76
lib/openflow/actions/nx_learn.ex
Normal file
76
lib/openflow/actions/nx_learn.ex
Normal 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
|
||||||
106
lib/openflow/actions/nx_learn2.ex
Normal file
106
lib/openflow/actions/nx_learn2.ex
Normal 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
|
||||||
88
lib/openflow/actions/nx_multipath.ex
Normal file
88
lib/openflow/actions/nx_multipath.ex
Normal 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
|
||||||
145
lib/openflow/actions/nx_nat.ex
Normal file
145
lib/openflow/actions/nx_nat.ex
Normal 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
|
||||||
26
lib/openflow/actions/nx_note.ex
Normal file
26
lib/openflow/actions/nx_note.ex
Normal 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
|
||||||
43
lib/openflow/actions/nx_output_reg.ex
Normal file
43
lib/openflow/actions/nx_output_reg.ex
Normal 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
|
||||||
54
lib/openflow/actions/nx_output_reg2.ex
Normal file
54
lib/openflow/actions/nx_output_reg2.ex
Normal 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
|
||||||
31
lib/openflow/actions/nx_output_trunc.ex
Normal file
31
lib/openflow/actions/nx_output_trunc.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_pop_mpls.ex
Normal file
24
lib/openflow/actions/nx_pop_mpls.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_pop_queue.ex
Normal file
24
lib/openflow/actions/nx_pop_queue.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_push_mpls.ex
Normal file
24
lib/openflow/actions/nx_push_mpls.ex
Normal 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
|
||||||
45
lib/openflow/actions/nx_reg_load.ex
Normal file
45
lib/openflow/actions/nx_reg_load.ex
Normal 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
|
||||||
44
lib/openflow/actions/nx_reg_load2.ex
Normal file
44
lib/openflow/actions/nx_reg_load2.ex
Normal 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
|
||||||
67
lib/openflow/actions/nx_reg_move.ex
Normal file
67
lib/openflow/actions/nx_reg_move.ex
Normal 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
|
||||||
26
lib/openflow/actions/nx_resubmit.ex
Normal file
26
lib/openflow/actions/nx_resubmit.ex
Normal 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
|
||||||
36
lib/openflow/actions/nx_resubmit_table.ex
Normal file
36
lib/openflow/actions/nx_resubmit_table.ex
Normal 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
|
||||||
32
lib/openflow/actions/nx_resubmit_table_ct.ex
Normal file
32
lib/openflow/actions/nx_resubmit_table_ct.ex
Normal 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
|
||||||
55
lib/openflow/actions/nx_sample.ex
Normal file
55
lib/openflow/actions/nx_sample.ex
Normal 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
|
||||||
60
lib/openflow/actions/nx_sample2.ex
Normal file
60
lib/openflow/actions/nx_sample2.ex
Normal 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
|
||||||
69
lib/openflow/actions/nx_sample3.ex
Normal file
69
lib/openflow/actions/nx_sample3.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_set_mpls_label.ex
Normal file
24
lib/openflow/actions/nx_set_mpls_label.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_set_mpls_tc.ex
Normal file
24
lib/openflow/actions/nx_set_mpls_tc.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_set_mpls_ttl.ex
Normal file
24
lib/openflow/actions/nx_set_mpls_ttl.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_set_queue.ex
Normal file
24
lib/openflow/actions/nx_set_queue.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_set_tunnel.ex
Normal file
24
lib/openflow/actions/nx_set_tunnel.ex
Normal 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
|
||||||
24
lib/openflow/actions/nx_set_tunnel64.ex
Normal file
24
lib/openflow/actions/nx_set_tunnel64.ex
Normal 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
|
||||||
41
lib/openflow/actions/nx_stack_pop.ex
Normal file
41
lib/openflow/actions/nx_stack_pop.ex
Normal 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
|
||||||
41
lib/openflow/actions/nx_stack_push.ex
Normal file
41
lib/openflow/actions/nx_stack_push.ex
Normal 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
|
||||||
35
lib/openflow/actions/nx_write_metadata.ex
Normal file
35
lib/openflow/actions/nx_write_metadata.ex
Normal 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
|
||||||
32
lib/openflow/actions/output.ex
Normal file
32
lib/openflow/actions/output.ex
Normal 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
|
||||||
19
lib/openflow/actions/pop_mpls.ex
Normal file
19
lib/openflow/actions/pop_mpls.ex
Normal 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
|
||||||
19
lib/openflow/actions/pop_pbb.ex
Normal file
19
lib/openflow/actions/pop_pbb.ex
Normal 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
Loading…
Add table
Add a link
Reference in a new issue