Fix to handler start_link fail
This commit is contained in:
parent
0df6336f5e
commit
658b7448da
17 changed files with 17 additions and 2012 deletions
20
examples/heckle/.gitignore
vendored
20
examples/heckle/.gitignore
vendored
|
|
@ -1,20 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# Heckle
|
||||
|
||||
**TODO: Add description**
|
||||
|
||||
## Installation
|
||||
|
||||
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||
by adding `heckle` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:heckle, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||
be found at [https://hexdocs.pm/heckle](https://hexdocs.pm/heckle).
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# This file is responsible for configuring your application
|
||||
# and its dependencies with the aid of the Mix.Config module.
|
||||
use Mix.Config
|
||||
|
||||
config :heckle,
|
||||
vlan_tagging: true,
|
||||
vlan_id: 123,
|
||||
vlan_trunk1: "1",
|
||||
vlan_trunk2: "4",
|
||||
access_port1: "2",
|
||||
access_port2: "3",
|
||||
receiver_mac: "1a2c3d0e0191",
|
||||
receiver_ip: {192,168,123,1},
|
||||
sender_mac: "cec9bd531dfc",
|
||||
inside_local: {192,168,11,1},
|
||||
outside_local: {192,168,123,2},
|
||||
flow_pattern: :nat # :nat || :bum
|
||||
|
||||
config :tres,
|
||||
protocol: :tcp,
|
||||
port: 6633,
|
||||
max_connections: 10,
|
||||
num_acceptors: 10,
|
||||
callback_module: Heckle.Controller,
|
||||
callback_args: ["0002b05ada98d790"]
|
||||
|
||||
config :logger,
|
||||
level: :info,
|
||||
format: "$date $time [$level] $metadata$message\n",
|
||||
metadata: [:application],
|
||||
handle_otp_reports: true
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
defmodule Heckle do
|
||||
@moduledoc """
|
||||
Documentation for Heckle.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Hello world.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Heckle.hello
|
||||
:world
|
||||
|
||||
"""
|
||||
def hello do
|
||||
:world
|
||||
end
|
||||
end
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
defmodule Heckle.Application do
|
||||
# See https://hexdocs.pm/elixir/Application.html
|
||||
# for more information on OTP Applications
|
||||
@moduledoc false
|
||||
|
||||
use Application
|
||||
|
||||
def start(_type, _args) do
|
||||
# List all child processes to be supervised
|
||||
children = [
|
||||
# Starts a worker by calling: Heckle.Worker.start_link(arg)
|
||||
# {Heckle.Worker, arg},
|
||||
]
|
||||
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
# for other strategies and supported options
|
||||
opts = [strategy: :one_for_one, name: Heckle.Supervisor]
|
||||
Supervisor.start_link(children, opts)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
defmodule Heckle.Controller do
|
||||
use GenServer
|
||||
use Bitwise
|
||||
use Tres.Controller
|
||||
|
||||
import Logger
|
||||
|
||||
alias Heckle.PipelineProfiles
|
||||
alias Heckle.FlowPatterns
|
||||
|
||||
defmodule State do
|
||||
defstruct [
|
||||
dpid: nil,
|
||||
conn_ref: nil,
|
||||
access_port1_name: nil,
|
||||
access_port2_name: nil,
|
||||
trunk_port1_name: nil,
|
||||
trunk_port2_name: nil,
|
||||
access_port1: nil,
|
||||
access_port2: nil,
|
||||
trunk_port1: nil,
|
||||
trunk_port2: nil,
|
||||
vlan_tagging: nil,
|
||||
vlan_id: nil,
|
||||
receiver_mac: nil,
|
||||
receiver_ip: nil,
|
||||
sender_mac: nil,
|
||||
inside_local: nil,
|
||||
outside_local: nil,
|
||||
flow_pattern: nil
|
||||
]
|
||||
end
|
||||
|
||||
def start_link(dpid, args) do
|
||||
GenServer.start(__MODULE__, [dpid, args])
|
||||
end
|
||||
|
||||
def init([{dpid, _aux_id}, [dpid]]) do
|
||||
:ok = info("Switch Ready: dpid: #{inspect(dpid)} on #{inspect(self())}")
|
||||
state = init_state(dpid)
|
||||
{:ok, state}
|
||||
end
|
||||
def init([{dpid, _aux_id}, [_dpid]]) do
|
||||
:ok = info("Switch Ready: dpid: #{inspect(dpid)} but not acceptable")
|
||||
:ignore
|
||||
end
|
||||
|
||||
def handle_cast(:send_flows, state) do
|
||||
:ok = print_flows(state)
|
||||
|
||||
state
|
||||
|> FlowPatterns.flows
|
||||
|> Enum.each(&send_flow_mod_add(state.dpid, &1))
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(%Desc.Reply{mfr_desc: "Aruba"} = desc, %State{dpid: dpid} = state) do
|
||||
info("Switch Desc: mfr = #{desc.mfr_desc} hw = #{desc.hw_desc} sw = #{desc.sw_desc}")
|
||||
:ok = PipelineProfiles.of_aruba()
|
||||
|> TableFeatures.Request.new
|
||||
|> send_message(dpid)
|
||||
{:noreply, state}
|
||||
end
|
||||
def handle_info(%Desc.Reply{} = desc, state) do
|
||||
:ok = info("Switch Desc: mfr = #{desc.mfr_desc} hw = #{desc.hw_desc} sw = #{desc.sw_desc}")
|
||||
:ok = GenServer.cast(self(), :send_flows)
|
||||
{:noreply, state}
|
||||
end
|
||||
def handle_info(%TableFeatures.Reply{xid: xid}, state) do
|
||||
:ok = info("Pipeline modification is success (xid: #{xid})")
|
||||
:ok = GenServer.cast(self(), :send_flows)
|
||||
{:noreply, state}
|
||||
end
|
||||
def handle_info(%Flow.Reply{flows: flows}, state) do
|
||||
for flow <- flows do
|
||||
:ok = info(
|
||||
"table_id: #{flow.table_id} " <>
|
||||
"pkt_count: #{flow.packet_count} "<>
|
||||
"byt_count: #{flow.byte_count} "<>
|
||||
"match: #{inspect(flow.match)} "<>
|
||||
"insts: #{inspect(flow.instructions)} "
|
||||
)
|
||||
end
|
||||
{:noreply, state}
|
||||
end
|
||||
def handle_info(%PortDesc.Reply{ports: ports}, state) do
|
||||
info("Received Port Desc")
|
||||
access_port1 = Enum.find(ports, fn(port) -> port.name == state.access_port1_name end)
|
||||
access_port2 = Enum.find(ports, fn(port) -> port.name == state.access_port2_name end)
|
||||
trunk_port1 = Enum.find(ports, fn(port) -> port.name == state.trunk_port1_name end)
|
||||
trunk_port2 = Enum.find(ports, fn(port) -> port.name == state.trunk_port2_name end)
|
||||
:ok = desc_stats_request(state.dpid)
|
||||
{:noreply, %{state|
|
||||
access_port1: access_port1,
|
||||
access_port2: access_port2,
|
||||
trunk_port1: trunk_port1,
|
||||
trunk_port2: trunk_port2}
|
||||
}
|
||||
end
|
||||
def handle_info(%ErrorMsg{code: code, type: type, data: data, xid: xid}, state) do
|
||||
:ok = warn("Request Failed(xid: #{xid}):"<>
|
||||
" code: #{code}"<>
|
||||
" type: #{type}"<>
|
||||
" data: #{inspect(data)}"<>
|
||||
" dpid: #{inspect(state.dpid)}")
|
||||
{:stop, :request_failed, state}
|
||||
end
|
||||
def handle_info(%PacketIn{data: data}, state) do
|
||||
:ok = warn("Table miss occured:"<>
|
||||
" data: #{inspect(:pkt.decapsulate(data))}"<>
|
||||
" dpid: #{inspect(state.dpid)}")
|
||||
{:noreply, state}
|
||||
end
|
||||
def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do
|
||||
:ok = debug("Switch Disconnected: dpid: #{inspect(state.dpid)}")
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
def handle_info(info, state) do
|
||||
:ok = info("Unhandled message: #{inspect(info)}")
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# private functions
|
||||
defp init_state(dpid) do
|
||||
:ok = init_datapath(dpid)
|
||||
conn_ref = SwitchRegistry.monitor(dpid)
|
||||
config = Application.get_all_env(:heckle)
|
||||
%State{
|
||||
dpid: dpid,
|
||||
conn_ref: conn_ref,
|
||||
access_port1_name: config[:access_port1],
|
||||
access_port2_name: config[:access_port2],
|
||||
trunk_port1_name: config[:vlan_trunk1],
|
||||
trunk_port2_name: config[:vlan_trunk2],
|
||||
vlan_tagging: config[:vlan_tagging] || true,
|
||||
vlan_id: 0x1000 ||| (config[:vlan_id] || 0),
|
||||
receiver_mac: config[:receiver_mac],
|
||||
receiver_ip: config[:receiver_ip],
|
||||
sender_mac: config[:sender_mac],
|
||||
inside_local: config[:inside_local],
|
||||
outside_local: config[:outside_local],
|
||||
flow_pattern: config[:flow_pattern] || :nat
|
||||
}
|
||||
end
|
||||
|
||||
defp init_datapath(dpid) do
|
||||
:ok = send_flow_mod_delete(dpid)
|
||||
:ok = port_desc_stats_request(dpid)
|
||||
:ok = set_config(dpid)
|
||||
end
|
||||
|
||||
defp desc_stats_request(dpid) do
|
||||
:ok = send_message(Desc.Request.new, dpid)
|
||||
end
|
||||
|
||||
defp port_desc_stats_request(dpid) do
|
||||
:ok = send_message(PortDesc.Request.new, dpid)
|
||||
end
|
||||
|
||||
defp set_config(dpid) do
|
||||
:ok = send_message(SetConfig.new(miss_send_len: :no_buffer), dpid)
|
||||
end
|
||||
|
||||
defp print_flows(state) do
|
||||
state
|
||||
|> FlowPatterns.flows
|
||||
|> Enum.each(&print_flow/1)
|
||||
end
|
||||
|
||||
defp print_flow(flow_opts) do
|
||||
flow_opts
|
||||
|> FlowMod.new
|
||||
|> Openflow.to_binary
|
||||
|> binary_to_space_delimited_hex
|
||||
|> ofp_print_cmd
|
||||
|> Logger.info
|
||||
end
|
||||
|
||||
defp ofp_print_cmd(print_args) do
|
||||
{result, _code} = System.cmd("ovs-ofctl", ["ofp-print", "#{print_args}"])
|
||||
result
|
||||
end
|
||||
|
||||
defp binary_to_space_delimited_hex(binary) do
|
||||
binary
|
||||
|> split_to_hex_string
|
||||
|> Enum.join(" ")
|
||||
|> String.downcase
|
||||
end
|
||||
|
||||
defp split_to_hex_string(binary) do
|
||||
for <<int <- binary>>, do: integer_to_hex(int)
|
||||
end
|
||||
|
||||
defp integer_to_hex(int) do
|
||||
case Integer.to_string(int, 16) do
|
||||
<<d>> -> <<48, d>>
|
||||
dd -> dd
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
defmodule Heckle.FlowPatterns do
|
||||
use Bitwise
|
||||
use Tres.Controller
|
||||
|
||||
@mcast {"010000000000", "010000000000"}
|
||||
|
||||
def flows(%Heckle.Controller.State{flow_pattern: :nat} = state) do
|
||||
[[table_id: 1,
|
||||
priority: 30,
|
||||
cookie: 0x3000000000000001,
|
||||
match: Match.new(
|
||||
eth_dst: state.receiver_mac,
|
||||
eth_src: state.sender_mac,
|
||||
eth_type: 0x0800,
|
||||
ipv4_src: state.inside_local,
|
||||
ipv4_dst: state.receiver_ip
|
||||
),
|
||||
instructions: [
|
||||
ApplyActions.new([
|
||||
SetField.new({:eth_dst, state.receiver_mac}),
|
||||
SetField.new({:ipv4_src, state.outside_local}),
|
||||
Output.new(state.trunk_port2.number)
|
||||
])]
|
||||
],
|
||||
[table_id: 1,
|
||||
priority: 30,
|
||||
cookie: 0x3000000000000001,
|
||||
match: Match.new(
|
||||
eth_dst: state.sender_mac,
|
||||
eth_src: state.receiver_mac,
|
||||
eth_type: 0x0800,
|
||||
vlan_vid: 123,
|
||||
ipv4_src: state.receiver_ip,
|
||||
ipv4_dst: state.outside_local
|
||||
),
|
||||
instructions: [
|
||||
ApplyActions.new([
|
||||
PopVlan.new,
|
||||
SetField.new({:eth_src, state.receiver_mac}),
|
||||
SetField.new({:eth_dst, state.sender_mac}),
|
||||
SetField.new({:ipv4_dst, state.inside_local}),
|
||||
Output.new(state.access_port1.number)
|
||||
])]
|
||||
],
|
||||
] ++ classifier(state)
|
||||
end
|
||||
def flows(%Heckle.Controller.State{flow_pattern: :bum} = state) do
|
||||
[[table_id: 1,
|
||||
priority: 30,
|
||||
cookie: 0x3000000000000001,
|
||||
match: Match.new(
|
||||
vlan_vid: state.vlan_id,
|
||||
eth_dst: @mcast
|
||||
),
|
||||
instructions: [
|
||||
ApplyActions.new([
|
||||
Openflow.Action.Output.new(state.trunk_port1.number),
|
||||
Openflow.Action.PopVlan.new,
|
||||
Openflow.Action.Output.new(state.access_port1.number),
|
||||
Openflow.Action.Output.new(state.access_port2.number)
|
||||
])]
|
||||
]] ++ classifier(state)
|
||||
end
|
||||
|
||||
# private functions
|
||||
|
||||
defp classifier(state) do
|
||||
[
|
||||
[table_id: 0,
|
||||
priority: 50,
|
||||
cookie: 0x1000000000000001,
|
||||
match: Match.new(
|
||||
in_port: state.trunk_port1.number,
|
||||
vlan_vid: state.vlan_id
|
||||
),
|
||||
instructions: [GotoTable.new(1)]
|
||||
],
|
||||
[table_id: 0,
|
||||
priority: 50,
|
||||
cookie: 0x1000000000000001,
|
||||
match: Match.new(
|
||||
in_port: state.trunk_port2.number,
|
||||
vlan_vid: state.vlan_id
|
||||
),
|
||||
instructions: [GotoTable.new(1)]
|
||||
],
|
||||
[table_id: 0,
|
||||
priority: 20,
|
||||
cookie: 0x1000000000000001,
|
||||
match: Match.new(
|
||||
in_port: state.access_port1.number,
|
||||
eth_src: state.sender_mac,
|
||||
),
|
||||
instructions: [
|
||||
ApplyActions.new([
|
||||
PushVlan.new,
|
||||
SetField.new({:vlan_vid, state.vlan_id})
|
||||
]),
|
||||
GotoTable.new(1)
|
||||
]
|
||||
],
|
||||
[table_id: 0,
|
||||
priority: 20,
|
||||
cookie: 0x1000000000000001,
|
||||
match: Match.new(
|
||||
in_port: state.access_port2.number,
|
||||
eth_src: state.sender_mac,
|
||||
),
|
||||
instructions: [
|
||||
ApplyActions.new([
|
||||
PushVlan.new,
|
||||
SetField.new({:vlan_vid, state.vlan_id})
|
||||
]),
|
||||
GotoTable.new(1)
|
||||
]
|
||||
],
|
||||
[table_id: 0,
|
||||
priority: 1,
|
||||
match: Match.new,
|
||||
cookie: 0x1000000000000001,
|
||||
instructions: [ApplyActions.new(Output.new(:controller))]
|
||||
],
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
defmodule Heckle.PipelineProfiles do
|
||||
use Tres.Controller
|
||||
|
||||
def of_aruba do
|
||||
[
|
||||
TableFeatures.Body.new(
|
||||
table_id: 0,
|
||||
name: "classifier",
|
||||
max_entries: 10,
|
||||
config: [:table_miss_mask], # deprecated mask.
|
||||
match: [:in_port, :vlan_vid, :eth_src],
|
||||
wildcards: [:in_port, :vlan_vid, :eth_src],
|
||||
instructions: [GotoTable, ApplyActions],
|
||||
apply_actions: [Output, PushVlan, SetField, PopVlan],
|
||||
apply_setfield: [:eth_dst, :vlan_vid],
|
||||
next_tables: [1],
|
||||
),
|
||||
TableFeatures.Body.new(
|
||||
table_id: 1,
|
||||
name: "process",
|
||||
max_entries: 10,
|
||||
config: [:table_miss_mask],
|
||||
match: [:eth_src, :masked_eth_dst, :vlan_vid, :eth_type, :ipv4_src, :ipv4_dst],
|
||||
wildcards: [:eth_src, :masked_eth_dst, :vlan_vid, :eth_type, :ipv4_src, :ipv4_dst],
|
||||
instructions: [ApplyActions],
|
||||
apply_actions: [SetField, PopVlan, PushVlan, Output],
|
||||
apply_setfield: [:eth_src, :eth_dst, :vlan_vid, :ipv4_src, :ipv4_dst],
|
||||
next_tables: [],
|
||||
)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
defmodule Heckle.Mixfile do
|
||||
use Mix.Project
|
||||
|
||||
@tres_path "../../../tres"
|
||||
|
||||
def project do
|
||||
[app: :heckle,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.5",
|
||||
start_permanent: Mix.env == :prod,
|
||||
deps: deps()]
|
||||
end
|
||||
|
||||
def application do
|
||||
[extra_applications: [:logger, :tres],
|
||||
mod: {Heckle.Application, []}]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[{:tres, path: @tres_path},
|
||||
{:pkt, github: "msantos/pkt", branch: "master"}]
|
||||
end
|
||||
end
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
%{"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"]},
|
||||
"pkt": {:git, "https://github.com/msantos/pkt.git", "3afb1967f34324c1dec5035a6e36232da815c2e6", [branch: "master"]},
|
||||
"ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"},
|
||||
"uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
defmodule HeckleTest do
|
||||
use ExUnit.Case
|
||||
doctest Heckle
|
||||
|
||||
test "greets the world" do
|
||||
assert Heckle.hello() == :world
|
||||
end
|
||||
end
|
||||
|
|
@ -1 +0,0 @@
|
|||
ExUnit.start()
|
||||
Loading…
Add table
Add a link
Reference in a new issue