Merge branch 'release/0.1.1'
This commit is contained in:
commit
c8e1fbbbc5
228 changed files with 10073 additions and 3841 deletions
|
|
@ -1,5 +1,5 @@
|
|||
[
|
||||
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||
inputs: ["mix.exs", "{config,lib,test,priv}/**/*.{ex,exs}"],
|
||||
|
||||
locals_without_parens: [
|
||||
# Formatter tests
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -21,3 +21,5 @@ erl_crash.dump
|
|||
|
||||
# Examples
|
||||
*/polaris/*
|
||||
|
||||
/bin/enum_gen
|
||||
|
|
|
|||
34
.travis.yml
Normal file
34
.travis.yml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
sudo: true
|
||||
dist: xenial
|
||||
language: elixir
|
||||
elixir:
|
||||
- "1.8.1"
|
||||
otp_release:
|
||||
- "21.3.6"
|
||||
|
||||
before_install:
|
||||
- sudo apt-get install -y wget curl gdebi debhelper unbound
|
||||
- sudo apt-get install build-essential fakeroot autoconf automake libssl-dev bzip2 openssl graphviz python-all procps python-qt4 python-zopeinterface python-twisted-conch libtool dh-autoreconf libunbound-dev
|
||||
- wget https://github.com/openvswitch/ovs/archive/v2.11.0.tar.gz
|
||||
- tar xvzf v2.11.0.tar.gz
|
||||
- cd ovs-2.11.0/
|
||||
- sudo DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary
|
||||
- sudo dpkg -i ../python-openvswitch_2.11.0-1_all.deb
|
||||
- sudo dpkg -i ../libopenvswitch_2.11.0-1_amd64.deb
|
||||
- sudo dpkg -i ../libopenvswitch-dev_2.11.0-1_amd64.deb
|
||||
- sudo dpkg -i ../openvswitch-common_2.11.0-1_amd64.deb
|
||||
- sudo dpkg -i ../openvswitch-switch_2.11.0-1_amd64.deb
|
||||
- sudo ovs-vsctl add-br br0
|
||||
- sudo ovs-vsctl set bridge br0 datapath_type=netdev
|
||||
- sudo ovs-vsctl set bridge br0 protocols=OpenFlow13
|
||||
- sudo ovs-vsctl set bridge br0 other-config:datapath-id=0000000000000001
|
||||
- sudo ovs-vsctl set-controller br0 tcp:127.0.0.1:6653
|
||||
- sudo ovs-vsctl set-manager ptcp:6640
|
||||
- sudo ovs-vsctl show
|
||||
- cd -
|
||||
|
||||
script:
|
||||
- MIX_ENV=test mix do compile, coveralls.json
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
10
CHANGELOG.md
Normal file
10
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
## develop (unreleased)
|
||||
|
||||
### New Features
|
||||
* tres/secure_channel: Add a new function for blocking send
|
||||
|
||||
### Bugs Fixed
|
||||
* tres/secure_channel: Fix to interpolate terms when logging
|
||||
* tres/message_handler: Fix to use `DynamicSupervisor` instead of `:simple_one_for_one`
|
||||
19
LICENSE
19
LICENSE
|
|
@ -1,13 +1,12 @@
|
|||
Copyright [2018] [Eishun Kondoh]
|
||||
# "THE SUSHI-WARE LICENSE"
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
<dreamdiagnosis@gmail.com> wrote this file.
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
As long as you retain this notice you can do whatever you want
|
||||
with this stuff. If we meet some day, and you think this stuff
|
||||
is worth it, you can buy me a **sushi 🍣** in return.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
(This license is based on ["THE BEER-WARE LICENSE" (Revision 42)].
|
||||
Thanks a lot, Poul-Henning Kamp ;)
|
||||
|
||||
["THE BEER-WARE LICENSE" (Revision 42)]: https://people.freebsd.org/~phk/
|
||||
|
|
|
|||
18
README.md
18
README.md
|
|
@ -1,5 +1,11 @@
|
|||
# Tres - an Elixir OpenFlow development platform
|
||||
|
||||
[](https://travis-ci.org/shun159/tres)
|
||||
[](https://codecov.io/gh/shun159/tres)
|
||||
[](http://erlang.org/)
|
||||
[](https://github.com/MakeNowJust/sushi-ware)
|
||||
[](https://hex.pm/packages/tres/0.1.0)
|
||||
|
||||
## Overview
|
||||
|
||||
Tres is a framework and set of helper libraries to develop OpenFlow controllers in Elixir.
|
||||
|
|
@ -11,7 +17,7 @@ Tres is a framework and set of helper libraries to develop OpenFlow controllers
|
|||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:tres, github: "shun159/tres", branch: "develop"}
|
||||
{:tres, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
|
@ -25,7 +31,7 @@ config :tres,
|
|||
```
|
||||
|
||||
To use `Tres.Controller` with your code, set the handler callback_module to your callback module.
|
||||
This module must implement the `Module.start_link/2` that returns `on_start`.
|
||||
This module must implement the `Module.start_link/1` that returns `on_start`.
|
||||
|
||||
Set the callback_args to the terms you want pass to the `start_link/2` callback function.
|
||||
|
||||
|
|
@ -35,7 +41,7 @@ defmodule Sample do
|
|||
use GenServer
|
||||
use Tres.Controller
|
||||
|
||||
def start_link(datapath_id, _start_args) do
|
||||
def start_link([datapath_id, _start_args]) do
|
||||
GenServer.start_link(__MODULE__, [datapath_id])
|
||||
end
|
||||
|
||||
|
|
@ -70,9 +76,11 @@ $ iex -S mix
|
|||
- learning-switch: Simple Layer2 switch
|
||||
- leader-example: Simple election based multiple controller using Ulf Wiger's Locks Leader
|
||||
- patch\_panel: inteligent patch\_panel example
|
||||
- simple\_router: An OpenFlow controller that emulates layer 3 switch (router).
|
||||
- nx\_learning\_switch: Example for very simple-minded L2 switch using NXAST_LEARN action.
|
||||
|
||||
License
|
||||
-------
|
||||
Tres is released under the Apache license Version 2.0:
|
||||
Tres is released under the __SUSHI-WARE LICENSE__.
|
||||
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
__私に寿司をおごってください__
|
||||
|
|
|
|||
BIN
bin/enum_gen
BIN
bin/enum_gen
Binary file not shown.
2
codecov.yml
Normal file
2
codecov.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
codecov:
|
||||
token: db65a412-7a83-4c38-ad5b-9c68677c6680
|
||||
|
|
@ -11,7 +11,7 @@ config :tres,
|
|||
callback_args: []
|
||||
|
||||
config :logger,
|
||||
level: :info,
|
||||
format: "$date $time [$level] $metadata$message\n",
|
||||
metadata: [:application],
|
||||
level: :debug,
|
||||
format: "$date $time [$level] $message\n",
|
||||
metadata: [],
|
||||
handle_otp_reports: true
|
||||
|
|
|
|||
11
coveralls.json
Normal file
11
coveralls.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"skip_files": [
|
||||
"lib/openflow/enums.ex"
|
||||
],
|
||||
|
||||
"coverage_options": {
|
||||
"output_dir": "cover/",
|
||||
"template_path": "lib/templates/html/htmlcov/",
|
||||
"minimum_coverage": 0
|
||||
}
|
||||
}
|
||||
|
|
@ -63,14 +63,18 @@ defmodule LearningSwitch.Ofctl do
|
|||
end
|
||||
|
||||
defp init_flow_tables(datapath_id) do
|
||||
:ok = onf_bundle_open(datapath_id, bundle_id: 1, flags: [:atomic, :ordered])
|
||||
|
||||
for flow_options <- [
|
||||
add_default_broadcast_flow_entry(),
|
||||
add_default_flooding_flow_entry(),
|
||||
add_multicast_mac_drop_flow_entry(),
|
||||
add_ipv6_multicast_mac_drop_flow_entry(),
|
||||
add_default_forwarding_flow_entry()] do
|
||||
send_flow_mod_add(datapath_id, flow_options)
|
||||
send_flow_mod_add(datapath_id, Keyword.merge(flow_options, [bundle_id: 1, bundle_flags: [:atomic, :ordered]]))
|
||||
end
|
||||
|
||||
:ok = onf_bundle_commit(datapath_id, bundle_id: 1, flags: [:atomic, :ordered])
|
||||
end
|
||||
|
||||
defp add_forwarding_flow_and_packet_out(packet_in, state) do
|
||||
|
|
@ -81,12 +85,8 @@ defmodule LearningSwitch.Ofctl do
|
|||
packet_out(packet_in, port_no || :flood)
|
||||
end
|
||||
|
||||
defp packet_out(%PacketIn{datapath_id: datapath_id, data: data}, port_no) do
|
||||
send_packet_out(
|
||||
datapath_id,
|
||||
data: data,
|
||||
actions: [Output.new(port_no)]
|
||||
)
|
||||
defp packet_out(%PacketIn{} = pin, port_no) do
|
||||
send_packet_out(packet_in: pin, actions: [Output.new(port_no)])
|
||||
end
|
||||
|
||||
defp add_forwarding_flow_entry(_packet_in, nil), do: :noop
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
%{
|
||||
"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]},
|
||||
"eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]},
|
||||
"jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]},
|
||||
"ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"},
|
||||
"jsone": {:git, "https://github.com/sile/jsone.git", "b23d312a5ed051ea7ad0989a9f2cb1a9c3f9a502", [tag: "1.4.6"]},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||
"uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]},
|
||||
}
|
||||
|
|
|
|||
4
examples/nx_learning_switch/.formatter.exs
Normal file
4
examples/nx_learning_switch/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
examples/nx_learning_switch/.gitignore
vendored
Normal file
24
examples/nx_learning_switch/.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 third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
nx_learning_switch-*.tar
|
||||
|
||||
3
examples/nx_learning_switch/README.md
Normal file
3
examples/nx_learning_switch/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# NxLearningSwitch
|
||||
|
||||
Example for a very simple-minded MAC learning switch
|
||||
17
examples/nx_learning_switch/config/config.exs
Normal file
17
examples/nx_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: NxLearningSwitch,
|
||||
callback_args: []
|
||||
|
||||
config :logger,
|
||||
level: :debug,
|
||||
format: "$date $time [$level] $metadata$message\n",
|
||||
metadata: [:application],
|
||||
handle_otp_reports: true
|
||||
93
examples/nx_learning_switch/lib/nx_learning_switch.ex
Normal file
93
examples/nx_learning_switch/lib/nx_learning_switch.ex
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
defmodule NxLearningSwitch do
|
||||
@moduledoc """
|
||||
Emulates Layer2 switch
|
||||
"""
|
||||
|
||||
use GenServer
|
||||
use Tres.Controller
|
||||
|
||||
import Logger
|
||||
|
||||
# API functions
|
||||
|
||||
def start_link(datapath_id, _args) do
|
||||
GenServer.start_link(__MODULE__, [datapath_id])
|
||||
end
|
||||
|
||||
# GenServer callback functions
|
||||
|
||||
@impl GenServer
|
||||
def init([{datapath_id, _aux_id}]) do
|
||||
:ok = info("Connected: datapath_id: #{datapath_id}")
|
||||
{:ok, datapath_id, {:continue, :init}}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_continue(:init, datapath_id) do
|
||||
:ok = l2_learning_flow(datapath_id)
|
||||
:ok = l2_flooding_flows(datapath_id)
|
||||
{:noreply, datapath_id}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call(_request, _from, state) do
|
||||
{:reply, :ok, state}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_cast(_request, state) do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_info({:switch_disconnected, reason}, datapath_id) do
|
||||
:ok = warn("Disconnected: datapath_id: #{datapath_id} by #{inspect(reason)}")
|
||||
{:stop, :normal, datapath_id}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_info(_info, state) do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
## private functions
|
||||
|
||||
defp l2_learning_flow(datapath_id) do
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
table_id: 0,
|
||||
instructions: ApplyActions.new([
|
||||
NxLearn.new(
|
||||
table_id: 1,
|
||||
priority: 2,
|
||||
hard_timeout: 10,
|
||||
flow_specs: [
|
||||
NxFlowSpecMatch.new(
|
||||
src: :nx_eth_src,
|
||||
dst: :nx_eth_dst
|
||||
),
|
||||
NxFlowSpecMatch.new(
|
||||
src: :nx_vlan_tci,
|
||||
dst: :nx_vlan_tci,
|
||||
offset: 0,
|
||||
n_bits: 12
|
||||
),
|
||||
NxFlowSpecOutput.new(
|
||||
src: :nx_in_port
|
||||
)
|
||||
]
|
||||
),
|
||||
NxResubmitTable.new(1)
|
||||
])
|
||||
)
|
||||
end
|
||||
|
||||
defp l2_flooding_flows(datapath_id) do
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
table_id: 1,
|
||||
priority: 0,
|
||||
instructions: ApplyActions.new(Output.new(:flood))
|
||||
)
|
||||
end
|
||||
end
|
||||
27
examples/nx_learning_switch/mix.exs
Normal file
27
examples/nx_learning_switch/mix.exs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
defmodule NxLearningSwitch.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :nx_learning_switch,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.8",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:tres, path: "../../../tres"}
|
||||
]
|
||||
end
|
||||
end
|
||||
3
examples/nx_learning_switch/mix.lock
Normal file
3
examples/nx_learning_switch/mix.lock
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
%{
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
defmodule NxLearningSwitchTest do
|
||||
use ExUnit.Case
|
||||
doctest NxLearningSwitch
|
||||
|
||||
test "greets the world" do
|
||||
assert NxLearningSwitch.hello() == :world
|
||||
end
|
||||
end
|
||||
1
examples/nx_learning_switch/test/test_helper.exs
Normal file
1
examples/nx_learning_switch/test/test_helper.exs
Normal file
|
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
||||
4
examples/simple_router/.formatter.exs
Normal file
4
examples/simple_router/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
examples/simple_router/.gitignore
vendored
Normal file
24
examples/simple_router/.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").
|
||||
simple_router-*.tar
|
||||
|
||||
47
examples/simple_router/README.md
Normal file
47
examples/simple_router/README.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# SimpleRouter
|
||||
|
||||
#### An OpenFlow controller that emulates layer 3 switch (router).
|
||||
|
||||
## Configuration
|
||||
|
||||
```elixir
|
||||
config :simple_router,
|
||||
interfaces: %{
|
||||
"veth1" => %{mac_address: "02:00:00:01:00:01", ip_address: "192.168.1.1/24"},
|
||||
"veth3" => %{mac_address: "02:00:00:01:00:02", ip_address: "192.168.2.1/24"},
|
||||
},
|
||||
routes: %{
|
||||
"0.0.0.0/0" => "192.168.1.2"
|
||||
}
|
||||
```
|
||||
|
||||
## Flow Tables
|
||||
|
||||
```
|
||||
> $ sudo ovs-ofctl dump-flows br0 -OOpenFlow13 --color=auto
|
||||
cookie=0x0, duration=10.217s, table=0, n_packets=3, n_bytes=126, priority=1,arp actions=goto_table:1
|
||||
cookie=0x0, duration=10.217s, table=0, n_packets=19, n_bytes=1862, priority=1,ip actions=goto_table:2
|
||||
cookie=0x0, duration=10.208s, table=1, n_packets=1, n_bytes=42, priority=0,arp,in_port=veth1,arp_tpa=192.168.1.1,arp_op=1 actions=learn(table=4,idle_timeout=300,priority=1,NXM_NX_REG0[]=NXM_OF_ARP_SPA[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[]),push:NXM_OF_ARP_TPA[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],push:NXM_OF_ETH_SRC[],pop:NXM_OF_ETH_DST[],pop:NXM_OF_ARP_TPA[],pop:NXM_NX_ARP_THA[],pop:NXM_OF_ARP_SPA[],set_field:2->arp_op,set_field:02:00:00:01:00:01->arp_sha,set_field:02:00:00:01:00:01->eth_src,IN_PORT
|
||||
cookie=0x0, duration=10.207s, table=1, n_packets=1, n_bytes=42, priority=0,arp,in_port=veth1,arp_tpa=192.168.1.1,arp_op=2 actions=learn(table=4,idle_timeout=300,priority=1,NXM_NX_REG0[]=NXM_OF_ARP_SPA[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[])
|
||||
cookie=0x0, duration=10.206s, table=1, n_packets=1, n_bytes=42, priority=0,arp,in_port=veth3,arp_tpa=192.168.2.1,arp_op=1 actions=learn(table=4,idle_timeout=300,priority=1,NXM_NX_REG0[]=NXM_OF_ARP_SPA[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[]),push:NXM_OF_ARP_TPA[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],push:NXM_OF_ETH_SRC[],pop:NXM_OF_ETH_DST[],pop:NXM_OF_ARP_TPA[],pop:NXM_NX_ARP_THA[],pop:NXM_OF_ARP_SPA[],set_field:2->arp_op,set_field:02:00:00:01:00:02->arp_sha,set_field:02:00:00:01:00:02->eth_src,IN_PORT
|
||||
cookie=0x0, duration=10.206s, table=1, n_packets=0, n_bytes=0, priority=0,arp,in_port=veth3,arp_tpa=192.168.2.1,arp_op=2 actions=learn(table=4,idle_timeout=300,priority=1,NXM_NX_REG0[]=NXM_OF_ARP_SPA[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[])
|
||||
cookie=0x0, duration=10.205s, table=2, n_packets=0, n_bytes=0, priority=124,ip,nw_dst=192.168.1.0/24 actions=move:NXM_OF_IP_DST[]->NXM_NX_REG0[],goto_table:3
|
||||
cookie=0x0, duration=10.205s, table=2, n_packets=9, n_bytes=882, priority=124,ip,nw_dst=192.168.2.0/24 actions=move:NXM_OF_IP_DST[]->NXM_NX_REG0[],goto_table:3
|
||||
cookie=0x0, duration=10.205s, table=2, n_packets=10, n_bytes=980, priority=0,ip actions=set_field:0xc0a80102->reg0,goto_table:3
|
||||
cookie=0x0, duration=10.212s, table=3, n_packets=10, n_bytes=980, priority=24,reg0=0xc0a80100/0xffffff00 actions=set_field:0x1->reg1,resubmit(,4),resubmit(,5)
|
||||
cookie=0x0, duration=10.212s, table=3, n_packets=9, n_bytes=882, priority=24,reg0=0xc0a80200/0xffffff00 actions=set_field:0x2->reg1,resubmit(,4),resubmit(,5)
|
||||
cookie=0x0, duration=9.891s, table=4, n_packets=9, n_bytes=882, idle_timeout=300, priority=1,reg0=0xc0a80202 actions=load:0x9a9114dd25d8->NXM_OF_ETH_DST[]
|
||||
cookie=0x0, duration=9.855s, table=4, n_packets=9, n_bytes=882, idle_timeout=300, priority=1,reg0=0xc0a80102 actions=load:0x7a69d574651c->NXM_OF_ETH_DST[]
|
||||
cookie=0x0, duration=10.207s, table=4, n_packets=1, n_bytes=98, priority=0,reg1=0x1 actions=controller(userdata=41.52.50.5f.6d.69.73.73.69.6e.67.ff.ff.ff.ff.ff.ff.02.00.00.01.00.01.08.06.00.01.08.00.06.04.00.01.02.00.00.01.00.01.c0.a8.01.01.00.00.00.00.00.00,pause)
|
||||
cookie=0x0, duration=10.206s, table=4, n_packets=0, n_bytes=0, priority=0,reg1=0x2 actions=controller(userdata=41.52.50.5f.6d.69.73.73.69.6e.67.ff.ff.ff.ff.ff.ff.02.00.00.01.00.02.08.06.00.01.08.00.06.04.00.01.02.00.00.01.00.02.c0.a8.02.01.00.00.00.00.00.00,pause)
|
||||
cookie=0x0, duration=10.206s, table=5, n_packets=9, n_bytes=882, priority=1,reg1=0x1 actions=group:1
|
||||
cookie=0x0, duration=10.205s, table=5, n_packets=9, n_bytes=882, priority=1,reg1=0x2 actions=group:2
|
||||
```
|
||||
|
||||
## Group Tables
|
||||
```
|
||||
> $ sudo ovs-ofctl dump-groups br0 -OOpenFlow13 --color=auto
|
||||
OFPST_GROUP_DESC reply (OF1.3) (xid=0x2):
|
||||
group_id=1,type=indirect,bucket=actions=set_field:02:00:00:01:00:01->eth_src,output:veth1
|
||||
group_id=2,type=indirect,bucket=actions=set_field:02:00:00:01:00:02->eth_src,output:veth3
|
||||
```
|
||||
20
examples/simple_router/config/config.exs
Normal file
20
examples/simple_router/config/config.exs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# This file is responsible for configuring your application
|
||||
# and its dependencies with the aid of the Mix.Config module.
|
||||
use Mix.Config
|
||||
|
||||
config :tres,
|
||||
protocol: :tcp,
|
||||
port: 6653,
|
||||
max_connections: 10,
|
||||
num_acceptors: 10,
|
||||
callback_module: SimpleRouter.Openflow.Controller,
|
||||
callback_args: []
|
||||
|
||||
config :simple_router,
|
||||
interfaces: %{
|
||||
"veth1" => %{mac_address: "02:00:00:01:00:01", ip_address: "192.168.1.1/24"},
|
||||
"veth3" => %{mac_address: "02:00:00:01:00:02", ip_address: "192.168.2.1/24"},
|
||||
},
|
||||
routes: %{
|
||||
"0.0.0.0/0" => "192.168.1.2"
|
||||
}
|
||||
18
examples/simple_router/lib/simple_router.ex
Normal file
18
examples/simple_router/lib/simple_router.ex
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
defmodule SimpleRouter do
|
||||
@moduledoc """
|
||||
Documentation for SimpleRouter.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Hello world.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> SimpleRouter.hello()
|
||||
:world
|
||||
|
||||
"""
|
||||
def hello do
|
||||
:world
|
||||
end
|
||||
end
|
||||
9
examples/simple_router/lib/simple_router/application.ex
Normal file
9
examples/simple_router/lib/simple_router/application.ex
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
defmodule SimpleRouter.Application do
|
||||
@moduledoc false
|
||||
|
||||
use Application
|
||||
|
||||
def start(_type, _args) do
|
||||
SimpleRouter.Supervisor.start_link()
|
||||
end
|
||||
end
|
||||
55
examples/simple_router/lib/simple_router/config.ex
Normal file
55
examples/simple_router/lib/simple_router/config.ex
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
defmodule SimpleRouter.Config do
|
||||
@moduledoc false
|
||||
|
||||
alias Tres.IPv4Address
|
||||
|
||||
@spec interfaces() :: %{String.t() => map()}
|
||||
def interfaces do
|
||||
:interfaces
|
||||
|> get_env(%{})
|
||||
|> Enum.reduce(%{}, &interfaces_to_erl/2)
|
||||
end
|
||||
|
||||
@spec routes() :: [tuple()]
|
||||
def routes do
|
||||
:routes
|
||||
|> get_env(%{})
|
||||
|> Enum.reduce([], &routes_to_erl/2)
|
||||
end
|
||||
|
||||
# private functions
|
||||
|
||||
defp interfaces_to_erl({ifname, %{mac_address: mac, ip_address: ip}}, acc) do
|
||||
{ipaddr, mask} = IPv4Address.parse(ip)
|
||||
|
||||
entry =
|
||||
%{
|
||||
mac_address: String.replace(mac, ~r/:/, ""),
|
||||
ip_address: ipaddr,
|
||||
subnet_mask: mask,
|
||||
network_address: IPv4Address.to_network({ipaddr, mask}),
|
||||
prefix_length: get_cidr(ip)
|
||||
}
|
||||
|
||||
Map.merge(acc, %{ifname => entry})
|
||||
end
|
||||
|
||||
defp routes_to_erl({dst, nexthop}, acc) do
|
||||
{dstaddr, mask} = IPv4Address.parse(dst)
|
||||
{nexthop, _netmask} = IPv4Address.parse(nexthop)
|
||||
entry = %{dst: dstaddr, mask: mask, prefix_length: get_cidr(dst), nexthop: nexthop}
|
||||
[entry | acc]
|
||||
end
|
||||
|
||||
defp get_cidr(ip) do
|
||||
case String.split(ip, ~r/\//) do
|
||||
[^ip] ->
|
||||
32
|
||||
[_addr, cidr] ->
|
||||
String.to_integer(cidr)
|
||||
end
|
||||
end
|
||||
|
||||
defp get_env(key, default),
|
||||
do: Application.get_env(:simple_router, key, default)
|
||||
end
|
||||
139
examples/simple_router/lib/simple_router/openflow/controller.ex
Normal file
139
examples/simple_router/lib/simple_router/openflow/controller.ex
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
defmodule SimpleRouter.Openflow.Controller do
|
||||
@moduledoc """
|
||||
Emulates Router
|
||||
"""
|
||||
|
||||
use GenServer
|
||||
use Tres.Controller
|
||||
|
||||
import Logger
|
||||
|
||||
alias SimpleRouter.Config
|
||||
alias SimpleRouter.Openflow.FlowTables
|
||||
alias SimpleRouter.Openflow.GroupTables
|
||||
|
||||
defmodule State do
|
||||
@moduledoc false
|
||||
|
||||
defstruct [
|
||||
datapath_id: nil,
|
||||
ports: %{}
|
||||
]
|
||||
end
|
||||
|
||||
def start_link({datapath_id, _aux_id}, _start_args) do
|
||||
GenServer.start_link(__MODULE__, [datapath_id])
|
||||
end
|
||||
|
||||
def init([datapath_id]) do
|
||||
:ok = debug("Switch Connected: datapath_id = #{datapath_id}")
|
||||
{:ok, %State{datapath_id: datapath_id}, {:continue, :init_datapath}}
|
||||
end
|
||||
|
||||
def handle_continue(:init_datapath, state) do
|
||||
:ok = init_datapath(state.datapath_id)
|
||||
{:noreply, state, {:continue, :init_interface}}
|
||||
end
|
||||
|
||||
def handle_continue(:init_interface, state) do
|
||||
port_desc = PortDesc.Request.new()
|
||||
:ok = send_message(port_desc, state.datapath_id)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(%PortDesc.Reply{datapath_id: dpid, ports: ports}, state) do
|
||||
:ok = debug("port_reply from dpid: #{dpid}")
|
||||
ports = handle_ports(ports)
|
||||
routes = Config.routes()
|
||||
:ok = onf_bundle_open(dpid, bundle_id: 1, flags: [:atomic, :ordered])
|
||||
:ok = FlowTables.classifier(dpid)
|
||||
:ok = init_egress_groups(ports, dpid)
|
||||
:ok = init_interface_lookup_flows(ports, dpid)
|
||||
:ok = init_arp_handler_flows(ports, dpid)
|
||||
:ok = init_egress_flows(ports, dpid)
|
||||
:ok = init_connected_routes(ports, dpid)
|
||||
:ok = init_static_routes(routes, dpid)
|
||||
:ok = onf_bundle_commit(dpid, bundle_id: 1, flags: [:atomic, :ordered])
|
||||
{:noreply, %{state | ports: ports}}
|
||||
end
|
||||
|
||||
def handle_info(%NxPacketIn2{userdata: <<"ARP_missing", arp_req::bytes>>} = pin, state) do
|
||||
:ok = debug("ARP Entry missing: #{inspect(pin)}")
|
||||
nexthop_address = pin.metadata[:reg0]
|
||||
|
||||
send_packet_out(
|
||||
packet_in: pin,
|
||||
metadata: Keyword.replace!(pin.metadata, :in_port, :controller),
|
||||
data: <<arp_req::bytes, nexthop_address::32>>
|
||||
)
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(info, state) do
|
||||
:ok = warn("Unhandled info received: #{inspect(info)}")
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# private functions
|
||||
|
||||
@spec handle_ports([%Openflow.Port{}]) :: [map()]
|
||||
defp handle_ports(ports) do
|
||||
ifaces = Config.interfaces()
|
||||
handle_ports([], ports, ifaces)
|
||||
end
|
||||
|
||||
defp handle_ports(acc, [], _ifaces), do: acc
|
||||
|
||||
defp handle_ports(acc0, [port|rest], ifaces) do
|
||||
case Map.get(ifaces, port.name) do
|
||||
iface0 when is_map(iface0) ->
|
||||
iface = Map.merge(iface0, %{number: port.number})
|
||||
handle_ports([iface|acc0], rest, ifaces)
|
||||
_ ->
|
||||
handle_ports(acc0, rest, ifaces)
|
||||
end
|
||||
end
|
||||
|
||||
@spec init_egress_groups([map()], String.t()) :: :ok
|
||||
defp init_egress_groups(ifaces, datapath_id),
|
||||
do: Enum.each(ifaces, &GroupTables.egress(&1, datapath_id))
|
||||
|
||||
@spec init_interface_lookup_flows([map()], String.t()) :: :ok
|
||||
defp init_interface_lookup_flows(ifaces, datapath_id),
|
||||
do: Enum.each(ifaces, &FlowTables.lookup_iface(&1, datapath_id))
|
||||
|
||||
@spec init_arp_handler_flows([map()], String.t()) :: :ok
|
||||
defp init_arp_handler_flows(ifaces, datapath_id),
|
||||
do: Enum.each(ifaces, &FlowTables.arp_handlers(&1, datapath_id))
|
||||
|
||||
@spec init_egress_flows([map()], String.t()) :: :ok
|
||||
defp init_egress_flows(ifaces, datapath_id),
|
||||
do: Enum.each(ifaces, &FlowTables.egress(&1, datapath_id))
|
||||
|
||||
@spec init_connected_routes([map()], String.t()) :: :ok
|
||||
defp init_connected_routes(ifaces, datapath_id),
|
||||
do: Enum.each(ifaces, &FlowTables.connected_route(&1, datapath_id))
|
||||
|
||||
@spec init_static_routes([map()], String.t()) :: :ok
|
||||
defp init_static_routes(routes, datapath_id),
|
||||
do: Enum.each(routes, &FlowTables.static_route(&1, datapath_id))
|
||||
|
||||
@spec init_datapath(String.t()) :: :ok
|
||||
defp init_datapath(datapath_id) do
|
||||
:ok = set_config(datapath_id)
|
||||
:ok = set_packet_in_format(datapath_id)
|
||||
end
|
||||
|
||||
@spec set_config(String.t()) :: :ok
|
||||
defp set_config(datapath_id) do
|
||||
set_config = SetConfig.new(miss_send_len: :no_buffer)
|
||||
:ok = send_message(set_config, datapath_id)
|
||||
end
|
||||
|
||||
@spec set_packet_in_format(String.t()) :: :ok
|
||||
defp set_packet_in_format(datapath_id) do
|
||||
pin_format = NxSetPacketInFormat.new(:nxt_packet_in2)
|
||||
:ok = send_message(pin_format, datapath_id)
|
||||
end
|
||||
end
|
||||
240
examples/simple_router/lib/simple_router/openflow/flow_tables.ex
Normal file
240
examples/simple_router/lib/simple_router/openflow/flow_tables.ex
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
defmodule SimpleRouter.Openflow.FlowTables do
|
||||
@moduledoc """
|
||||
Flow Table Definitions
|
||||
"""
|
||||
|
||||
use Tres.Controller
|
||||
|
||||
alias Tres.IPv4Address
|
||||
alias Tres.MacAddress
|
||||
|
||||
@classifier_table_id 0
|
||||
@arp_handler_table_id 1
|
||||
@routing_table_id 2
|
||||
@interface_lookup_table_id 3
|
||||
@arp_lookup_table_id 4
|
||||
@egress_table_id 5
|
||||
|
||||
def classifier(datapath_id) do
|
||||
# ARP
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
table_id: @classifier_table_id,
|
||||
priority: 1,
|
||||
match: Match.new(eth_type: 0x0806),
|
||||
instructions: [GotoTable.new(@arp_handler_table_id)]
|
||||
)
|
||||
|
||||
# IPv4
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
table_id: @classifier_table_id,
|
||||
priority: 1,
|
||||
match: Match.new(eth_type: 0x0800),
|
||||
instructions: [GotoTable.new(@routing_table_id)]
|
||||
)
|
||||
end
|
||||
|
||||
def connected_route(iface, datapath_id) do
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
table_id: @routing_table_id,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
priority: 100 + iface.prefix_length,
|
||||
match: Match.new(
|
||||
eth_type: 0x0800,
|
||||
ipv4_dst: {iface.network_address, iface.subnet_mask}
|
||||
),
|
||||
instructions: [
|
||||
ApplyActions.new(NxRegMove.new(src_field: :ipv4_dst, dst_field: :reg0)),
|
||||
GotoTable.new(@interface_lookup_table_id)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def static_route(route, datapath_id) do
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
table_id: @routing_table_id,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
priority: route.prefix_length,
|
||||
match: Match.new(
|
||||
eth_type: 0x0800,
|
||||
ipv4_dst: {route.dst, route.mask}
|
||||
),
|
||||
instructions: [
|
||||
ApplyActions.new(SetField.new(reg0: IPv4Address.to_int(route.nexthop))),
|
||||
GotoTable.new(@interface_lookup_table_id)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def arp_handlers(iface, datapath_id) do
|
||||
:ok = arp_request_handler(iface, datapath_id)
|
||||
:ok = arp_reply_handler(iface, datapath_id)
|
||||
:ok = arp_entry_is_missing(iface, datapath_id)
|
||||
end
|
||||
|
||||
def arp_entry_is_missing(iface, datapath_id) do
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
priority: 0,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
table_id: @arp_lookup_table_id,
|
||||
match: Match.new(reg1: iface.number),
|
||||
instructions: [
|
||||
ApplyActions.new(
|
||||
NxController2.new(userdata: arp_packet(iface), pause: true)
|
||||
)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def lookup_iface(iface, datapath_id) do
|
||||
match =
|
||||
Match.new(
|
||||
reg0: {
|
||||
IPv4Address.to_int(iface.network_address),
|
||||
IPv4Address.to_int(iface.subnet_mask)
|
||||
}
|
||||
)
|
||||
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
table_id: @interface_lookup_table_id,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
priority: iface.prefix_length,
|
||||
match: match,
|
||||
instructions: [
|
||||
ApplyActions.new([
|
||||
SetField.new(reg1: iface.number),
|
||||
NxResubmitTable.new(@arp_lookup_table_id),
|
||||
NxResubmitTable.new(@egress_table_id)
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def egress(iface, datapath_id) do
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
table_id: @egress_table_id,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
priority: 1,
|
||||
match: Match.new(reg1: iface.number),
|
||||
instructions: ApplyActions.new(Group.new(iface.number))
|
||||
)
|
||||
end
|
||||
|
||||
# private functions
|
||||
|
||||
defp arp_request_handler(iface, datapath_id) do
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
table_id: @arp_handler_table_id,
|
||||
priority: 0,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
match: Match.new(
|
||||
in_port: iface.number,
|
||||
eth_type: 0x0806,
|
||||
# Request
|
||||
arp_op: 0x1,
|
||||
arp_tpa: iface.ip_address
|
||||
),
|
||||
instructions: ApplyActions.new(arp_respond_and_learn_actions(iface.mac_address))
|
||||
)
|
||||
end
|
||||
|
||||
defp arp_reply_handler(iface, datapath_id) do
|
||||
send_flow_mod_add(
|
||||
datapath_id,
|
||||
table_id: @arp_handler_table_id,
|
||||
priority: 0,
|
||||
bundle_id: 1,
|
||||
bundle_flags: [:atomic, :ordered],
|
||||
match: Match.new(
|
||||
in_port: iface.number,
|
||||
eth_type: 0x0806,
|
||||
# Reply
|
||||
arp_op: 0x2,
|
||||
arp_tpa: iface.ip_address
|
||||
),
|
||||
instructions: ApplyActions.new(learn_arp_reply_actions())
|
||||
)
|
||||
end
|
||||
|
||||
defp arp_respond_and_learn_actions(mac) do
|
||||
[
|
||||
NxLearn.new(
|
||||
priority: 1,
|
||||
table_id: @arp_lookup_table_id,
|
||||
idle_timeout: 300,
|
||||
flow_specs: [
|
||||
NxFlowSpecMatch.new(src: :arp_spa, dst: :reg0),
|
||||
NxFlowSpecLoad.new(src: :eth_src, dst: :eth_dst)
|
||||
]
|
||||
),
|
||||
NxStackPush.new(field: :arp_tpa),
|
||||
NxStackPush.new(field: :arp_sha),
|
||||
NxStackPush.new(field: :arp_spa),
|
||||
NxStackPush.new(field: :eth_src),
|
||||
NxStackPop.new(field: :eth_dst),
|
||||
NxStackPop.new(field: :arp_tpa),
|
||||
NxStackPop.new(field: :arp_tha),
|
||||
NxStackPop.new(field: :arp_spa),
|
||||
SetField.new(arp_op: 0x2),
|
||||
SetField.new(arp_sha: mac),
|
||||
SetField.new(eth_src: mac),
|
||||
Output.new(:in_port)
|
||||
]
|
||||
end
|
||||
|
||||
defp learn_arp_reply_actions do
|
||||
[
|
||||
NxLearn.new(
|
||||
priority: 1,
|
||||
table_id: @arp_lookup_table_id,
|
||||
idle_timeout: 300,
|
||||
flow_specs: [
|
||||
NxFlowSpecMatch.new(src: :arp_spa, dst: :reg0),
|
||||
NxFlowSpecLoad.new(src: :eth_src, dst: :eth_dst)
|
||||
]
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
@arphrd_ether 1
|
||||
@eth_p_ip 0x0800
|
||||
|
||||
defp arp_packet(iface) do
|
||||
ether_header = <<
|
||||
0xffffffffffff::48, # destination ethernet address
|
||||
(MacAddress.to_i(iface.mac_address))::48, # source ethernet address
|
||||
0x0806::16 # ethernet type
|
||||
>>
|
||||
|
||||
# Target Protocol Address will append in NX_PACKET_IN2
|
||||
arp_header = <<
|
||||
@arphrd_ether::16, # hardware type
|
||||
@eth_p_ip::16, # protocol type
|
||||
6::8, # hardware address length
|
||||
4::8, # protocol address length
|
||||
1::16, # ARPOP_REQUEST
|
||||
(MacAddress.to_i(iface.mac_address))::48, # Source Hardware Address
|
||||
(IPv4Address.to_int(iface.ip_address))::32, # Source Protocol Address
|
||||
0x000000000000::48 # Target Hardware Address
|
||||
>>
|
||||
|
||||
<<"ARP_missing", ether_header::bytes, arp_header::bytes>>
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
defmodule SimpleRouter.Openflow.GroupTables do
|
||||
@moduledoc """
|
||||
Group Table Definitions
|
||||
"""
|
||||
|
||||
use Tres.Controller
|
||||
|
||||
def egress(iface, datapath_id) do
|
||||
bucket =
|
||||
Openflow.Bucket.new(
|
||||
actions: [
|
||||
SetField.new(eth_src: iface.mac_address),
|
||||
Output.new(iface.number)
|
||||
]
|
||||
)
|
||||
|
||||
send_group_mod_add(
|
||||
datapath_id,
|
||||
type: :indirect,
|
||||
group_id: iface.number,
|
||||
buckets: [bucket]
|
||||
)
|
||||
end
|
||||
end
|
||||
17
examples/simple_router/lib/simple_router/supervisor.ex
Normal file
17
examples/simple_router/lib/simple_router/supervisor.ex
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
defmodule SimpleRouter.Supervisor do
|
||||
@moduledoc """
|
||||
Top Level supervisor
|
||||
"""
|
||||
|
||||
use Supervisor
|
||||
|
||||
@children []
|
||||
|
||||
def start_link do
|
||||
Supervisor.start_link(__MODULE__, [], name: __MODULE__)
|
||||
end
|
||||
|
||||
def init(_args) do
|
||||
Supervisor.init(@children, strategy: :one_for_one)
|
||||
end
|
||||
end
|
||||
27
examples/simple_router/mix.exs
Normal file
27
examples/simple_router/mix.exs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
defmodule SimpleRouter.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :simple_router,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.7",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger, :tres, :pkt],
|
||||
mod: {SimpleRouter.Application, []}
|
||||
]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:tres, path: "../../../tres"},
|
||||
{:pkt, github: "msantos/pkt"}
|
||||
]
|
||||
end
|
||||
end
|
||||
7
examples/simple_router/mix.lock
Normal file
7
examples/simple_router/mix.lock
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
%{
|
||||
"eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]},
|
||||
"jsone": {:git, "https://github.com/sile/jsone.git", "b23d312a5ed051ea7ad0989a9f2cb1a9c3f9a502", [tag: "1.4.6"]},
|
||||
"pkt": {:git, "https://github.com/msantos/pkt.git", "ff0e9a7d28cdae941bce935602cd252cad1ea296", []},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||
"uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]},
|
||||
}
|
||||
8
examples/simple_router/test/simple_router_test.exs
Normal file
8
examples/simple_router/test/simple_router_test.exs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
defmodule SimpleRouterTest do
|
||||
use ExUnit.Case
|
||||
doctest SimpleRouter
|
||||
|
||||
test "greets the world" do
|
||||
assert SimpleRouter.hello() == :world
|
||||
end
|
||||
end
|
||||
1
examples/simple_router/test/test_helper.exs
Normal file
1
examples/simple_router/test/test_helper.exs
Normal file
|
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
||||
|
|
@ -15,24 +15,27 @@ defmodule Openflow do
|
|||
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
|
||||
try 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)
|
||||
result =
|
||||
type
|
||||
|> Openflow.Enums.to_atom(:openflow_codec)
|
||||
|> do_read(body_bin)
|
||||
|
||||
case result do
|
||||
{:ok, struct} -> {:ok, %{struct | version: ver, xid: xid}, rest}
|
||||
{:error, reason} -> {:error, reason}
|
||||
case result do
|
||||
{:ok, struct} -> {:ok, %{struct | version: ver, xid: xid}, rest}
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
catch
|
||||
_c, reason ->
|
||||
{:error, {:malformed_packet, {reason, __STACKTRACE__}}}
|
||||
end
|
||||
end
|
||||
|
||||
def to_binary(messages) when is_list(messages) do
|
||||
binaries = for message <- messages, do: to_binary(message)
|
||||
Enum.join(binaries, "")
|
||||
end
|
||||
def to_binary(messages) when is_list(messages),
|
||||
do: Enum.reduce(messages, "", &(&2 <> to_binary(&1)))
|
||||
|
||||
def to_binary(%{__struct__: encoder, version: version, xid: xid} = msg) do
|
||||
case encoder.to_binary(msg) do
|
||||
|
|
|
|||
|
|
@ -1,4 +1,63 @@
|
|||
defmodule Openflow.Action do
|
||||
@moduledoc """
|
||||
Openflow parser handler
|
||||
"""
|
||||
|
||||
@type type ::
|
||||
Openflow.Action.Output.t()
|
||||
| Openflow.Action.CopyTtlOut.t()
|
||||
| Openflow.Action.CopyTtlIn.t()
|
||||
| Openflow.Action.SetMplsTtl.t()
|
||||
| Openflow.Action.DecMplsTtl.t()
|
||||
| Openflow.Action.PushVlan.t()
|
||||
| Openflow.Action.PopVlan.t()
|
||||
| Openflow.Action.PushMpls.t()
|
||||
| Openflow.Action.PopMpls.t()
|
||||
| Openflow.Action.SetQueue.t()
|
||||
| Openflow.Action.Group.t()
|
||||
| Openflow.Action.SetNwTtl.t()
|
||||
| Openflow.Action.DecNwTtl.t()
|
||||
| Openflow.Action.SetField.t()
|
||||
| Openflow.Action.PushPbb.t()
|
||||
| Openflow.Action.PopPbb.t()
|
||||
| Openflow.Action.NxResubmit.t()
|
||||
| Openflow.Action.NxSetTunnel.t()
|
||||
| Openflow.Action.NxRegMove.t()
|
||||
| Openflow.Action.NxRegLoad.t()
|
||||
| Openflow.Action.NxNote.t()
|
||||
| Openflow.Action.NxSetTunnel64.t()
|
||||
| Openflow.Action.NxMultipath.t()
|
||||
| Openflow.Action.NxBundle.t()
|
||||
| Openflow.Action.NxBundleLoad.t()
|
||||
| Openflow.Action.NxResubmitTable.t()
|
||||
| Openflow.Action.NxOutputReg.t()
|
||||
| Openflow.Action.NxLearn.t()
|
||||
| Openflow.Action.NxExit.t()
|
||||
| Openflow.Action.NxDecTtl.t()
|
||||
| Openflow.Action.NxFinTimeout.t()
|
||||
| Openflow.Action.NxController.t()
|
||||
| Openflow.Action.NxDecTtlCntIds.t()
|
||||
| Openflow.Action.NxWriteMetadata.t()
|
||||
| Openflow.Action.NxPushMpls.t()
|
||||
| Openflow.Action.NxPopMpls.t()
|
||||
| Openflow.Action.NxStackPush.t()
|
||||
| Openflow.Action.NxStackPop.t()
|
||||
| Openflow.Action.NxSample.t()
|
||||
| Openflow.Action.NxOutputReg2.t()
|
||||
| Openflow.Action.NxRegLoad2.t()
|
||||
| Openflow.Action.NxConjunction.t()
|
||||
| Openflow.Action.NxConntrack.t()
|
||||
| Openflow.Action.NxNat.t()
|
||||
| Openflow.Action.NxController2.t()
|
||||
| Openflow.Action.NxSample2.t()
|
||||
| Openflow.Action.NxOutputTrunc.t()
|
||||
| Openflow.Action.NxGroup.t()
|
||||
| Openflow.Action.NxSample3.t()
|
||||
| Openflow.Action.NxClone.t()
|
||||
| Openflow.Action.NxCtClear.t()
|
||||
| Openflow.Action.NxResubmitTableCt.t()
|
||||
| Openflow.Action.NxLearn2.t()
|
||||
|
||||
def read(action_bin) do
|
||||
do_read([], action_bin)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,37 @@
|
|||
defmodule Openflow.Action.CopyTtlIn do
|
||||
@moduledoc """
|
||||
Copy the TTL from next-to-outermost to outermost header with TTL.\\
|
||||
Copy can be IP-to-IP, MPLS-to-MPLS, or IP-to-MPLS.
|
||||
"""
|
||||
|
||||
defstruct([])
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %CopyTtlIn{}
|
||||
|
||||
@spec ofpat() :: 12
|
||||
def ofpat, do: 12
|
||||
|
||||
@doc """
|
||||
Create a copy_ttl_in action struct
|
||||
|
||||
## Example:
|
||||
```elixir
|
||||
iex> %CopyTtlIn{} = Openflow.Action.CopyTtlIn.new()
|
||||
```
|
||||
"""
|
||||
@spec new() :: CopyTtlIn.t()
|
||||
def new do
|
||||
%CopyTtlIn{}
|
||||
end
|
||||
|
||||
@spec to_binary(CopyTtlIn.t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%CopyTtlIn{}) do
|
||||
<<12::16, 8::16, 0::size(4)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: CopyTtlIn.t()
|
||||
def read(<<12::16, 8::16, _::size(4)-unit(8)>>) do
|
||||
%CopyTtlIn{}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,38 @@
|
|||
defmodule Openflow.Action.CopyTtlOut do
|
||||
@moduledoc """
|
||||
Copy the TTL from outermost to next-to-outermost header with TTL.\\
|
||||
Copy can be IP-to-IP, MPLS-to-MPLS, or MPLS-to-IP.
|
||||
"""
|
||||
|
||||
defstruct([])
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %CopyTtlOut{}
|
||||
|
||||
@spec ofpat() :: 11
|
||||
def ofpat, do: 11
|
||||
|
||||
@doc """
|
||||
Create a copy_ttl_out action structure
|
||||
|
||||
## Example
|
||||
|
||||
```elixir
|
||||
iex> %CopyTtlOut{} = Openflow.Action.CopyTtlOut.new()
|
||||
```
|
||||
"""
|
||||
@spec new() :: CopyTtlOut.t()
|
||||
def new do
|
||||
%CopyTtlOut{}
|
||||
end
|
||||
|
||||
@spec to_binary(CopyTtlIn.t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%CopyTtlOut{}) do
|
||||
<<11::16, 8::16, 0::size(4)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: CopyTtlOut.t()
|
||||
def read(<<11::16, 8::16, _::size(4)-unit(8)>>) do
|
||||
%CopyTtlOut{}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,39 @@
|
|||
defmodule Openflow.Action.DecMplsTtl do
|
||||
@moduledoc """
|
||||
Decrement MPLS TTL action
|
||||
"""
|
||||
|
||||
defstruct([])
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %DecMplsTtl{}
|
||||
|
||||
@spec ofpat() :: 16
|
||||
def ofpat, do: 16
|
||||
|
||||
@doc """
|
||||
Create a new dec_mpls_ttl action struct.
|
||||
|
||||
Note: Need to be specified the one of `ETH_P_MPLS_*` on `eth_type` match field when using this action.
|
||||
|
||||
## Example:
|
||||
|
||||
````elixir
|
||||
iex> %DecMplsTtl{} = DecMplsTtl.new()
|
||||
````
|
||||
"""
|
||||
@spec new() :: t()
|
||||
def new do
|
||||
%DecMplsTtl{}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%DecMplsTtl{}) do
|
||||
<<16::16, 8::16, 0::size(4)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<16::16, 8::16, _::size(4)-unit(8)>>) do
|
||||
%DecMplsTtl{}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,35 @@
|
|||
defmodule Openflow.Action.DecNwTtl do
|
||||
@moduledoc """
|
||||
Decrement IP TTL
|
||||
"""
|
||||
|
||||
defstruct([])
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %DecNwTtl{}
|
||||
|
||||
@spec ofpat() :: 24
|
||||
def ofpat, do: 24
|
||||
|
||||
@doc """
|
||||
Create a new dec_nw_ttl action struct
|
||||
|
||||
```elixir
|
||||
iex> %DecNwTtl{} = DecNwTtl.new()
|
||||
```
|
||||
"""
|
||||
@spec new() :: t()
|
||||
def new do
|
||||
%DecNwTtl{}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%DecNwTtl{}) do
|
||||
<<24::16, 8::16, 0::size(4)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<24::16, 8::16, _::size(4)-unit(8)>>) do
|
||||
%DecNwTtl{}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ defmodule Openflow.Action.Experimenter do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %Experimenter{exp_id: 0..0xFFFFFFFF, data: binary()}
|
||||
|
||||
@experimter_size 8
|
||||
|
||||
def ofpat, do: 0xFFFF
|
||||
|
|
@ -16,6 +18,23 @@ defmodule Openflow.Action.Experimenter do
|
|||
<<0xFFFF::16, length::16, exp_id::32, data::bytes>>
|
||||
end
|
||||
|
||||
@spec pack_exp_header(binary()) :: binary()
|
||||
def pack_exp_header(exp_body) do
|
||||
pad_length =
|
||||
exp_body
|
||||
|> Kernel.byte_size()
|
||||
|> Kernel.+(4)
|
||||
|> Openflow.Utils.padding(8)
|
||||
|
||||
length =
|
||||
exp_body
|
||||
|> byte_size()
|
||||
|> Kernel.+(4)
|
||||
|> Kernel.+(pad_length)
|
||||
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(pad_length)-unit(8)>>
|
||||
end
|
||||
|
||||
def read(<<0xFFFF::16, _length::16, exp_id::32, exp_type::16, data::bytes>>) do
|
||||
case Openflow.Utils.get_enum(exp_id, :action_vendor) do
|
||||
vendor_id when is_integer(vendor_id) ->
|
||||
|
|
|
|||
|
|
@ -1,18 +1,38 @@
|
|||
defmodule Openflow.Action.Group do
|
||||
@moduledoc """
|
||||
Apply Group
|
||||
"""
|
||||
|
||||
defstruct(id: 0)
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %Group{id: 0..0xFFFFFFFF}
|
||||
|
||||
@spec ofpat() :: 22
|
||||
def ofpat, do: 22
|
||||
|
||||
@doc """
|
||||
Create a new group action struct
|
||||
|
||||
## Options
|
||||
- id: Group identifier
|
||||
|
||||
```elixir
|
||||
iex> %Group{id: 10} = Group.new(10)
|
||||
```
|
||||
"""
|
||||
@spec new(0..0xFFFFFFFF) :: t()
|
||||
def new(id) do
|
||||
%Group{id: id}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%Group{id: id}) do
|
||||
<<22::16, 8::16, id::32>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<22::16, 8::16, id::32>>) do
|
||||
%Group{id: id}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,8 +12,35 @@ defmodule Openflow.Action.NxBundle do
|
|||
@nxast 12
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
@type algorithm :: :active_backup | :highest_random_weight
|
||||
@type hash_field ::
|
||||
:eth_src
|
||||
| :symmetric_l4
|
||||
| :symmetric_l3l4
|
||||
| :symmetric_l3l4_udp
|
||||
| :nw_src
|
||||
| :nw_dst
|
||||
|
||||
@type t :: %NxBundle{
|
||||
algorithm: algorithm(),
|
||||
hash_field: hash_field(),
|
||||
basis: non_neg_integer(),
|
||||
slave_type: :nx_in_port,
|
||||
n_slaves: non_neg_integer(),
|
||||
slaves: [pos_integer()]
|
||||
}
|
||||
|
||||
@spec new(
|
||||
algorithm: algorithm(),
|
||||
hash_field: hash_field(),
|
||||
basis: non_neg_integer(),
|
||||
slave_type: :nx_in_port,
|
||||
n_slaves: non_neg_integer(),
|
||||
slaves: [pos_integer()]
|
||||
) :: t()
|
||||
def new(options \\ []) do
|
||||
slaves = options[:slaves] || []
|
||||
|
||||
%NxBundle{
|
||||
|
|
@ -25,28 +52,25 @@ defmodule Openflow.Action.NxBundle do
|
|||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxBundle{
|
||||
algorithm: alg,
|
||||
hash_field: hash_field,
|
||||
basis: basis,
|
||||
slave_type: slave_type,
|
||||
n_slaves: n_slaves,
|
||||
slaves: slaves
|
||||
}) do
|
||||
hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields)
|
||||
alg_int = Openflow.Enums.to_int(alg, :nx_bd_algorithm)
|
||||
slave_type_bin = Openflow.Match.codec_header(slave_type)
|
||||
slaves_bin = codec_slaves(slaves)
|
||||
def to_binary(%NxBundle{} = bundle) do
|
||||
bundle_hash_field_int = Openflow.Enums.to_int(bundle.hash_field, :nx_hash_fields)
|
||||
bundle_alg_int = Openflow.Enums.to_int(bundle.algorithm, :nx_bd_algorithm)
|
||||
bundle_slave_type_bin = Openflow.Match.codec_header(bundle.slave_type)
|
||||
bundle_slaves_bin = codec_slaves(bundle.slaves)
|
||||
|
||||
body =
|
||||
<<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)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
bundle_alg_int::16,
|
||||
bundle_hash_field_int::16,
|
||||
bundle.basis::16,
|
||||
bundle_slave_type_bin::4-bytes,
|
||||
bundle.n_slaves::16,
|
||||
0::size(2)-unit(8),
|
||||
0::size(4)-unit(8),
|
||||
0::size(4)-unit(8),
|
||||
bundle_slaves_bin::bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -17,9 +17,42 @@ defmodule Openflow.Action.NxBundleLoad do
|
|||
@nxast 13
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
dst_field = options[:dst_field]
|
||||
@type algorithm :: :active_backup | :highest_random_weight
|
||||
@type hash_field ::
|
||||
:eth_src
|
||||
| :symmetric_l4
|
||||
| :symmetric_l3l4
|
||||
| :symmetric_l3l4_udp
|
||||
| :nw_src
|
||||
| :nw_dst
|
||||
|
||||
@type t :: %NxBundleLoad{
|
||||
algorithm: algorithm(),
|
||||
hash_field: hash_field(),
|
||||
basis: non_neg_integer(),
|
||||
slave_type: :nx_in_port,
|
||||
n_slaves: non_neg_integer(),
|
||||
slaves: [pos_integer()],
|
||||
offset: non_neg_integer(),
|
||||
n_bits: pos_integer(),
|
||||
dst_field: atom()
|
||||
}
|
||||
|
||||
@spec new(
|
||||
algorithm: algorithm(),
|
||||
hash_field: hash_field(),
|
||||
basis: non_neg_integer(),
|
||||
slave_type: :nx_in_port,
|
||||
n_slaves: non_neg_integer(),
|
||||
slaves: [pos_integer()],
|
||||
offset: non_neg_integer(),
|
||||
n_bits: pos_integer(),
|
||||
dst_field: atom()
|
||||
) :: t()
|
||||
def new(options \\ []) do
|
||||
dst_field = options[:dst_field] || raise "dst_field must be specified"
|
||||
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
|
||||
slaves = options[:slaves] || []
|
||||
|
||||
|
|
@ -31,37 +64,31 @@ defmodule Openflow.Action.NxBundleLoad do
|
|||
slaves: slaves,
|
||||
offset: options[:offset] || 0,
|
||||
n_bits: options[:n_bits] || default_n_bits,
|
||||
dst_field: options[:dst_field]
|
||||
dst_field: dst_field
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxBundleLoad{
|
||||
algorithm: alg,
|
||||
hash_field: hash_field,
|
||||
basis: basis,
|
||||
slave_type: slave_type,
|
||||
n_slaves: n_slaves,
|
||||
slaves: slaves,
|
||||
offset: ofs,
|
||||
n_bits: n_bits,
|
||||
dst_field: dst_field
|
||||
}) do
|
||||
hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields)
|
||||
alg_int = Openflow.Enums.to_int(alg, :nx_bd_algorithm)
|
||||
slave_type_bin = Openflow.Match.codec_header(slave_type)
|
||||
slaves_bin = codec_slaves(slaves)
|
||||
ofs_nbits = ofs <<< 6 ||| n_bits - 1
|
||||
dst_field_bin = Openflow.Match.codec_header(dst_field)
|
||||
def to_binary(%NxBundleLoad{} = bundle_load) do
|
||||
hash_field_int = Openflow.Enums.to_int(bundle_load.hash_field, :nx_hash_fields)
|
||||
alg_int = Openflow.Enums.to_int(bundle_load.algorithm, :nx_bd_algorithm)
|
||||
slave_type_bin = Openflow.Match.codec_header(bundle_load.slave_type)
|
||||
slaves_bin = codec_slaves(bundle_load.slaves)
|
||||
ofs_nbits = bundle_load.offset <<< 6 ||| bundle_load.n_bits - 1
|
||||
dst_field_bin = Openflow.Match.codec_header(bundle_load.dst_field)
|
||||
|
||||
body =
|
||||
<<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)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
alg_int::16,
|
||||
hash_field_int::16,
|
||||
bundle_load.basis::16,
|
||||
slave_type_bin::4-bytes,
|
||||
bundle_load.n_slaves::16,
|
||||
ofs_nbits::16,
|
||||
dst_field_bin::4-bytes,
|
||||
0::32,
|
||||
slaves_bin::bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ defmodule Openflow.Action.NxClone do
|
|||
@nxast 42
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(actions \\ []) do
|
||||
%NxClone{actions: actions}
|
||||
|
|
@ -12,11 +13,10 @@ defmodule Openflow.Action.NxClone do
|
|||
|
||||
def to_binary(%NxClone{actions: actions}) do
|
||||
actions_bin = Openflow.Action.to_binary(actions)
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
|
||||
Experimenter.pack_exp_header(
|
||||
<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>>
|
||||
)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -9,18 +9,32 @@ defmodule Openflow.Action.NxConjunction do
|
|||
@nxast 34
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@spec new(Keyword.t()) :: %NxConjunction{}
|
||||
def new(options) do
|
||||
clause = (options[:clause] || 0) + 1
|
||||
n_clauses = options[:n_clauses] || raise "n_clauses must be specified"
|
||||
n_clauses >= 2 || raise "n_clauses must be greater than 1"
|
||||
|
||||
(1 <= clause and clause <= n_clauses and 2 <= n_clauses and n_clauses <= 64) ||
|
||||
raise "conjunction(id, k/n) must satisfy 1 <= k <= n and 2 <= n <= 64"
|
||||
|
||||
%NxConjunction{
|
||||
clause: options[:clause] || 0,
|
||||
n_clauses: options[:n_clauses] || 0,
|
||||
clause: clause,
|
||||
n_clauses: n_clauses,
|
||||
id: options[:id] || 0
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxConjunction{clause: clause, n_clauses: n_clauses, id: id}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>>
|
||||
<<0xFFFF::16, 16::16, exp_body::bytes>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
clause::8,
|
||||
n_clauses::8,
|
||||
id::32
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>>) do
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ defmodule Openflow.Action.NxConntrack do
|
|||
@nxast 35
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options \\ []) do
|
||||
%NxConntrack{
|
||||
|
|
@ -30,36 +31,21 @@ defmodule Openflow.Action.NxConntrack do
|
|||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxConntrack{
|
||||
flags: flags,
|
||||
zone_src: zone_src,
|
||||
zone_offset: zone_ofs,
|
||||
zone_n_bits: zone_n_bits,
|
||||
zone_imm: zone_imm,
|
||||
recirc_table: recirc_table,
|
||||
alg: alg,
|
||||
exec: exec
|
||||
}) do
|
||||
flags_int = Openflow.Enums.flags_to_int(flags, :nx_conntrack_flags)
|
||||
def to_binary(%NxConntrack{} = ct) do
|
||||
flags_int = Openflow.Enums.flags_to_int(ct.flags, :nx_conntrack_flags)
|
||||
ct_context_bin = ct_context_binary(ct)
|
||||
exec_bin = Openflow.Action.to_binary(ct.exec)
|
||||
|
||||
{src_bin, ofs_nbits} =
|
||||
if not is_nil(zone_src) do
|
||||
zone_src_bin = Openflow.Match.codec_header(zone_src)
|
||||
{zone_src_bin, zone_ofs <<< 6 ||| zone_n_bits - 1}
|
||||
else
|
||||
{<<0::32>>, zone_imm}
|
||||
end
|
||||
|
||||
exec_bin = Openflow.Action.to_binary(exec)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, flags_int::16, src_bin::bytes, ofs_nbits::16,
|
||||
recirc_table::8, 0::size(3)-unit(8), alg::16, exec_bin::bytes>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
flags_int::16,
|
||||
ct_context_bin::bytes,
|
||||
ct.recirc_table::8,
|
||||
0::size(3)-unit(8),
|
||||
ct.alg::16,
|
||||
exec_bin::bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
|
|
@ -87,4 +73,15 @@ defmodule Openflow.Action.NxConntrack do
|
|||
%{ct | zone_src: zone_src, zone_offset: ofs, zone_n_bits: n_bits + 1}
|
||||
end
|
||||
end
|
||||
|
||||
# private functions
|
||||
|
||||
defp ct_context_binary(%NxConntrack{zone_src: nil} = ct),
|
||||
do: <<0::32, ct.zone_imm::16>>
|
||||
|
||||
defp ct_context_binary(%NxConntrack{} = ct) do
|
||||
zone_src_bin = Openflow.Match.codec_header(ct.zone_src)
|
||||
ofs_nbits = ct.zone_offset <<< 6 ||| ct.zone_n_bits - 1
|
||||
<<zone_src_bin::bytes, ofs_nbits::16>>
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,8 +9,23 @@ defmodule Openflow.Action.NxController do
|
|||
@nxast 20
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
@type max_len :: :max | :no_buffer | non_neg_integer()
|
||||
@type packet_in_reason ::
|
||||
:no_match | :action | :invalid_ttl | :action_set | :group | :packet_out
|
||||
@type t :: %NxController{
|
||||
max_len: max_len(),
|
||||
id: non_neg_integer(),
|
||||
reason: packet_in_reason()
|
||||
}
|
||||
|
||||
@spec new(
|
||||
max_len: max_len(),
|
||||
id: non_neg_integer(),
|
||||
reason: packet_in_reason()
|
||||
) :: t()
|
||||
def new(options \\ []) do
|
||||
%NxController{
|
||||
max_len: options[:max_len] || :no_buffer,
|
||||
id: options[:id] || 0,
|
||||
|
|
@ -18,14 +33,18 @@ defmodule Openflow.Action.NxController do
|
|||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxController{max_len: max_len, id: controller_id, reason: reason}) do
|
||||
max_len_int = Openflow.Utils.get_enum(max_len, :controller_max_len)
|
||||
reason_int = Openflow.Enums.to_int(reason, :packet_in_reason)
|
||||
def to_binary(%NxController{} = controller) do
|
||||
max_len_int = Openflow.Utils.get_enum(controller.max_len, :controller_max_len)
|
||||
reason_int = Openflow.Enums.to_int(controller.reason, :packet_in_reason)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, max_len_int::16, controller_id::16, reason_int::8, 0::8>>
|
||||
|
||||
<<0xFFFF::16, 16::16, exp_body::bytes>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
max_len_int::16,
|
||||
controller.id::16,
|
||||
reason_int::8,
|
||||
0::8
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ defmodule Openflow.Action.NxController2 do
|
|||
id: 0,
|
||||
reason: :action,
|
||||
userdata: "",
|
||||
pause: false
|
||||
pause: false,
|
||||
meter_id: 0
|
||||
)
|
||||
|
||||
@experimenter 0x00002320
|
||||
|
|
@ -17,28 +18,57 @@ defmodule Openflow.Action.NxController2 do
|
|||
@prop_reason 2
|
||||
@prop_userdata 3
|
||||
@prop_pause 4
|
||||
@prop_meter_id 5
|
||||
|
||||
@nx_ctlr_no_meter 0
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
@type max_len :: pos_integer() | :max | :no_buffer
|
||||
@type packet_in_reason ::
|
||||
:no_match | :action | :invalid_ttl | :action_set | :group | :packet_out
|
||||
@type meter_id :: pos_integer() | :max | :slowpath | :controller | :all
|
||||
|
||||
@type t :: %NxController2{
|
||||
max_len: max_len(),
|
||||
id: non_neg_integer(),
|
||||
reason: packet_in_reason(),
|
||||
userdata: binary(),
|
||||
pause: boolean(),
|
||||
meter_id: meter_id()
|
||||
}
|
||||
|
||||
@spec new(
|
||||
max_len: max_len(),
|
||||
id: non_neg_integer(),
|
||||
reason: packet_in_reason(),
|
||||
userdata: binary(),
|
||||
pause: boolean(),
|
||||
meter_id: meter_id()
|
||||
) :: t()
|
||||
def new(options \\ [])
|
||||
|
||||
def new(options) when is_list(options) do
|
||||
%NxController2{
|
||||
max_len: options[:max_len] || :no_buffer,
|
||||
id: options[:id] || 0,
|
||||
reason: options[:reason] || :action,
|
||||
userdata: options[:userdata],
|
||||
pause: options[:pause] || false
|
||||
pause: options[:pause] || false,
|
||||
meter_id: options[:meter_id] || @nx_ctlr_no_meter
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxController2{} = ctl) do
|
||||
ext_header = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>
|
||||
prop_keys = get_prop_key(ctl)
|
||||
props_bin = encode_prop("", prop_keys, ctl)
|
||||
exp_body = <<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>>
|
||||
props_bin = encode_props(ctl)
|
||||
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
0::size(6)-unit(8),
|
||||
props_bin::bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), body::bytes>>) do
|
||||
|
|
@ -48,93 +78,144 @@ defmodule Openflow.Action.NxController2 do
|
|||
|
||||
# private functions
|
||||
|
||||
defp get_prop_key(ctl) do
|
||||
defp encode_props(ctl) do
|
||||
ctl
|
||||
|> Map.from_struct()
|
||||
|> Enum.map(fn {k, v} -> if(not is_nil(v), do: k, else: nil) end)
|
||||
|> Enum.filter(fn v -> not is_nil(v) end)
|
||||
|> Enum.reduce(<<>>, &encode_prop/2)
|
||||
end
|
||||
|
||||
defp encode_prop(acc, [], _ctl), do: acc
|
||||
defp encode_prop({:max_len, value}, acc)
|
||||
when value != :no_buffer or value < 0xFFFF do
|
||||
pad_length = 2
|
||||
prop_length = @prop_header_size + 2 + pad_length
|
||||
max_len_int = Openflow.Utils.get_enum(value, :controller_max_len)
|
||||
|
||||
defp encode_prop(acc, [prop | rest], ctl) do
|
||||
value = Map.get(ctl, prop)
|
||||
<<
|
||||
acc::bytes,
|
||||
@prop_max_len::16,
|
||||
prop_length::16,
|
||||
max_len_int::16,
|
||||
0::size(pad_length)-unit(8)
|
||||
>>
|
||||
end
|
||||
|
||||
prop_bin =
|
||||
cond do
|
||||
prop == :max_len and (value != :no_buffer or value < 0xFFFF) ->
|
||||
padding_length = 2
|
||||
prop_length = @prop_header_size + 2 + padding_length
|
||||
max_len_int = Openflow.Utils.get_enum(value, :controller_max_len)
|
||||
<<@prop_max_len::16, prop_length::16, max_len_int::16, 0::size(padding_length)-unit(8)>>
|
||||
defp encode_prop({:id, value}, acc) do
|
||||
pad_length = 2
|
||||
prop_length = @prop_header_size + 2 + pad_length
|
||||
|
||||
prop == :id ->
|
||||
padding_length = 2
|
||||
prop_length = @prop_header_size + 2 + padding_length
|
||||
<<@prop_ctl_id::16, prop_length::16, value::16, 0::size(padding_length)-unit(8)>>
|
||||
<<
|
||||
acc::bytes,
|
||||
@prop_ctl_id::16,
|
||||
prop_length::16,
|
||||
value::16,
|
||||
0::size(pad_length)-unit(8)
|
||||
>>
|
||||
end
|
||||
|
||||
prop == :reason and value != :action ->
|
||||
padding_length = 3
|
||||
prop_length = @prop_header_size + 1 + padding_length
|
||||
reason_int = Openflow.Utils.get_enum(value, :packet_in_reason)
|
||||
<<@prop_reason::16, prop_length::16, reason_int::8, 0::size(padding_length)-unit(8)>>
|
||||
defp encode_prop({:reason, reason}, acc)
|
||||
when reason != :action do
|
||||
padding_length = 3
|
||||
prop_length = @prop_header_size + 1 + padding_length
|
||||
reason_int = Openflow.Utils.get_enum(reason, :packet_in_reason)
|
||||
|
||||
prop == :userdata and byte_size(value) > 0 ->
|
||||
prop_length = @prop_header_size + byte_size(value)
|
||||
padding_length = Openflow.Utils.padding(prop_length, 8)
|
||||
<<@prop_userdata::16, prop_length::16, value::bytes, 0::size(padding_length)-unit(8)>>
|
||||
<<
|
||||
acc::bytes,
|
||||
@prop_reason::16,
|
||||
prop_length::16,
|
||||
reason_int::8,
|
||||
0::size(padding_length)-unit(8)
|
||||
>>
|
||||
end
|
||||
|
||||
prop == :pause and value == true ->
|
||||
padding_length = 4
|
||||
prop_length = @prop_header_size + padding_length
|
||||
<<@prop_pause::16, prop_length::16, 0::size(padding_length)-unit(8)>>
|
||||
defp encode_prop({:userdata, value}, acc)
|
||||
when byte_size(value) > 0 do
|
||||
prop_length = @prop_header_size + byte_size(value)
|
||||
padding_length = Openflow.Utils.padding(prop_length, 8)
|
||||
|
||||
true ->
|
||||
""
|
||||
end
|
||||
<<
|
||||
acc::bytes,
|
||||
@prop_userdata::16,
|
||||
prop_length::16,
|
||||
value::bytes,
|
||||
0::size(padding_length)-unit(8)
|
||||
>>
|
||||
end
|
||||
|
||||
encode_prop(<<acc::bytes, prop_bin::bytes>>, rest, ctl)
|
||||
defp encode_prop({:pause, true}, acc) do
|
||||
padding_length = 4
|
||||
prop_length = @prop_header_size + padding_length
|
||||
|
||||
<<
|
||||
acc::bytes,
|
||||
@prop_pause::16,
|
||||
prop_length::16,
|
||||
0::size(padding_length)-unit(8)
|
||||
>>
|
||||
end
|
||||
|
||||
defp encode_prop({:meter_id, value}, acc)
|
||||
when value != @nx_ctlr_no_meter do
|
||||
prop_length = @prop_header_size + 4
|
||||
|
||||
<<
|
||||
acc::bytes,
|
||||
@prop_meter_id::16,
|
||||
prop_length::16,
|
||||
value::32
|
||||
>>
|
||||
end
|
||||
|
||||
defp encode_prop(_, acc) do
|
||||
acc
|
||||
end
|
||||
|
||||
defp decode_prop(ctl, ""), do: ctl
|
||||
|
||||
defp decode_prop(ctl, <<prop_type_int::16, _::bytes>> = bin) do
|
||||
prop_type = Openflow.Enums.to_atom(prop_type_int, :nx_action_controller2_prop_type)
|
||||
defp decode_prop(
|
||||
ctl,
|
||||
<<
|
||||
@prop_max_len::16,
|
||||
_prop_length::16,
|
||||
max_len_int::16,
|
||||
_::size(2)-unit(8),
|
||||
rest::bytes
|
||||
>>
|
||||
) do
|
||||
max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len)
|
||||
decode_prop(%{ctl | max_len: max_len}, rest)
|
||||
end
|
||||
|
||||
case prop_type do
|
||||
:max_len ->
|
||||
<<@prop_max_len::16, _prop_length::16, max_len_int::16, _::size(2)-unit(8), rest::bytes>> =
|
||||
bin
|
||||
defp decode_prop(
|
||||
ctl,
|
||||
<<@prop_reason::16, _prop_length::16, reason_int::8, _::size(3)-unit(8), rest::bytes>>
|
||||
) do
|
||||
reason = Openflow.Utils.get_enum(reason_int, :packet_in_reason)
|
||||
decode_prop(%{ctl | reason: reason}, rest)
|
||||
end
|
||||
|
||||
max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len)
|
||||
decode_prop(struct(ctl, %{max_len: max_len}), rest)
|
||||
defp decode_prop(
|
||||
ctl,
|
||||
<<@prop_ctl_id::16, _prop_length::16, controller_id::16, _::size(2)-unit(8),
|
||||
rest::bytes>>
|
||||
) do
|
||||
decode_prop(%{ctl | id: controller_id}, rest)
|
||||
end
|
||||
|
||||
:controller_id ->
|
||||
<<@prop_ctl_id::16, _prop_length::16, controller_id::16, _::size(2)-unit(8), rest::bytes>> =
|
||||
bin
|
||||
defp decode_prop(
|
||||
ctl,
|
||||
<<@prop_userdata::16, prop_length::16, remains::bytes>>
|
||||
) do
|
||||
userdata_len = prop_length - 4
|
||||
padding_length = Openflow.Utils.padding(prop_length, 8)
|
||||
<<userdata::size(userdata_len)-bytes, _::size(padding_length)-unit(8), rest::bytes>> = remains
|
||||
decode_prop(%{ctl | userdata: userdata}, rest)
|
||||
end
|
||||
|
||||
decode_prop(struct(ctl, %{controller_id: controller_id}), rest)
|
||||
defp decode_prop(ctl, <<@prop_pause::16, _::16, 0::size(4)-unit(8), rest::bytes>>) do
|
||||
decode_prop(%{ctl | pause: true}, rest)
|
||||
end
|
||||
|
||||
:reason ->
|
||||
<<@prop_reason::16, _prop_length::16, reason_int::8, _::size(3)-unit(8), rest::bytes>> =
|
||||
bin
|
||||
|
||||
reason = Openflow.Utils.get_enum(reason_int, :packet_in_reason)
|
||||
decode_prop(struct(ctl, %{reason: reason}), rest)
|
||||
|
||||
:userdata ->
|
||||
<<@prop_userdata::16, prop_length::16, remains::bytes>> = bin
|
||||
userdata_len = prop_length - 4
|
||||
padding_length = Openflow.Utils.padding(prop_length, 8)
|
||||
|
||||
<<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
|
||||
defp decode_prop(ctl, <<@prop_meter_id::16, _::16, meter_id::32, rest::bytes>>) do
|
||||
decode_prop(%{ctl | meter_id: meter_id}, rest)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,14 +5,19 @@ defmodule Openflow.Action.NxCtClear do
|
|||
@nxast 43
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new do
|
||||
%NxCtClear{}
|
||||
end
|
||||
|
||||
def to_binary(%NxCtClear{}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::16, 0::size(4)-unit(8)>>
|
||||
<<0xFFFF::16, 16::16, exp_body::bytes>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
0::16,
|
||||
0::size(4)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
defmodule Openflow.Action.NxDecMplsTtl do
|
||||
defstruct([])
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 26
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
def new do
|
||||
%NxDecMplsTtl{}
|
||||
end
|
||||
|
||||
def to_binary(%NxDecMplsTtl{}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>
|
||||
<<0xFFFF::16, 16::16, exp_body::bytes>>
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8)>>) do
|
||||
%NxDecMplsTtl{}
|
||||
end
|
||||
end
|
||||
|
|
@ -5,14 +5,18 @@ defmodule Openflow.Action.NxDecTtl do
|
|||
@nxast 18
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new do
|
||||
%NxDecTtl{}
|
||||
end
|
||||
|
||||
def to_binary(%NxDecTtl{}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>
|
||||
<<0xFFFF::16, 16::16, exp_body::bytes>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
0::size(6)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do
|
||||
|
|
|
|||
|
|
@ -5,24 +5,26 @@ defmodule Openflow.Action.NxDecTtlCntIds do
|
|||
@nxast 21
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@type t :: %NxDecTtlCntIds{ids: [non_neg_integer()]}
|
||||
|
||||
@spec new(ids :: [non_neg_integer()]) :: t()
|
||||
def new(ids) do
|
||||
%NxDecTtlCntIds{ids: ids}
|
||||
end
|
||||
|
||||
def to_binary(%NxDecTtlCntIds{ids: ids}) do
|
||||
n_controllers = length(ids)
|
||||
ids_bin = Enum.join(for(id <- ids, do: <<id::16>>), "")
|
||||
padding = Openflow.Utils.padding(n_controllers, 8)
|
||||
ids_bin = Enum.reduce(ids, <<>>, fn id, acc -> <<acc::bytes, id::16>> end)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, n_controllers::16, 0::size(4)-unit(8), ids_bin::bytes,
|
||||
0::size(padding)-unit(8)>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
n_controllers::16,
|
||||
0::size(4)-unit(8),
|
||||
ids_bin::bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, n_controllers::16, body::bitstring>>) do
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ defmodule Openflow.Action.NxExit do
|
|||
@nxast 17
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new do
|
||||
%NxExit{}
|
||||
end
|
||||
|
||||
def to_binary(%NxExit{}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::48>>
|
||||
<<0xFFFF::16, 16::16, exp_body::bytes>>
|
||||
Experimenter.pack_exp_header(<<@experimenter::32, @nxast::16, 0::48>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, 0::48>>) do
|
||||
def read(<<@experimenter::32, @nxast::16, 0::48, _::bytes>>) do
|
||||
%NxExit{}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ defmodule Openflow.Action.NxFinTimeout do
|
|||
@nxast 19
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@spec new(Keyword.t()) :: %NxFinTimeout{}
|
||||
def new(options) do
|
||||
%NxFinTimeout{
|
||||
idle_timeout: options[:idle_timeout] || 0,
|
||||
|
|
@ -16,9 +18,13 @@ defmodule Openflow.Action.NxFinTimeout do
|
|||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16>>
|
||||
<<0xFFFF::16, 16::16, exp_body::bytes, 0::size(2)-unit(8)>>
|
||||
def to_binary(%NxFinTimeout{} = fin_timeout) do
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
fin_timeout.idle_timeout::16,
|
||||
fin_timeout.hard_timeout::16
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16, _::size(2)-unit(8)>>) do
|
||||
|
|
|
|||
|
|
@ -13,12 +13,28 @@ defmodule Openflow.Action.NxFlowSpecLoad do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %NxFlowSpecLoad{
|
||||
src: atom(),
|
||||
dst: atom(),
|
||||
n_bits: non_neg_integer(),
|
||||
src_offset: non_neg_integer(),
|
||||
dst_offset: non_neg_integer()
|
||||
}
|
||||
|
||||
@spec new(
|
||||
src: atom(),
|
||||
dst: atom(),
|
||||
n_bits: non_neg_integer(),
|
||||
src_offset: non_neg_integer(),
|
||||
dst_offset: non_neg_integer()
|
||||
) :: t()
|
||||
def new(options) do
|
||||
dst = options[:dst]
|
||||
dst = options[:dst] || raise(":dst must be specified")
|
||||
src = options[:src] || raise(":src must be specified")
|
||||
n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst)
|
||||
|
||||
%NxFlowSpecLoad{
|
||||
src: options[:src],
|
||||
src: src,
|
||||
dst: dst,
|
||||
n_bits: n_bits,
|
||||
src_offset: options[:src_offset] || 0,
|
||||
|
|
|
|||
|
|
@ -13,12 +13,28 @@ defmodule Openflow.Action.NxFlowSpecMatch do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
def new(options) do
|
||||
dst = options[:dst]
|
||||
@type t :: %NxFlowSpecMatch{
|
||||
src: atom(),
|
||||
dst: atom(),
|
||||
n_bits: non_neg_integer(),
|
||||
src_offset: non_neg_integer(),
|
||||
dst_offset: non_neg_integer()
|
||||
}
|
||||
|
||||
@spec new(
|
||||
src: atom(),
|
||||
dst: atom(),
|
||||
n_bits: non_neg_integer(),
|
||||
src_offset: non_neg_integer(),
|
||||
dst_offset: non_neg_integer()
|
||||
) :: t()
|
||||
def new(options \\ []) do
|
||||
dst = options[:dst] || raise ":dst must be specified"
|
||||
src = options[:src] || raise ":src must be specified"
|
||||
n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst)
|
||||
|
||||
%NxFlowSpecMatch{
|
||||
src: options[:src],
|
||||
src: src,
|
||||
dst: dst,
|
||||
n_bits: n_bits,
|
||||
src_offset: options[:src_offset] || 0,
|
||||
|
|
@ -26,6 +42,7 @@ defmodule Openflow.Action.NxFlowSpecMatch do
|
|||
}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: binary()
|
||||
def to_binary(%NxFlowSpecMatch{} = fsm) do
|
||||
%NxFlowSpecMatch{
|
||||
dst: dst_field,
|
||||
|
|
@ -48,6 +65,7 @@ defmodule Openflow.Action.NxFlowSpecMatch do
|
|||
end
|
||||
end
|
||||
|
||||
@spec read(binary()) :: {t(), binary()}
|
||||
def read(
|
||||
<<_::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16,
|
||||
dst_bin::4-bytes, dst_ofs::16, rest::bitstring>>
|
||||
|
|
|
|||
|
|
@ -10,23 +10,42 @@ defmodule Openflow.Action.NxFlowSpecOutput do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
def new(options) do
|
||||
src = options[:src]
|
||||
@type t :: %NxFlowSpecOutput{
|
||||
n_bits: non_neg_integer(),
|
||||
src: atom(),
|
||||
src_offset: non_neg_integer()
|
||||
}
|
||||
|
||||
@spec new(src: atom(), n_bits: non_neg_integer(), src_offset: non_neg_integer()) :: t()
|
||||
def new(options \\ []) do
|
||||
src = options[:src] || raise ":src must be specified"
|
||||
n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(src)
|
||||
%NxFlowSpecOutput{n_bits: n_bits, src: src, src_offset: options[:src_offset] || 0}
|
||||
end
|
||||
|
||||
def to_binary(%NxFlowSpecOutput{n_bits: n_bits, src: src, src_offset: src_ofs}) do
|
||||
src_bin = Openflow.Match.codec_header(src)
|
||||
<<0::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16>>
|
||||
@spec to_binary(t()) :: binary()
|
||||
def to_binary(%NxFlowSpecOutput{} = flow_spec) do
|
||||
<<
|
||||
0::2,
|
||||
@learn_src_field::1,
|
||||
@learn_dst::2,
|
||||
flow_spec.n_bits::11,
|
||||
Openflow.Match.codec_header(flow_spec.src)::4-bytes,
|
||||
flow_spec.src_offset::16
|
||||
>>
|
||||
end
|
||||
|
||||
@spec read(binary()) :: {t(), binary()}
|
||||
def read(
|
||||
<<0::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16,
|
||||
rest::bitstring>>
|
||||
) do
|
||||
src = Openflow.Match.codec_header(src_bin)
|
||||
flow_spec = %NxFlowSpecOutput{n_bits: n_bits, src: src, src_offset: src_ofs}
|
||||
flow_spec = %NxFlowSpecOutput{
|
||||
n_bits: n_bits,
|
||||
src: Openflow.Match.codec_header(src_bin),
|
||||
src_offset: src_ofs
|
||||
}
|
||||
|
||||
{flow_spec, rest}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ defmodule Openflow.Action.NxLearn do
|
|||
@nxast 16
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
%NxLearn{
|
||||
|
|
@ -30,28 +31,24 @@ defmodule Openflow.Action.NxLearn do
|
|||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxLearn{
|
||||
idle_timeout: idle,
|
||||
hard_timeout: hard,
|
||||
priority: prio,
|
||||
cookie: cookie,
|
||||
flags: flags,
|
||||
table_id: table_id,
|
||||
fin_idle_timeout: fin_idle,
|
||||
fin_hard_timeout: fin_hard,
|
||||
flow_specs: flow_specs
|
||||
}) do
|
||||
flags_int = Openflow.Enums.flags_to_int(flags, :nx_learn_flag)
|
||||
flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(flow_specs)
|
||||
def to_binary(%NxLearn{} = learn) do
|
||||
learn_flags_int = Openflow.Enums.flags_to_int(learn.flags, :nx_learn_flag)
|
||||
flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(learn.flow_specs)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, idle::16, hard::16, prio::16, cookie::64, flags_int::16,
|
||||
table_id::8, 0::size(1)-unit(8), fin_idle::16, fin_hard::16, flow_specs_bin::bitstring>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
learn.idle_timeout::16,
|
||||
learn.hard_timeout::16,
|
||||
learn.priority::16,
|
||||
learn.cookie::64,
|
||||
learn_flags_int::16,
|
||||
learn.table_id::8,
|
||||
0::8,
|
||||
learn.fin_idle_timeout::16,
|
||||
learn.fin_hard_timeout::16,
|
||||
flow_specs_bin::bitstring
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, body::bitstring>>) do
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ defmodule Openflow.Action.NxLearn2 do
|
|||
@nxast 45
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
def new(options \\ []) do
|
||||
%NxLearn2{
|
||||
idle_timeout: options[:idle_timeout] || 0,
|
||||
hard_timeout: options[:hard_timeout] || 0,
|
||||
|
|
@ -36,40 +37,33 @@ defmodule Openflow.Action.NxLearn2 do
|
|||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxLearn2{
|
||||
idle_timeout: idle,
|
||||
hard_timeout: hard,
|
||||
priority: prio,
|
||||
cookie: cookie,
|
||||
flags: flags,
|
||||
table_id: table_id,
|
||||
fin_idle_timeout: fin_idle,
|
||||
fin_hard_timeout: fin_hard,
|
||||
limit: limit,
|
||||
result_dst_offset: result_dst_ofs,
|
||||
result_dst: result_dst,
|
||||
flow_specs: flow_specs
|
||||
}) do
|
||||
flags_int = Openflow.Enums.flags_to_int(flags, :nx_learn_flag)
|
||||
def to_binary(%NxLearn2{} = learn) do
|
||||
learn_flags_int = Openflow.Enums.flags_to_int(learn.flags, :nx_learn_flag)
|
||||
learn_flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(learn.flow_specs)
|
||||
|
||||
result_dst_bin =
|
||||
if :write_result in flags do
|
||||
Openflow.Match.codec_header(result_dst)
|
||||
else
|
||||
""
|
||||
end
|
||||
learn_result_dst_bin =
|
||||
if :write_result in learn.flags,
|
||||
do: Openflow.Match.codec_header(learn.result_dst),
|
||||
else: <<>>
|
||||
|
||||
flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(flow_specs)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, idle::16, hard::16, prio::16, cookie::64, flags_int::16,
|
||||
table_id::8, 0::size(1)-unit(8), fin_idle::16, fin_hard::16, limit::32,
|
||||
result_dst_ofs::16, 0::size(2)-unit(8), result_dst_bin::bytes, flow_specs_bin::bitstring>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
learn.idle_timeout::16,
|
||||
learn.hard_timeout::16,
|
||||
learn.priority::16,
|
||||
learn.cookie::64,
|
||||
learn_flags_int::16,
|
||||
learn.table_id::8,
|
||||
0::size(1)-unit(8),
|
||||
learn.fin_idle_timeout::16,
|
||||
learn.fin_hard_timeout::16,
|
||||
learn.limit::32,
|
||||
learn.result_dst_offset::16,
|
||||
0::size(2)-unit(8),
|
||||
learn_result_dst_bin::bytes,
|
||||
learn_flow_specs_bin::bitstring
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -16,54 +16,44 @@ defmodule Openflow.Action.NxMultipath do
|
|||
@nxast 10
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@spec new(Keyword.t()) :: %NxMultipath{}
|
||||
def new(options) do
|
||||
hash_field = Keyword.get(options, :hash_field, :eth_src)
|
||||
basis = Keyword.get(options, :basis, 0)
|
||||
alg = Keyword.get(options, :algorithm, :modulo_n)
|
||||
max_link = Keyword.get(options, :max_link, 0)
|
||||
arg = Keyword.get(options, :argument, 0)
|
||||
dst_field = Keyword.get(options, :dst_field)
|
||||
dst_field = options[:dst_field] || raise "dst_field must be specified"
|
||||
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
|
||||
n_bits = Keyword.get(options, :n_bits, default_n_bits)
|
||||
ofs = Keyword.get(options, :offset, 0)
|
||||
|
||||
%NxMultipath{
|
||||
hash_field: hash_field,
|
||||
basis: basis,
|
||||
algorithm: alg,
|
||||
max_link: max_link,
|
||||
offset: ofs,
|
||||
n_bits: n_bits,
|
||||
argument: arg,
|
||||
hash_field: options[:hash_field] || :eth_src,
|
||||
basis: options[:basis] || 0,
|
||||
algorithm: options[:algorithm] || :modulo_n,
|
||||
max_link: options[:max_link] || 0,
|
||||
offset: options[:offset] || 0,
|
||||
n_bits: options[:n_bits] || default_n_bits,
|
||||
argument: options[:argument] || 0,
|
||||
dst_field: dst_field
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxMultipath{
|
||||
hash_field: hash_field,
|
||||
basis: basis,
|
||||
algorithm: alg,
|
||||
max_link: max_link,
|
||||
argument: arg,
|
||||
offset: ofs,
|
||||
n_bits: n_bits,
|
||||
dst_field: dst_field
|
||||
}) do
|
||||
hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields)
|
||||
alg_int = Openflow.Enums.to_int(alg, :nx_mp_algorithm)
|
||||
dst_field_bin = Openflow.Match.codec_header(dst_field)
|
||||
ofs_nbits = ofs <<< 6 ||| n_bits - 1
|
||||
def to_binary(%NxMultipath{} = multipath) do
|
||||
hash_field_int = Openflow.Enums.to_int(multipath.hash_field, :nx_hash_fields)
|
||||
alg_int = Openflow.Enums.to_int(multipath.algorithm, :nx_mp_algorithm)
|
||||
dst_field_bin = Openflow.Match.codec_header(multipath.dst_field)
|
||||
ofs_nbits = multipath.offset <<< 6 ||| multipath.n_bits - 1
|
||||
|
||||
body =
|
||||
<<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)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
hash_field_int::16,
|
||||
multipath.basis::16,
|
||||
0::size(2)-unit(8),
|
||||
alg_int::16,
|
||||
multipath.max_link::16,
|
||||
multipath.argument::32,
|
||||
0::size(2)-unit(8),
|
||||
ofs_nbits::16,
|
||||
dst_field_bin::4-bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -13,49 +13,62 @@ defmodule Openflow.Action.NxNat do
|
|||
@nxast 36
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@type in_addr :: :inet.ip4_address()
|
||||
@type in6_addr :: :inet.ip6_address()
|
||||
@type port_number :: :inet.port_number()
|
||||
@type flag :: :src | :dst | :persistent | :protocol_hash | :protocol_random
|
||||
@type nat_range :: :ipv4_min | :ipv4_max | :ipv6_min | :ipv6_max | :proto_min | :proto_max
|
||||
@type t :: %NxNat{
|
||||
flags: [flag()],
|
||||
ipv4_min: in_addr(),
|
||||
ipv4_max: in_addr(),
|
||||
ipv6_min: in6_addr(),
|
||||
ipv6_max: in6_addr(),
|
||||
proto_min: port_number(),
|
||||
proto_max: port_number()
|
||||
}
|
||||
|
||||
@spec new(
|
||||
flags: [flag()],
|
||||
ipv4_min: in_addr(),
|
||||
ipv4_max: in_addr(),
|
||||
ipv6_min: in6_addr(),
|
||||
ipv6_max: in6_addr(),
|
||||
proto_min: port_number(),
|
||||
proto_max: port_number()
|
||||
) :: t()
|
||||
def new(options \\ []) do
|
||||
flags = Keyword.get(options, :flags, [])
|
||||
ipv4_min = Keyword.get(options, :ipv4_min)
|
||||
ipv4_max = Keyword.get(options, :ipv4_max)
|
||||
ipv6_min = Keyword.get(options, :ipv6_min)
|
||||
ipv6_max = Keyword.get(options, :ipv6_max)
|
||||
proto_min = Keyword.get(options, :proto_min)
|
||||
proto_max = Keyword.get(options, :proto_max)
|
||||
|
||||
%NxNat{
|
||||
flags: flags,
|
||||
ipv4_min: ipv4_min,
|
||||
ipv4_max: ipv4_max,
|
||||
ipv6_min: ipv6_min,
|
||||
ipv6_max: ipv6_max,
|
||||
proto_min: proto_min,
|
||||
proto_max: proto_max
|
||||
flags: options[:flags] || [],
|
||||
ipv4_min: options[:ipv4_min],
|
||||
ipv4_max: options[:ipv4_max],
|
||||
ipv6_min: options[:ipv6_min],
|
||||
ipv6_max: options[:ipv6_max],
|
||||
proto_min: options[:proto_min],
|
||||
proto_max: options[:proto_max]
|
||||
}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: binary()
|
||||
def to_binary(%NxNat{flags: flags} = nat) do
|
||||
flags_int = Openflow.Enums.flags_to_int(flags, :nx_nat_flags)
|
||||
|
||||
range_flags =
|
||||
nat
|
||||
|> get_ranges
|
||||
|> Openflow.Enums.flags_to_int(:nx_nat_range)
|
||||
|> Openflow.Enums.int_to_flags(:nx_nat_range)
|
||||
|
||||
range_flags = get_range_flags(nat)
|
||||
ranges_bin = encode_ranges("", range_flags, nat)
|
||||
range_flags_int = Openflow.Enums.flags_to_int(range_flags, :nx_nat_range)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, 0::size(2)-unit(8), flags_int::16, range_flags_int::16,
|
||||
ranges_bin::bytes>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
0::size(2)-unit(8),
|
||||
flags_int::16,
|
||||
range_flags_int::16,
|
||||
ranges_bin::bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
@spec read(binary()) :: t()
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
<<0::size(2)-unit(8), flags_int::16, range_flags_int::16, ranges_bin::bytes>> = body
|
||||
flags = Openflow.Enums.int_to_flags(flags_int, :nx_nat_flags)
|
||||
|
|
@ -65,81 +78,95 @@ defmodule Openflow.Action.NxNat do
|
|||
|
||||
# private functions
|
||||
|
||||
defp get_ranges(nat) do
|
||||
nat
|
||||
|> Map.from_struct()
|
||||
|> Map.delete(:flags)
|
||||
|> Enum.map(fn {k, v} -> if(not is_nil(v), do: k, else: nil) end)
|
||||
|> Enum.filter(fn v -> not is_nil(v) end)
|
||||
@range_keys [:ipv4_min, :ipv4_max, :ipv6_min, :ipv6_max, :proto_min, :proto_max]
|
||||
|
||||
@spec get_range_flags(t()) :: [nat_range()]
|
||||
defp get_range_flags(nat) do
|
||||
@range_keys
|
||||
|> Enum.reduce([], fn
|
||||
key, acc when key in @range_keys ->
|
||||
if not is_nil(Map.get(nat, key)), do: [key | acc], else: acc
|
||||
end)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
@spec encode_ranges(binary(), [nat_range()], t()) :: binary()
|
||||
defp encode_ranges(acc, [], _nat), do: acc
|
||||
|
||||
defp encode_ranges(acc, [range | rest], nat) do
|
||||
cond do
|
||||
range == :ipv4_min or range == :ipv4_max ->
|
||||
case Map.get(nat, range) do
|
||||
{a1, a2, a3, a4} ->
|
||||
encode_ranges(<<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
|
||||
defp encode_ranges(acc, [:ipv4_min | rest], %NxNat{ipv4_min: {a1, a2, a3, a4}} = nat) do
|
||||
encode_ranges(<<acc::bytes, a1, a2, a3, a4>>, rest, nat)
|
||||
end
|
||||
|
||||
defp encode_ranges(acc, [:ipv4_max | rest], %NxNat{ipv4_max: {a1, a2, a3, a4}} = nat) do
|
||||
encode_ranges(<<acc::bytes, a1, a2, a3, a4>>, rest, nat)
|
||||
end
|
||||
|
||||
defp encode_ranges(
|
||||
acc,
|
||||
[:ipv6_min | rest],
|
||||
%NxNat{ipv6_min: {a1, a2, a3, a4, a5, a6, a7, a8}} = nat
|
||||
) do
|
||||
encode_ranges(
|
||||
<<acc::bytes, a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16>>,
|
||||
rest,
|
||||
nat
|
||||
)
|
||||
end
|
||||
|
||||
defp encode_ranges(
|
||||
acc,
|
||||
[:ipv6_max | rest],
|
||||
%NxNat{ipv6_max: {a1, a2, a3, a4, a5, a6, a7, a8}} = nat
|
||||
) do
|
||||
encode_ranges(
|
||||
<<acc::bytes, a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16>>,
|
||||
rest,
|
||||
nat
|
||||
)
|
||||
end
|
||||
|
||||
defp encode_ranges(acc, [:proto_min | rest], %NxNat{proto_min: proto} = nat)
|
||||
when is_integer(proto) do
|
||||
encode_ranges(<<acc::bytes, proto::16>>, rest, nat)
|
||||
end
|
||||
|
||||
defp encode_ranges(acc, [:proto_max | rest], %NxNat{proto_max: proto} = nat)
|
||||
when is_integer(proto) do
|
||||
encode_ranges(<<acc::bytes, proto::16>>, rest, nat)
|
||||
end
|
||||
|
||||
@spec decode_ranges(t(), [nat_range()], binary()) :: t()
|
||||
defp decode_ranges(nat, [], _), do: nat
|
||||
|
||||
defp decode_ranges(nat, [range | ranges], bin) do
|
||||
cond do
|
||||
range == :ipv4_min or range == :ipv4_max ->
|
||||
case bin do
|
||||
<<a1, a2, a3, a4, rest::bytes>> ->
|
||||
decode_ranges(struct(nat, %{range => {a1, a2, a3, a4}}), ranges, rest)
|
||||
defp decode_ranges(acc, [:ipv4_min | rest], <<a1, a2, a3, a4, binary::bytes>>) do
|
||||
decode_ranges(%{acc | ipv4_min: {a1, a2, a3, a4}}, rest, binary)
|
||||
end
|
||||
|
||||
rest ->
|
||||
decode_ranges(struct(nat, %{range => ""}), ranges, rest)
|
||||
end
|
||||
defp decode_ranges(acc, [:ipv4_max | rest], <<a1, a2, a3, a4, binary::bytes>>) do
|
||||
decode_ranges(%{acc | ipv4_max: {a1, a2, a3, a4}}, rest, binary)
|
||||
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)
|
||||
defp decode_ranges(
|
||||
acc,
|
||||
[:ipv6_min | rest],
|
||||
<<a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16, binary::bytes>>
|
||||
) do
|
||||
decode_ranges(%{acc | ipv6_min: {a1, a2, a3, a4, a5, a6, a7, a8}}, rest, binary)
|
||||
end
|
||||
|
||||
rest ->
|
||||
decode_ranges(struct(nat, %{range => ""}), ranges, rest)
|
||||
end
|
||||
defp decode_ranges(
|
||||
acc,
|
||||
[:ipv6_max | rest],
|
||||
<<a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16, binary::bytes>>
|
||||
) do
|
||||
decode_ranges(%{acc | ipv6_max: {a1, a2, a3, a4, a5, a6, a7, a8}}, rest, binary)
|
||||
end
|
||||
|
||||
range == :proto_min or range == :proto_max ->
|
||||
case bin do
|
||||
<<proto::16, rest::bytes>> when proto in 1..0xFFFF ->
|
||||
decode_ranges(struct(nat, %{range => proto}), ranges, rest)
|
||||
defp decode_ranges(acc, [:proto_min | rest], <<proto::16, binary::bytes>>) do
|
||||
decode_ranges(%{acc | proto_min: proto}, rest, binary)
|
||||
end
|
||||
|
||||
rest ->
|
||||
decode_ranges(struct(nat, %{range => ""}), ranges, rest)
|
||||
end
|
||||
end
|
||||
defp decode_ranges(acc, [:proto_max | rest], <<proto::16, binary::bytes>>) do
|
||||
decode_ranges(%{acc | proto_max: proto}, rest, binary)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,18 +5,14 @@ defmodule Openflow.Action.NxNote do
|
|||
@nxast 8
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(note) do
|
||||
%NxNote{note: note}
|
||||
end
|
||||
|
||||
def to_binary(%NxNote{note: note}) do
|
||||
padding = Openflow.Utils.padding(byte_size(note) + 2, 8)
|
||||
exp_body = <<@experimenter::32, @nxast::16, note::bytes, 0::size(padding)-unit(8)>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<@experimenter::32, @nxast::16, note::bytes>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, note_bin::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -12,26 +12,47 @@ defmodule Openflow.Action.NxOutputReg do
|
|||
@nxast 15
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@type max_len :: :no_buffer | :max | non_neg_integer()
|
||||
@type t :: %NxOutputReg{
|
||||
n_bits: pos_integer(),
|
||||
offset: non_neg_integer(),
|
||||
src_field: atom(),
|
||||
max_len: max_len()
|
||||
}
|
||||
|
||||
@spec new(
|
||||
n_bits: pos_integer(),
|
||||
offset: non_neg_integer(),
|
||||
src_field: atom(),
|
||||
max_len: max_len()
|
||||
) :: t()
|
||||
def new(options) do
|
||||
src_field = Keyword.get(options, :src_field)
|
||||
src_field = options[:src_field] || raise "src_field must be specified"
|
||||
default_n_bits = Openflow.Match.Field.n_bits_of(src_field)
|
||||
n_bits = Keyword.get(options, :n_bits, default_n_bits)
|
||||
ofs = Keyword.get(options, :offset, 0)
|
||||
max_len = Keyword.get(options, :max_len, :no_buffer)
|
||||
%NxOutputReg{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}
|
||||
|
||||
%NxOutputReg{
|
||||
n_bits: options[:n_bits] || default_n_bits,
|
||||
offset: options[:offset] || 0,
|
||||
src_field: src_field,
|
||||
max_len: options[:max_len] || :no_buffer
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxOutputReg{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}) do
|
||||
src_field_bin = Openflow.Match.codec_header(src_field)
|
||||
ofs_nbits = ofs <<< 6 ||| n_bits - 1
|
||||
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
|
||||
body = <<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)>>
|
||||
def to_binary(%NxOutputReg{} = output_reg) do
|
||||
src_field_bin = Openflow.Match.codec_header(output_reg.src_field)
|
||||
ofs_nbits = output_reg.offset <<< 6 ||| output_reg.n_bits - 1
|
||||
max_len = Openflow.Utils.get_enum(output_reg.max_len, :controller_max_len)
|
||||
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
ofs_nbits::16,
|
||||
src_field_bin::4-bytes,
|
||||
max_len::16,
|
||||
0::size(6)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -12,27 +12,52 @@ defmodule Openflow.Action.NxOutputReg2 do
|
|||
@nxast 32
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@type max_len :: :no_buffer | :max | non_neg_integer()
|
||||
@type t :: %NxOutputReg2{
|
||||
n_bits: pos_integer(),
|
||||
offset: non_neg_integer(),
|
||||
src_field: atom(),
|
||||
max_len: max_len()
|
||||
}
|
||||
|
||||
@spec new(
|
||||
n_bits: pos_integer(),
|
||||
offset: non_neg_integer(),
|
||||
src_field: atom(),
|
||||
max_len: max_len()
|
||||
) :: t()
|
||||
def new(options) do
|
||||
src_field = Keyword.get(options, :src_field)
|
||||
src_field = options[:src_field] || raise "src_field must be specified"
|
||||
default_n_bits = Openflow.Match.Field.n_bits_of(src_field)
|
||||
n_bits = Keyword.get(options, :n_bits, default_n_bits)
|
||||
ofs = Keyword.get(options, :offset, 0)
|
||||
max_len = Keyword.get(options, :max_len, :no_buffer)
|
||||
%NxOutputReg2{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}
|
||||
|
||||
%NxOutputReg2{
|
||||
n_bits: options[:n_bits] || default_n_bits,
|
||||
offset: options[:offset] || 0,
|
||||
src_field: src_field,
|
||||
max_len: options[:max_len] || :no_buffer
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxOutputReg2{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}) do
|
||||
src_field_bin = Openflow.Match.codec_header(src_field)
|
||||
ofs_nbits = ofs <<< 6 ||| n_bits - 1
|
||||
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
|
||||
padding = Openflow.Utils.padding(byte_size(src_field_bin), 10)
|
||||
body = <<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)>>
|
||||
def to_binary(%NxOutputReg2{} = output_reg) do
|
||||
src_field_bin = Openflow.Match.codec_header(output_reg.src_field)
|
||||
ofs_nbits = output_reg.offset <<< 6 ||| output_reg.n_bits - 1
|
||||
max_len = Openflow.Utils.get_enum(output_reg.max_len, :controller_max_len)
|
||||
|
||||
padding =
|
||||
src_field_bin
|
||||
|> byte_size()
|
||||
|> Openflow.Utils.padding(10)
|
||||
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
ofs_nbits::16,
|
||||
max_len::16,
|
||||
src_field_bin::bytes,
|
||||
0::size(padding)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -8,20 +8,28 @@ defmodule Openflow.Action.NxOutputTrunc do
|
|||
@nxast 39
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@spec new(Keyword.t()) :: %NxOutputTrunc{}
|
||||
def new(options) do
|
||||
port_no = Keyword.get(options, :port_number)
|
||||
max_len = Keyword.get(options, :max_len)
|
||||
%NxOutputTrunc{port_number: port_no, max_len: max_len}
|
||||
port_no = options[:port_no] || raise "port_no must be specified"
|
||||
max_len = options[:max_len] || raise "max_len must be specified"
|
||||
|
||||
%NxOutputTrunc{
|
||||
port_number: port_no,
|
||||
max_len: max_len
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxOutputTrunc{port_number: port_no, max_len: max_len}) do
|
||||
port_no_int = Openflow.Utils.get_enum(port_no, :openflow10_port_no)
|
||||
exp_body = <<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
port_no_int::16,
|
||||
max_len::32
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>>) do
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
defmodule Openflow.Action.NxPopMpls do
|
||||
defstruct(ethertype: 0x8847)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 24
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
def new(ethertype \\ 0x8847) do
|
||||
%NxPopMpls{ethertype: ethertype}
|
||||
end
|
||||
|
||||
def to_binary(%NxPopMpls{ethertype: ethertype}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, ethertype::16, 0::size(4)-unit(8)>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, ethertype::16, _::size(4)-unit(8)>>) do
|
||||
%NxPopMpls{ethertype: ethertype}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
defmodule Openflow.Action.NxPopQueue do
|
||||
defstruct([])
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 5
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
def new do
|
||||
%NxPopQueue{}
|
||||
end
|
||||
|
||||
def to_binary(%NxPopQueue{}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::48>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>) do
|
||||
%NxPopQueue{}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
defmodule Openflow.Action.NxPushMpls do
|
||||
defstruct(ethertype: 0x8847)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 23
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
def new(ethertype \\ 0x8847) do
|
||||
%NxPushMpls{ethertype: ethertype}
|
||||
end
|
||||
|
||||
def to_binary(%NxPushMpls{ethertype: ethertype}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, ethertype::16, 0::size(4)-unit(8)>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, ethertype::16, _::size(4)-unit(8)>>) do
|
||||
%NxPushMpls{ethertype: ethertype}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,4 +1,32 @@
|
|||
defmodule Openflow.Action.NxRegLoad do
|
||||
@moduledoc """
|
||||
Copies value[0:n_bits] to dst[ofs:ofs+n_bits], where a[b:c] denotes the bits
|
||||
within 'a' numbered 'b' through 'c' (not including bit 'c'). Bit numbering
|
||||
starts at 0 for the least-significant bit, 1 for the next most significant
|
||||
bit, and so on.
|
||||
|
||||
'dst' is an nxm_header with nxm_hasmask=0. See the documentation for
|
||||
NXAST_REG_MOVE, above, for the permitted fields and for the side effects of
|
||||
loading them.
|
||||
|
||||
The 'ofs' and 'n_bits' fields are combined into a single 'ofs_nbits' field
|
||||
to avoid enlarging the structure by another 8 bytes. To allow 'n_bits' to
|
||||
take a value between 1 and 64 (inclusive) while taking up only 6 bits, it is
|
||||
also stored as one less than its true value:
|
||||
|
||||
```
|
||||
15 6 5 0
|
||||
+------------------------------+------------------+
|
||||
| ofs | n_bits - 1 |
|
||||
+------------------------------+------------------+
|
||||
```
|
||||
|
||||
The switch will reject actions for which ofs+n_bits is greater than the
|
||||
width of 'dst', or in which any bits in 'value' with value 2n_bits or
|
||||
greater are set to 1, with error type OFPET_BAD_ACTION, code
|
||||
OFPBAC_BAD_ARGUMENT.
|
||||
"""
|
||||
|
||||
import Bitwise
|
||||
|
||||
defstruct(
|
||||
|
|
@ -12,28 +40,37 @@ defmodule Openflow.Action.NxRegLoad do
|
|||
@nxast 7
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
dst_field = Keyword.get(options, :dst_field)
|
||||
def new(options \\ []) do
|
||||
dst_field = options[:dst_field] || raise "dst_field must be specified"
|
||||
value = options[:value] || raise "value must be specified"
|
||||
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
|
||||
n_bits = Keyword.get(options, :n_bits, default_n_bits)
|
||||
ofs = Keyword.get(options, :offset, 0)
|
||||
value = Keyword.get(options, :value)
|
||||
%NxRegLoad{n_bits: n_bits, offset: ofs, dst_field: dst_field, value: value}
|
||||
|
||||
%NxRegLoad{
|
||||
n_bits: options[:n_bits] || default_n_bits,
|
||||
offset: options[:offset] || 0,
|
||||
dst_field: dst_field,
|
||||
value: value
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxRegLoad{n_bits: n_bits, offset: ofs, dst_field: dst_field, value: value}) do
|
||||
dst_field_bin = Openflow.Match.codec_header(dst_field)
|
||||
value_bin0 = Openflow.Match.Field.codec(value, dst_field)
|
||||
tmp_value = :binary.decode_unsigned(value_bin0, :big)
|
||||
value_bin = <<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)>>
|
||||
def to_binary(%NxRegLoad{} = load) do
|
||||
ofs_nbits = load.offset <<< 6 ||| load.n_bits - 1
|
||||
dst_field_bin = Openflow.Match.codec_header(load.dst_field)
|
||||
|
||||
value_int =
|
||||
load.value
|
||||
|> Openflow.Match.Field.codec(load.dst_field)
|
||||
|> :binary.decode_unsigned(:big)
|
||||
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
ofs_nbits::16,
|
||||
dst_field_bin::4-bytes,
|
||||
value_int::size(8)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
|
|
|
|||
|
|
@ -1,44 +1,38 @@
|
|||
defmodule Openflow.Action.NxRegLoad2 do
|
||||
defstruct(
|
||||
dst_field: nil,
|
||||
value: nil
|
||||
)
|
||||
defstruct(field: nil)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 33
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
dst_field = Keyword.get(options, :dst_field)
|
||||
value = Keyword.get(options, :value)
|
||||
%NxRegLoad2{dst_field: dst_field, value: value}
|
||||
def new([{_field, _value}] = field) do
|
||||
%NxRegLoad2{field: field}
|
||||
end
|
||||
|
||||
def to_binary(%NxRegLoad2{dst_field: dst_field, value: value}) do
|
||||
def to_binary(%NxRegLoad2{field: field}) do
|
||||
match_bin =
|
||||
[{dst_field, value}]
|
||||
field
|
||||
|> Openflow.Match.new()
|
||||
|> Openflow.Match.to_binary()
|
||||
|
||||
<<1::16, _length::16, padded_field::bytes>> = match_bin
|
||||
patial_len = 4 + 4 + 2 + 6 + byte_size(padded_field)
|
||||
padding = Openflow.Utils.padding(patial_len, 8)
|
||||
<<1::16, length::16, padded_field::bytes>> = match_bin
|
||||
field_len = length - 4
|
||||
<<field_bin::size(field_len)-bytes, _::bytes>> = padded_field
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, 0::48, padded_field::bytes, 0::size(padding)-unit(8)>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
field_bin::bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, _::48, match_field_bin::bytes>>) do
|
||||
def read(<<@experimenter::32, @nxast::16, match_field_bin::bytes>>) do
|
||||
<<_class::16, _field::7, _hm::1, flen::8, _rest::bytes>> = match_field_bin
|
||||
match_len = 4 + 4 + flen
|
||||
match_bin = <<1::16, match_len::16, match_field_bin::bytes, 0::size(4)-unit(8)>>
|
||||
{[{dst_field, value} | _], _rest} = Openflow.Match.read(match_bin)
|
||||
%NxRegLoad2{dst_field: dst_field, value: value}
|
||||
match_bin = <<1::16, match_len::16, match_field_bin::bytes>>
|
||||
{[field | _], _rest} = Openflow.Match.read(match_bin)
|
||||
%NxRegLoad2{field: [field]}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,94 @@
|
|||
defmodule Openflow.Action.NxRegMove do
|
||||
@moduledoc """
|
||||
Copies src[src_ofs:src_ofs+n_bits] to dst[dst_ofs:dst_ofs+n_bits], where
|
||||
a[b:c] denotes the bits within 'a' numbered 'b' through 'c' (not including
|
||||
bit 'c'). Bit numbering starts at 0 for the least-significant bit, 1 for
|
||||
the next most significant bit, and so on.
|
||||
|
||||
The following nxm_header values are potentially acceptable as 'src':
|
||||
- `:in_port`
|
||||
- `:eth_dst`
|
||||
- `:eth_src`
|
||||
- `:eth_type`
|
||||
- `:vlan_tci`
|
||||
- `:ip_tos`
|
||||
- `:ip_proto`
|
||||
- `:ip_src`
|
||||
- `:ip_dst`
|
||||
- `:tcp_src`
|
||||
- `:tcp_dst`
|
||||
- `:udp_src`
|
||||
- `:udp_dst`
|
||||
- `:icmp_type`
|
||||
- `:icmp_code`
|
||||
- `:arp_op`
|
||||
- `:arp_spa`
|
||||
- `:arp_tpa`
|
||||
- `:tun_id`
|
||||
- `:arp_sha`
|
||||
- `:arp_tha`
|
||||
- `:icmpv6_type`
|
||||
- `:icmpv6_code`
|
||||
- `:nd_sll`
|
||||
- `:nd_tll`
|
||||
- `:reg(idx)` for idx in the switch's accepted range.
|
||||
- `:pkt_mark`
|
||||
- `:tun_ipv4_src`
|
||||
- `:tun_ipv4_dst`
|
||||
|
||||
The following nxm_header values are potentially acceptable as 'dst':
|
||||
- `:eth_dst`
|
||||
- `:eth_src`
|
||||
- `:ip_tos`
|
||||
- `:ip_src`
|
||||
- `:ip_dst`
|
||||
- `:tcp_src`
|
||||
- `:tcp_dst`
|
||||
- `:udp_src`
|
||||
- `:udp_dst`
|
||||
- `:icmp_type`
|
||||
- `:icmp_code`
|
||||
- `:icmpv6_type`
|
||||
- `:icmpv6_code`
|
||||
- `:arp_sha`
|
||||
- `:arp_tha`
|
||||
- `:arp_op`
|
||||
- `:arp_spa`
|
||||
- `:arp_tpa`
|
||||
|
||||
Modifying any of the above fields changes the corresponding packet header.
|
||||
|
||||
- `:in_port`
|
||||
|
||||
- `:reg(idx)` for idx in the switch's accepted range.
|
||||
|
||||
- `:pkt_mark`
|
||||
|
||||
- `:vlan_tci`. Modifying this field's value has side effects on the
|
||||
packet's 802.1Q header. Setting a value with CFI=0 removes the 802.1Q
|
||||
header (if any), ignoring the other bits. Setting a value with CFI=1
|
||||
adds or modifies the 802.1Q header appropriately, setting the TCI field
|
||||
to the field's new value (with the CFI bit masked out).
|
||||
|
||||
- `:tun_id`, `:tun_ipv4_src`, `:tun_ipv4_dst`. Modifying
|
||||
any of these values modifies the corresponding tunnel header field used
|
||||
for the packet's next tunnel encapsulation, if allowed by the
|
||||
configuration of the output tunnel port.
|
||||
|
||||
A given nxm_header value may be used as 'src' or 'dst' only on a flow whose
|
||||
nx_match satisfies its prerequisites. For example, NXM_OF_IP_TOS may be
|
||||
used only if the flow's nx_match includes an nxm_entry that specifies
|
||||
nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, and nxm_value=0x0800.
|
||||
|
||||
The switch will reject actions for which src_ofs+n_bits is greater than the
|
||||
width of 'src' or dst_ofs+n_bits is greater than the width of 'dst' with
|
||||
error type OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT.
|
||||
|
||||
This action behaves properly when 'src' overlaps with 'dst', that is, it
|
||||
behaves as if 'src' were copied out to a temporary buffer, then the
|
||||
temporary buffer copied to 'dst'.
|
||||
"""
|
||||
|
||||
defstruct(
|
||||
n_bits: 0,
|
||||
src_offset: 0,
|
||||
|
|
@ -11,44 +101,80 @@ defmodule Openflow.Action.NxRegMove do
|
|||
@nxast 6
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
src_field = Keyword.get(options, :src_field)
|
||||
dst_field = Keyword.get(options, :dst_field)
|
||||
@type t :: %NxRegMove{
|
||||
n_bits: 0..0xFFFF,
|
||||
src_offset: 0..0xFFFF,
|
||||
dst_offset: 0..0xFFFF,
|
||||
src_field: atom(),
|
||||
dst_field: atom()
|
||||
}
|
||||
|
||||
@doc """
|
||||
Creates a new reg_move action struct
|
||||
|
||||
## Options:
|
||||
- n_bits: Number of bits
|
||||
- src_offset: Starting bit offset in source
|
||||
- dst_offset: Starting bit offset in destination
|
||||
- src_field: oxm/nxm field name for source field
|
||||
- dst_field: oxm/nxm field name for destination field
|
||||
|
||||
## Example
|
||||
|
||||
```elixir
|
||||
# 1. move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[]
|
||||
iex> NxRegMove.new(src_field: :nx_in_port, :nx_vlan_tci)
|
||||
|
||||
# 2. move:NXM_NX_TUN_ID[40..57]->NXM_NX_REG1[0..17]
|
||||
iex> NxRegMove.new(
|
||||
iex> src_field: :tun_id,
|
||||
iex> src_offset: 40,
|
||||
iex> dst_field: :reg1
|
||||
iex> dst_offset: 0,
|
||||
iex> n_bits: 18
|
||||
iex> )
|
||||
```
|
||||
"""
|
||||
@spec new(
|
||||
n_bits: 0..0xFFFF,
|
||||
src_offset: 0..0xFFFF,
|
||||
dst_offset: 0..0xFFFF,
|
||||
src_field: atom(),
|
||||
dst_field: atom()
|
||||
) :: t()
|
||||
def new(options \\ []) do
|
||||
src_field = options[:src_field] || raise "src_field must be specified"
|
||||
dst_field = options[:dst_field] || raise "dst_field must be specified"
|
||||
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
|
||||
n_bits = Keyword.get(options, :n_bits, default_n_bits)
|
||||
src_ofs = Keyword.get(options, :src_offset, 0)
|
||||
dst_ofs = Keyword.get(options, :dst_offset, 0)
|
||||
|
||||
%NxRegMove{
|
||||
n_bits: n_bits,
|
||||
src_offset: src_ofs,
|
||||
dst_offset: dst_ofs,
|
||||
n_bits: options[:n_bits] || default_n_bits,
|
||||
src_offset: options[:src_offset] || 0,
|
||||
dst_offset: options[:dst_offset] || 0,
|
||||
src_field: src_field,
|
||||
dst_field: dst_field
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxRegMove{
|
||||
n_bits: n_bits,
|
||||
src_offset: src_ofs,
|
||||
dst_offset: dst_ofs,
|
||||
src_field: src_field,
|
||||
dst_field: dst_field
|
||||
}) do
|
||||
src_field_bin = Openflow.Match.codec_header(src_field)
|
||||
dst_field_bin = Openflow.Match.codec_header(dst_field)
|
||||
@spec to_binary(t()) :: binary()
|
||||
def to_binary(%NxRegMove{} = move) do
|
||||
src_field_bin = Openflow.Match.codec_header(move.src_field)
|
||||
dst_field_bin = Openflow.Match.codec_header(move.dst_field)
|
||||
|
||||
body =
|
||||
<<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)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
move.n_bits::16,
|
||||
move.src_offset::16,
|
||||
move.dst_offset::16,
|
||||
src_field_bin::4-bytes,
|
||||
dst_field_bin::4-bytes
|
||||
>>)
|
||||
end
|
||||
|
||||
@spec read(binary()) :: t()
|
||||
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
|
||||
<<n_bits::16, src_ofs::16, dst_ofs::16, src_field_bin::4-bytes, dst_field_bin::4-bytes>> =
|
||||
body
|
||||
|
|
|
|||
|
|
@ -1,24 +1,76 @@
|
|||
defmodule Openflow.Action.NxResubmit do
|
||||
@moduledoc """
|
||||
Searches the flow table again, using a flow that is slightly modified from the original lookup:
|
||||
|
||||
Following the lookup, the original in_port is restored.
|
||||
|
||||
If the modified flow matched in the flow table, then the corresponding
|
||||
actions are executed. Afterward, actions following NXAST_RESUBMIT in
|
||||
the original set of actions, if any, are executed; any changes made to
|
||||
the packet (e.g. changes to VLAN) by secondary actions persist when
|
||||
those actions are executed, although the original in_port is restored.
|
||||
"""
|
||||
|
||||
defstruct(in_port: :in_port)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 1
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@type t :: %{in_port: port_no()}
|
||||
|
||||
@type port_no ::
|
||||
:max
|
||||
| :in_port
|
||||
| :table
|
||||
| :normal
|
||||
| :flood
|
||||
| :all
|
||||
| :controller
|
||||
| :local
|
||||
| :none
|
||||
| 1..0xFFFF
|
||||
|
||||
@doc """
|
||||
Creates a new nx_resubmit action struct
|
||||
|
||||
## Options:
|
||||
- in_port: New in_port for checking flow table in the one of the `port_no()` type
|
||||
|
||||
## Note:
|
||||
If the modified flow matchd in the flow table, then the corresponding actions are executed,\\
|
||||
Afterward, actions following the resubmit in the original set of actions, if any, are executed;\\
|
||||
any changes made to the packet by secondary actions persist when those actions are executed,
|
||||
although the original in_port is restored
|
||||
|
||||
## Example:
|
||||
|
||||
```elixir
|
||||
iex> %NxResubmit{in_port: :in_port} = NxResubmit.new()
|
||||
iex> %NxResubmit{in_port: 1} = NxResubmit.new(1)
|
||||
```
|
||||
|
||||
"""
|
||||
@spec new(port_no()) :: t()
|
||||
def new(in_port \\ :in_port) do
|
||||
%NxResubmit{in_port: in_port}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: binary()
|
||||
def to_binary(%NxResubmit{in_port: in_port}) do
|
||||
in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no)
|
||||
exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, 0::size(4)-unit(8)>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
in_port_int::16,
|
||||
0::size(4)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
@spec read(binary()) :: t()
|
||||
def read(<<@experimenter::32, @nxast::16, in_port_int::16, _::size(4)-unit(8)>>) do
|
||||
in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no)
|
||||
%NxResubmit{in_port: in_port}
|
||||
|
|
|
|||
|
|
@ -5,25 +5,31 @@ defmodule Openflow.Action.NxResubmitTable do
|
|||
@nxast 14
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options \\ [])
|
||||
|
||||
def new(table_id) when is_atom(table_id) or is_integer(table_id) do
|
||||
new(table_id: table_id)
|
||||
end
|
||||
|
||||
def new(options) do
|
||||
in_port = Keyword.get(options, :in_port, :in_port)
|
||||
table_id = Keyword.get(options, :table_id, :all)
|
||||
in_port = options[:in_port] || :in_port
|
||||
table_id = options[:table_id] || :all
|
||||
%NxResubmitTable{in_port: in_port, table_id: table_id}
|
||||
end
|
||||
|
||||
def to_binary(%NxResubmitTable{in_port: in_port, table_id: table_id}) do
|
||||
in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no)
|
||||
table_id_int = Openflow.Utils.get_enum(table_id, :table_id)
|
||||
exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::24>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
def to_binary(%NxResubmitTable{} = resubmit_table) do
|
||||
in_port_int = Openflow.Utils.get_enum(resubmit_table.in_port, :openflow10_port_no)
|
||||
table_id_int = Openflow.Utils.get_enum(resubmit_table.table_id, :table_id)
|
||||
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
in_port_int::16,
|
||||
table_id_int::8,
|
||||
0::24
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
|
|
|
|||
|
|
@ -5,21 +5,31 @@ defmodule Openflow.Action.NxResubmitTableCt do
|
|||
@nxast 44
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
in_port = Keyword.get(options, :in_port, :in_port)
|
||||
table_id = Keyword.get(options, :table_id, :all)
|
||||
def new(options \\ [])
|
||||
|
||||
def new(table_id) when is_atom(table_id) or is_integer(table_id) do
|
||||
new(table_id: table_id)
|
||||
end
|
||||
|
||||
def new(options) when is_list(options) do
|
||||
in_port = options[:in_port] || :in_port
|
||||
table_id = options[:table_id] || :all
|
||||
%NxResubmitTableCt{in_port: in_port, table_id: table_id}
|
||||
end
|
||||
|
||||
def to_binary(%NxResubmitTableCt{in_port: in_port, table_id: table_id}) do
|
||||
in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no)
|
||||
table_id_int = Openflow.Utils.get_enum(table_id, :table_id)
|
||||
exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::24>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
def to_binary(%NxResubmitTableCt{} = resubmit_table) do
|
||||
in_port_int = Openflow.Utils.get_enum(resubmit_table.in_port, :openflow10_port_no)
|
||||
table_id_int = Openflow.Utils.get_enum(resubmit_table.table_id, :table_id)
|
||||
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
in_port_int::16,
|
||||
table_id_int::8,
|
||||
0::24
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
|
|
|
|||
|
|
@ -10,35 +10,40 @@ defmodule Openflow.Action.NxSample do
|
|||
@nxast 29
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@spec new(
|
||||
probability: 1..0xFFFF,
|
||||
collector_set_id: 0..0xFFFFFFFF,
|
||||
obs_domain_id: 0..0xFFFFFFFF,
|
||||
obs_point_id: 0..0xFFFFFFFF
|
||||
) :: %NxSample{
|
||||
probability: 1..0xFFFF,
|
||||
collector_set_id: 0..0xFFFFFFFF,
|
||||
obs_domain_id: 0..0xFFFFFFFF,
|
||||
obs_point_id: 0..0xFFFFFFFF
|
||||
}
|
||||
def new(options) do
|
||||
probability = Keyword.get(options, :probability, 0)
|
||||
collector_set_id = Keyword.get(options, :collector_set_id, 0)
|
||||
obs_domain_id = Keyword.get(options, :obs_domain_id, 0)
|
||||
obs_point_id = Keyword.get(options, :obs_point_id, 0)
|
||||
(is_integer(options[:probability]) and options[:probability] > 0) ||
|
||||
raise("probability must be greater than 0")
|
||||
|
||||
%NxSample{
|
||||
probability: probability,
|
||||
collector_set_id: collector_set_id,
|
||||
obs_domain_id: obs_domain_id,
|
||||
obs_point_id: obs_point_id
|
||||
probability: options[:probability],
|
||||
collector_set_id: options[:collector_set_id] || 0,
|
||||
obs_domain_id: options[:obs_domain_id] || 0,
|
||||
obs_point_id: options[:obs_point_id] || 0
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxSample{
|
||||
probability: probability,
|
||||
collector_set_id: collector_set_id,
|
||||
obs_domain_id: obs_domain_id,
|
||||
obs_point_id: obs_point_id
|
||||
}) do
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
|
||||
obs_point_id::32>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
def to_binary(%NxSample{} = sample) do
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
sample.probability::16,
|
||||
sample.collector_set_id::32,
|
||||
sample.obs_domain_id::32,
|
||||
sample.obs_point_id::32
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
|
|
|
|||
|
|
@ -11,44 +11,44 @@ defmodule Openflow.Action.NxSample2 do
|
|||
@nxast 38
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
probability = Keyword.get(options, :probability, 0)
|
||||
collector_set_id = Keyword.get(options, :collector_set_id, 0)
|
||||
obs_domain_id = Keyword.get(options, :obs_domain_id, 0)
|
||||
obs_point_id = Keyword.get(options, :obs_point_id, 0)
|
||||
sampling_port = Keyword.get(options, :sampling_port, 0)
|
||||
def new(options \\ []) do
|
||||
(is_integer(options[:probability]) and options[:probability] > 0) ||
|
||||
raise("probability must be greater than 0")
|
||||
|
||||
%NxSample2{
|
||||
probability: probability,
|
||||
collector_set_id: collector_set_id,
|
||||
obs_domain_id: obs_domain_id,
|
||||
obs_point_id: obs_point_id,
|
||||
sampling_port: sampling_port
|
||||
probability: options[:probability],
|
||||
collector_set_id: options[:collector_set_id] || 0,
|
||||
obs_domain_id: options[:obs_domain_id] || 0,
|
||||
obs_point_id: options[:obs_point_id] || 0,
|
||||
sampling_port: options[:sampling_port] || 0
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxSample2{
|
||||
probability: probability,
|
||||
collector_set_id: collector_set_id,
|
||||
obs_domain_id: obs_domain_id,
|
||||
obs_point_id: obs_point_id,
|
||||
sampling_port: sampling_port
|
||||
}) do
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
|
||||
obs_point_id::32, sampling_port::16, 0::size(6)-unit(8)>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
def to_binary(%NxSample2{} = sample) do
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
sample.probability::16,
|
||||
sample.collector_set_id::32,
|
||||
sample.obs_domain_id::32,
|
||||
sample.obs_point_id::32,
|
||||
sample.sampling_port::16,
|
||||
0::size(6)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
|
||||
obs_point_id::32, sampling_port::16, 0::size(6)-unit(8)>>
|
||||
) do
|
||||
def read(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
probability::16,
|
||||
collector_set_id::32,
|
||||
obs_domain_id::32,
|
||||
obs_point_id::32,
|
||||
sampling_port::16,
|
||||
_::size(6)-unit(8)
|
||||
>>) do
|
||||
%NxSample2{
|
||||
probability: probability,
|
||||
collector_set_id: collector_set_id,
|
||||
|
|
|
|||
|
|
@ -12,43 +12,36 @@ defmodule Openflow.Action.NxSample3 do
|
|||
@nxast 41
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
probability = Keyword.get(options, :probability, 0)
|
||||
collector_set_id = Keyword.get(options, :collector_set_id, 0)
|
||||
obs_domain_id = Keyword.get(options, :obs_domain_id, 0)
|
||||
obs_point_id = Keyword.get(options, :obs_point_id, 0)
|
||||
sampling_port = Keyword.get(options, :sampling_port, 0)
|
||||
direction = Keyword.get(options, :direction, :default)
|
||||
def new(options \\ []) do
|
||||
(is_integer(options[:probability]) and options[:probability] > 0) ||
|
||||
raise("probability must be greater than 0")
|
||||
|
||||
%NxSample3{
|
||||
probability: probability,
|
||||
collector_set_id: collector_set_id,
|
||||
obs_domain_id: obs_domain_id,
|
||||
obs_point_id: obs_point_id,
|
||||
sampling_port: sampling_port,
|
||||
direction: direction
|
||||
probability: options[:probability] || 0,
|
||||
collector_set_id: options[:collector_set_id] || 0,
|
||||
obs_domain_id: options[:obs_domain_id] || 0,
|
||||
obs_point_id: options[:obs_point_id] || 0,
|
||||
sampling_port: options[:sampling_port] || 0,
|
||||
direction: options[:direction] || :default
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxSample3{
|
||||
probability: probability,
|
||||
collector_set_id: collector_set_id,
|
||||
obs_domain_id: obs_domain_id,
|
||||
obs_point_id: obs_point_id,
|
||||
sampling_port: sampling_port,
|
||||
direction: direction
|
||||
}) do
|
||||
direction_int = Openflow.Enums.to_int(direction, :nx_action_sample_direction)
|
||||
def to_binary(%NxSample3{} = sample) do
|
||||
sample_direction_int = Openflow.Enums.to_int(sample.direction, :nx_action_sample_direction)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, obs_domain_id::32,
|
||||
obs_point_id::32, sampling_port::16, direction_int::8, 0::size(5)-unit(8)>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
sample.probability::16,
|
||||
sample.collector_set_id::32,
|
||||
sample.obs_domain_id::32,
|
||||
sample.obs_point_id::32,
|
||||
sample.sampling_port::16,
|
||||
sample_direction_int::8,
|
||||
0::size(5)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
defmodule Openflow.Action.NxSetMplsLabel do
|
||||
defstruct(label: 0)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 30
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
def new(label) do
|
||||
%NxSetMplsLabel{label: label}
|
||||
end
|
||||
|
||||
def to_binary(%NxSetMplsLabel{label: label}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::16, label::32>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, _::16, label::32>>) do
|
||||
%NxSetMplsLabel{label: label}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
defmodule Openflow.Action.NxSetMplsTc do
|
||||
defstruct(tc: 0)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 31
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
def new(tc) do
|
||||
%NxSetMplsTc{tc: tc}
|
||||
end
|
||||
|
||||
def to_binary(%NxSetMplsTc{tc: tc}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, tc::8, 0::size(5)-unit(8)>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, tc::8, _::size(5)-unit(8)>>) do
|
||||
%NxSetMplsTc{tc: tc}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
defmodule Openflow.Action.NxSetMplsTtl do
|
||||
defstruct(ttl: 0)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 25
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
def new(ttl) do
|
||||
%NxSetMplsTtl{ttl: ttl}
|
||||
end
|
||||
|
||||
def to_binary(%NxSetMplsTtl{ttl: ttl}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, ttl::8, 0::size(5)-unit(8)>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, ttl::8, _::size(5)-unit(8)>>) do
|
||||
%NxSetMplsTtl{ttl: ttl}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
defmodule Openflow.Action.NxSetQueue do
|
||||
defstruct(queue_id: 0)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 4
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
def new(queue_id) do
|
||||
%NxSetQueue{queue_id: queue_id}
|
||||
end
|
||||
|
||||
def to_binary(%NxSetQueue{queue_id: queue_id}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::16, queue_id::32>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, 0::size(2)-unit(8), queue_id::32>>) do
|
||||
%NxSetQueue{queue_id: queue_id}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,23 +1,43 @@
|
|||
defmodule Openflow.Action.NxSetTunnel do
|
||||
@moduledoc """
|
||||
Set encapsulating tunnel ID.
|
||||
"""
|
||||
|
||||
defstruct(tunnel_id: 0)
|
||||
|
||||
@experimenter 0x00002320
|
||||
@nxast 2
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@type tunnel_id :: 0..0xFFFFFFFF
|
||||
@type t :: %NxSetTunnel{tunnel_id: tunnel_id()}
|
||||
|
||||
@doc """
|
||||
Creates a new set_tunnel action struct
|
||||
|
||||
## Example:
|
||||
```elixir
|
||||
iex> %NxSetTunnel{tunnel_id: 1} = NxSetTunnel.new(1)
|
||||
```
|
||||
"""
|
||||
@spec new(tunnel_id()) :: t()
|
||||
def new(tunnel_id) do
|
||||
%NxSetTunnel{tunnel_id: tunnel_id}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: binary()
|
||||
def to_binary(%NxSetTunnel{tunnel_id: tunnel_id}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::16, tunnel_id::32>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
0::16,
|
||||
tunnel_id::32
|
||||
>>)
|
||||
end
|
||||
|
||||
@spec read(binary()) :: t()
|
||||
def read(<<@experimenter::32, @nxast::16, _::16, tunnel_id::32>>) do
|
||||
%NxSetTunnel{tunnel_id: tunnel_id}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,17 +5,22 @@ defmodule Openflow.Action.NxSetTunnel64 do
|
|||
@nxast 9
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
@spec new(tunnel_id :: 0..0xFFFFFFFFFFFFFFFF) :: %NxSetTunnel64{
|
||||
tunnel_id: 0..0xFFFFFFFFFFFFFFFF
|
||||
}
|
||||
def new(tunnel_id) do
|
||||
%NxSetTunnel64{tunnel_id: tunnel_id}
|
||||
end
|
||||
|
||||
def to_binary(%NxSetTunnel64{tunnel_id: tunnel_id}) do
|
||||
exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>>
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
0::size(6)-unit(8),
|
||||
tunnel_id::64
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>>) do
|
||||
|
|
|
|||
|
|
@ -9,26 +9,30 @@ defmodule Openflow.Action.NxStackPop do
|
|||
@nxast 28
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
field = Keyword.get(options, :field)
|
||||
field = options[:field] || raise "field must be specified"
|
||||
default_n_bits = Openflow.Match.Field.n_bits_of(field)
|
||||
n_bits = Keyword.get(options, :n_bits, default_n_bits)
|
||||
ofs = Keyword.get(options, :offset, 0)
|
||||
%NxStackPop{n_bits: n_bits, offset: ofs, field: field}
|
||||
|
||||
%NxStackPop{
|
||||
n_bits: options[:n_bits] || default_n_bits,
|
||||
offset: options[:offset] || 0,
|
||||
field: field
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxStackPop{n_bits: n_bits, offset: ofs, field: field}) do
|
||||
field_bin = Openflow.Match.codec_header(field)
|
||||
def to_binary(%NxStackPop{} = stack_pop) do
|
||||
field_bin = Openflow.Match.codec_header(stack_pop.field)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, ofs::16, field_bin::4-bytes, n_bits::16,
|
||||
0::size(6)-unit(8)>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
stack_pop.offset::16,
|
||||
field_bin::4-bytes,
|
||||
stack_pop.n_bits::16,
|
||||
0::size(6)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
|
|
|
|||
|
|
@ -9,26 +9,30 @@ defmodule Openflow.Action.NxStackPush do
|
|||
@nxast 27
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options) do
|
||||
field = Keyword.get(options, :field)
|
||||
field = options[:field] || raise "field must be specified"
|
||||
default_n_bits = Openflow.Match.Field.n_bits_of(field)
|
||||
n_bits = Keyword.get(options, :n_bits, default_n_bits)
|
||||
ofs = Keyword.get(options, :offset, 0)
|
||||
%NxStackPush{n_bits: n_bits, offset: ofs, field: field}
|
||||
|
||||
%NxStackPush{
|
||||
n_bits: options[:n_bits] || default_n_bits,
|
||||
offset: options[:offset] || 0,
|
||||
field: field
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%NxStackPush{n_bits: n_bits, offset: ofs, field: field}) do
|
||||
field_bin = Openflow.Match.codec_header(field)
|
||||
def to_binary(%NxStackPush{} = stack_pop) do
|
||||
field_bin = Openflow.Match.codec_header(stack_pop.field)
|
||||
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, ofs::16, field_bin::4-bytes, n_bits::16,
|
||||
0::size(6)-unit(8)>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
stack_pop.offset::16,
|
||||
field_bin::4-bytes,
|
||||
stack_pop.n_bits::16,
|
||||
0::size(6)-unit(8)
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(
|
||||
|
|
|
|||
|
|
@ -8,25 +8,28 @@ defmodule Openflow.Action.NxWriteMetadata do
|
|||
@nxast 22
|
||||
|
||||
alias __MODULE__
|
||||
alias Openflow.Action.Experimenter
|
||||
|
||||
def new(options \\ [])
|
||||
|
||||
def new(metadata) when is_integer(metadata) do
|
||||
new(metadata: metadata)
|
||||
end
|
||||
|
||||
def new(options) when is_list(options) do
|
||||
metadata = Keyword.get(options, :metadata, 0)
|
||||
metadata_mask = Keyword.get(options, :metadata_mask, 0xFFFFFFFFFFFFFFFF)
|
||||
metadata = options[:metadata] || raise "metadata must be specified"
|
||||
metadata_mask = options[:metadata_mask] || 0xFFFFFFFFFFFFFFFF
|
||||
%NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask}
|
||||
end
|
||||
|
||||
def to_binary(%NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask}) do
|
||||
exp_body =
|
||||
<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), metadata::64, metadata_mask::64>>
|
||||
|
||||
exp_body_size = byte_size(exp_body)
|
||||
padding_length = Openflow.Utils.padding(4 + exp_body_size, 8)
|
||||
length = 4 + exp_body_size + padding_length
|
||||
<<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>>
|
||||
Experimenter.pack_exp_header(<<
|
||||
@experimenter::32,
|
||||
@nxast::16,
|
||||
0::size(6)-unit(8),
|
||||
metadata::64,
|
||||
metadata_mask::64
|
||||
>>)
|
||||
end
|
||||
|
||||
def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8), metadata::64, metadata_mask::64>>) do
|
||||
|
|
|
|||
|
|
@ -1,29 +1,67 @@
|
|||
defmodule Openflow.Action.Output do
|
||||
defstruct(
|
||||
port_number: 0,
|
||||
max_len: :no_buffer
|
||||
)
|
||||
@moduledoc """
|
||||
Action for sends packets out `port_number`.
|
||||
"""
|
||||
|
||||
defstruct port_number: 0,
|
||||
max_len: :no_buffer
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type port_no ::
|
||||
0..0xFFFFFFFF
|
||||
| :max
|
||||
| :in_port
|
||||
| :table
|
||||
| :normal
|
||||
| :flood
|
||||
| :all
|
||||
| :controller
|
||||
| :local
|
||||
| :any
|
||||
|
||||
@type max_len :: 0..0xFFFFFFFF | :no_buffer | :max
|
||||
|
||||
@type t :: %Output{port_number: port_no(), max_len: max_len()}
|
||||
|
||||
@spec ofpat() :: 0
|
||||
def ofpat, do: 0
|
||||
|
||||
def new(port) when not is_list(port) do
|
||||
new(port_number: port)
|
||||
end
|
||||
@doc """
|
||||
Create a new output action structure
|
||||
|
||||
## Options:
|
||||
- `port_number`: Output port
|
||||
- `max_len`: Max length to send to controller
|
||||
|
||||
## Example
|
||||
|
||||
```elixir
|
||||
iex> %Output{port_number: 1, max_len: :no_buffer} = Output.new(1)
|
||||
```
|
||||
"""
|
||||
@spec new([port_number: port_no, max_len: max_len()] | port_no()) :: Output.t()
|
||||
def new(options \\ [])
|
||||
|
||||
@spec new(port :: port_no()) :: Output.t()
|
||||
def new(port) when is_atom(port) or is_integer(port),
|
||||
do: new(port_number: port)
|
||||
|
||||
@spec new(options :: [port_number: port_no(), max_len: max_len()]) :: Output.t()
|
||||
def new(options) when is_list(options) do
|
||||
port_no = Keyword.get(options, :port_number)
|
||||
max_len = Keyword.get(options, :max_len, :no_buffer)
|
||||
port_no = options[:port_number] || raise "port_number must be specified"
|
||||
max_len = options[:max_len] || :no_buffer
|
||||
%Output{port_number: port_no, max_len: max_len}
|
||||
end
|
||||
|
||||
@spec to_binary(Output.t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%Output{port_number: port_no, max_len: max_len}) do
|
||||
port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no)
|
||||
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
|
||||
<<0::16, 16::16, port_no_int::32, max_len::16, 0::size(6)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: Output.t()
|
||||
def read(<<0::16, 16::16, port_no_int::32, max_len::16, _pad::size(6)-unit(8)>>) do
|
||||
port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no)
|
||||
max_len = Openflow.Utils.get_enum(max_len, :controller_max_len)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,42 @@
|
|||
defmodule Openflow.Action.PopMpls do
|
||||
@moduledoc """
|
||||
Pop the out MPLS label
|
||||
|
||||
note: The one of ETH__P_MPLS_* is needed to be specified to eth_type field
|
||||
"""
|
||||
|
||||
defstruct(ethertype: 0x8847)
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %PopMpls{}
|
||||
|
||||
@eth_p_mpls_uc 0x8847
|
||||
|
||||
@spec ofpat() :: 20
|
||||
def ofpat, do: 20
|
||||
|
||||
def new(ethertype) do
|
||||
@doc """
|
||||
Create a new pop_mpls action struct
|
||||
|
||||
note: 0x8847(ETH_P_MPLS_UC) as default value.
|
||||
|
||||
```elixir
|
||||
iex> %PopMpls{ethertype: 0x8847} = PopMpls.new()
|
||||
```
|
||||
"""
|
||||
@spec new() :: t()
|
||||
@spec new(ethertype :: 0..0xFFFF) :: t()
|
||||
def new(ethertype \\ @eth_p_mpls_uc) do
|
||||
%PopMpls{ethertype: ethertype}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%PopMpls{ethertype: ethertype}) do
|
||||
<<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do
|
||||
%PopMpls{ethertype: ethertype}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,35 @@
|
|||
defmodule Openflow.Action.PopVlan do
|
||||
@moduledoc """
|
||||
Pop the outer VLAN tag.
|
||||
"""
|
||||
|
||||
defstruct([])
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t() :: %PopVlan{}
|
||||
|
||||
@spec ofpat() :: 18
|
||||
def ofpat, do: 18
|
||||
|
||||
@doc """
|
||||
Create a new pop_vlan action struct
|
||||
|
||||
````elixir
|
||||
iex> %PopVlan{} = PopVlan.new()
|
||||
````
|
||||
"""
|
||||
@spec new() :: t()
|
||||
def new do
|
||||
%PopVlan{}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%PopVlan{}) do
|
||||
<<18::16, 8::16, 0::size(4)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<18::16, 8::16, _::size(4)-unit(8)>>) do
|
||||
%PopVlan{}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,37 @@
|
|||
defmodule Openflow.Action.PushMpls do
|
||||
@moduledoc """
|
||||
Push a new MPLS label
|
||||
"""
|
||||
|
||||
defstruct(ethertype: 0x8847)
|
||||
|
||||
@eth_p_mpls_uc 0x8847
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %PushMpls{ethertype: 0..0xFFFF}
|
||||
|
||||
@spec ofpat() :: 19
|
||||
def ofpat, do: 19
|
||||
|
||||
def new(ethertype) do
|
||||
@doc """
|
||||
Create a new push_mpls action struct\\
|
||||
0x8847(ETH_P_MPLS_UC) as a default value.
|
||||
|
||||
iex> %PushMpls{ethertype: 0x8847} = PushMpls.new()
|
||||
"""
|
||||
@spec new() :: t()
|
||||
@spec new(ethertype :: 0..0xFFFF) :: t()
|
||||
def new(ethertype \\ @eth_p_mpls_uc) do
|
||||
%PushMpls{ethertype: ethertype}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%PushMpls{ethertype: ethertype}) do
|
||||
<<19::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<19::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do
|
||||
%PushMpls{ethertype: ethertype}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,34 @@
|
|||
defmodule Openflow.Action.PushVlan do
|
||||
@moduledoc """
|
||||
Push a new VLAN tag
|
||||
"""
|
||||
|
||||
defstruct(ethertype: 0x8100)
|
||||
|
||||
alias __MODULE__
|
||||
@type t :: %PushVlan{ethertype: 0..0xFFFF}
|
||||
|
||||
@spec ofpat() :: 17
|
||||
def ofpat, do: 17
|
||||
|
||||
@doc """
|
||||
Create a new push_vlan action struct.\\
|
||||
0x8100 is a default value.
|
||||
|
||||
iex> %PushVlan{ethertype: 0x8100} = PushVlan.new()
|
||||
"""
|
||||
@spec new() :: t()
|
||||
@spec new(ethertype :: 0..0xFFFF) :: t()
|
||||
def new(ethertype \\ 0x8100) do
|
||||
%PushVlan{ethertype: ethertype}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%PushVlan{ethertype: ethertype}) do
|
||||
<<17::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<17::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do
|
||||
%PushVlan{ethertype: ethertype}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,19 +1,81 @@
|
|||
defmodule Openflow.Action.SetField do
|
||||
@moduledoc """
|
||||
Set a header field using OXM TLV format.
|
||||
"""
|
||||
|
||||
defstruct(field: nil)
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %SetField{field: Keyword.t()}
|
||||
|
||||
@set_field_size 8
|
||||
|
||||
def ofpat, do: 25
|
||||
|
||||
def new({_field, _value} = oxm_field) do
|
||||
@doc """
|
||||
Create a new set_field action struct
|
||||
|
||||
note: The following oxm(nxm)_header values are potentially acceptable as `field`:
|
||||
|
||||
- :tun_id
|
||||
- :tun_ipv4_src
|
||||
- :tun_ipv4_dst
|
||||
- :tun_ipv6_src
|
||||
- :tun_ipv6_dst
|
||||
- :tun_flags
|
||||
- :tun_gbp_id
|
||||
- :tun_gbp_flags
|
||||
- :tun_metadata{0..63}
|
||||
- :in_port
|
||||
- :pkt_mark
|
||||
- :ct_mark
|
||||
- :ct_label
|
||||
- :reg{0..15}
|
||||
- :xreg{0..8}
|
||||
- :xxreg{0..4}
|
||||
- :eth_src
|
||||
- :eth_dst
|
||||
- :vlan_tci
|
||||
- :mpls_ttl
|
||||
- :ip_src
|
||||
- :ip_dst
|
||||
- :ipv6_src
|
||||
- :ipv6_dst
|
||||
- :ipv6_label
|
||||
- :ip_tos
|
||||
- :ip_ecn
|
||||
- :ip_ttl
|
||||
- :arp_op
|
||||
- :arp_spa
|
||||
- :arp_tpa
|
||||
- :arp_sha
|
||||
- :arp_tha
|
||||
- :tcp_src
|
||||
- :tcp_dst
|
||||
- :udp_src
|
||||
- :udp_dst
|
||||
- :icmp_type
|
||||
- :icmp_code
|
||||
- :icmpv6_type
|
||||
- :icmpv6_code
|
||||
- :nd_target
|
||||
- :nd_sll
|
||||
- :nd_tll
|
||||
- :metadata
|
||||
|
||||
```elixir
|
||||
iex> %SetField{field: [reg1: 10]} = SetField.new(reg1: 10)
|
||||
```
|
||||
"""
|
||||
@spec new(Keyword.t()) :: t()
|
||||
def new([{_field, _value}] = oxm_field) do
|
||||
%SetField{field: oxm_field}
|
||||
end
|
||||
|
||||
def to_binary(%SetField{field: {field, value}}) do
|
||||
def to_binary(%SetField{field: field}) do
|
||||
match_bin =
|
||||
[{field, value}]
|
||||
field
|
||||
|> Openflow.Match.new()
|
||||
|> Openflow.Match.to_binary()
|
||||
|
||||
|
|
@ -29,6 +91,6 @@ defmodule Openflow.Action.SetField do
|
|||
match_len = 4 + 4 + flen
|
||||
match_bin = <<1::16, match_len::16, match_field_bin::bytes>>
|
||||
{[field | _], _rest} = Openflow.Match.read(match_bin)
|
||||
%SetField{field: field}
|
||||
%SetField{field: [field]}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,38 @@
|
|||
defmodule Openflow.Action.SetMplsTtl do
|
||||
@moduledoc """
|
||||
Replace the existing MPLS TTL.\\
|
||||
Only applies to the packets with an existing MPLS shim header.
|
||||
"""
|
||||
|
||||
defstruct(ttl: 0)
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %SetMplsTtl{ttl: 0..0xFF}
|
||||
|
||||
@spec ofpat() :: 15
|
||||
def ofpat, do: 15
|
||||
|
||||
@doc """
|
||||
Create a set_mpls_ttl action structure
|
||||
|
||||
## Example:
|
||||
```elixir
|
||||
iex> %SetMplsTtl{ttl: 64} = Openflow.Action.SetMplsTtl.new(64)
|
||||
```
|
||||
"""
|
||||
@deprecated "OFPAT_SET_MPLS_TTL is deprecated in OpenFlow13, use SetField"
|
||||
@spec new(0..0xFF) :: SetMplsTtl.t()
|
||||
def new(ttl) do
|
||||
%SetMplsTtl{ttl: ttl}
|
||||
end
|
||||
|
||||
@spec to_binary(SetMplsTtl.t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%SetMplsTtl{ttl: ttl}) do
|
||||
<<15::16, 8::16, ttl::8, 0::size(3)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: SetMplsTtl.t()
|
||||
def read(<<15::16, 8::16, ttl::8, _::size(3)-unit(8)>>) do
|
||||
%SetMplsTtl{ttl: ttl}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,38 @@
|
|||
defmodule Openflow.Action.SetNwTtl do
|
||||
@moduledoc """
|
||||
Set IP TTL
|
||||
"""
|
||||
|
||||
defstruct(ttl: 0)
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %SetNwTtl{ttl: 0..0xFF}
|
||||
|
||||
@spec ofpat() :: 23
|
||||
def ofpat, do: 23
|
||||
|
||||
@doc """
|
||||
Create a new set_nw_ttl action struct
|
||||
|
||||
## Options:
|
||||
- IP TTL
|
||||
|
||||
```elixir
|
||||
iex> %SetNwTtl{ttl: 64} = SetNwTtl.new(_ttl = 64)
|
||||
```
|
||||
"""
|
||||
@spec new(ttl :: 0..0xFF) :: t()
|
||||
def new(ttl) do
|
||||
%SetNwTtl{ttl: ttl}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%SetNwTtl{ttl: ttl}) do
|
||||
<<23::16, 8::16, ttl::8, 0::size(3)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<23::16, 8::16, ttl::8, _::size(3)-unit(8)>>) do
|
||||
%SetNwTtl{ttl: ttl}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,38 @@
|
|||
defmodule Openflow.Action.SetQueue do
|
||||
@moduledoc """
|
||||
Set queue id when outputting to a port
|
||||
"""
|
||||
|
||||
defstruct(id: 0)
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %SetQueue{id: 0..0xFFFFFFFF}
|
||||
|
||||
@spec ofpat() :: 21
|
||||
def ofpat, do: 21
|
||||
|
||||
@doc """
|
||||
Create a new set_queue action struct
|
||||
|
||||
## Options:
|
||||
- Queue id for the packets
|
||||
|
||||
```elixir
|
||||
iex> %SetQueue{id: 1} = SetQueue.new(_id = 1)
|
||||
```
|
||||
"""
|
||||
@spec new(id :: 0..0xFFFFFFFF) :: t()
|
||||
def new(id) do
|
||||
%SetQueue{id: id}
|
||||
end
|
||||
|
||||
@spec to_binary(t()) :: <<_::16, _::_*8>>
|
||||
def to_binary(%SetQueue{id: id}) do
|
||||
<<21::16, 8::16, id::32>>
|
||||
end
|
||||
|
||||
@spec read(<<_::16, _::_*8>>) :: t()
|
||||
def read(<<21::16, 8::16, id::32>>) do
|
||||
%SetQueue{id: id}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,15 +12,9 @@ defmodule Openflow.Barrier.Reply do
|
|||
|
||||
def ofp_type, do: 21
|
||||
|
||||
def new do
|
||||
%Reply{}
|
||||
end
|
||||
def new(xid \\ 0), do: %Reply{xid: xid}
|
||||
|
||||
def read(_) do
|
||||
%Reply{}
|
||||
end
|
||||
def read(_), do: %Reply{}
|
||||
|
||||
def to_binary(%Reply{}) do
|
||||
<<>>
|
||||
end
|
||||
def to_binary(%Reply{}), do: <<>>
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,17 +10,22 @@ defmodule Openflow.Barrier.Request do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %Request{
|
||||
version: 4,
|
||||
xid: 0..0xFFFFFFFF,
|
||||
datapath_id: String.t() | nil,
|
||||
aux_id: non_neg_integer() | nil
|
||||
}
|
||||
|
||||
@spec ofp_type() :: 20
|
||||
def ofp_type, do: 20
|
||||
|
||||
def new(xid \\ 0) do
|
||||
%Request{xid: xid}
|
||||
end
|
||||
@spec new(xid :: 0..0xFFFFFFFF) :: t()
|
||||
def new(xid \\ 0), do: %Request{xid: xid}
|
||||
|
||||
def read(_) do
|
||||
%Request{}
|
||||
end
|
||||
@spec read(binary()) :: t()
|
||||
def read(_), do: %Request{}
|
||||
|
||||
def to_binary(%Request{}) do
|
||||
<<>>
|
||||
end
|
||||
@spec to_binary(t()) :: binary()
|
||||
def to_binary(%Request{}), do: <<>>
|
||||
end
|
||||
|
|
|
|||
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