Implement Openflow Protocol and Callback system
This commit is contained in:
parent
fc02a678de
commit
e52fe31b79
48 changed files with 937 additions and 244 deletions
|
|
@ -8,8 +8,9 @@ defmodule Tres.Application do
|
|||
def start(_type, _args) do
|
||||
import Supervisor.Spec
|
||||
|
||||
{cb_mod, _cb_args} = Tres.Utils.get_callback_module
|
||||
children = [worker(Registry, [[keys: :unique, name: SwitchRegistry]], id: SwitchRegistry),
|
||||
supervisor(Tres.MessageHandlerSup, [], id: MessageHandlerSup)]
|
||||
supervisor(Tres.MessageHandlerSup, [cb_mod], id: MessageHandlerSup)]
|
||||
opts = [strategy: :one_for_one, name: Tres.Supervisor]
|
||||
{:ok, _connection_pid} = Tres.Utils.start_openflow_listener
|
||||
Supervisor.start_link(children, opts)
|
||||
|
|
|
|||
14
lib/tres/controller.ex
Normal file
14
lib/tres/controller.ex
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
defmodule Tres.Controller do
|
||||
def controller_helpers do
|
||||
quote do
|
||||
import Tres.SwitchRegistry, only: [send_message: 2]
|
||||
|
||||
use Tres.Messages
|
||||
use Tres.MessageHelper
|
||||
end
|
||||
end
|
||||
|
||||
defmacro __using__(_) do
|
||||
controller_helpers()
|
||||
end
|
||||
end
|
||||
108
lib/tres/example_handler.ex
Normal file
108
lib/tres/example_handler.ex
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
defmodule Tres.ExampleHandler do
|
||||
use GenServer
|
||||
use Tres.Controller
|
||||
|
||||
import Logger
|
||||
|
||||
defmodule State do
|
||||
defstruct [
|
||||
datapath_id: nil,
|
||||
aux_id: nil,
|
||||
conn_ref: nil
|
||||
]
|
||||
end
|
||||
|
||||
def start_link(datapath, args) do
|
||||
GenServer.start_link(__MODULE__, [datapath, args])
|
||||
end
|
||||
|
||||
def init([{datapath_id, aux_id}, _args]) do
|
||||
info("[#{__MODULE__}] Switch Ready: "
|
||||
<> "datapath_id: #{datapath_id} "
|
||||
<> "aux_id: #{aux_id} "
|
||||
<> "in #{inspect(self())}")
|
||||
_ = send_flows_for_test(datapath_id)
|
||||
_ = send_flow_stats_request(datapath_id)
|
||||
_ = send_desc_stats_request(datapath_id)
|
||||
_ = send_port_desc_stats_request(datapath_id)
|
||||
conn_ref = SwitchRegistry.monitor(datapath_id)
|
||||
state = %State{datapath_id: datapath_id, aux_id: aux_id, conn_ref: conn_ref}
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
def handle_info(%Flow.Reply{datapath_id: datapath_id} = desc, state) do
|
||||
handle_flow_stats_reply(desc, datapath_id)
|
||||
{:noreply, state}
|
||||
end
|
||||
def handle_info(%PortDesc.Reply{datapath_id: datapath_id} = desc, state) do
|
||||
handle_port_desc_stats_reply(desc, datapath_id)
|
||||
{:noreply, state}
|
||||
end
|
||||
def handle_info(%Desc.Reply{datapath_id: datapath_id} = desc, state) do
|
||||
handle_desc_stats_reply(desc, datapath_id)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# To prevent process leakage, following section is required.
|
||||
def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do
|
||||
:ok = warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id}")
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
|
||||
# `Catch all` function is required.
|
||||
def handle_info(info, state) do
|
||||
:ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}: #{state.datapath_id}")
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# private functions
|
||||
|
||||
defp send_flows_for_test(datapath_id) do
|
||||
for count <- Range.new(1, 1024) do
|
||||
send_flow_mod_add(datapath_id, match: Match.new(metadata: count))
|
||||
end
|
||||
end
|
||||
|
||||
defp send_flow_stats_request(datapath_id) do
|
||||
Flow.Request.new
|
||||
|> send_message(datapath_id)
|
||||
end
|
||||
|
||||
defp send_desc_stats_request(datapath_id) do
|
||||
Desc.Request.new
|
||||
|> send_message(datapath_id)
|
||||
end
|
||||
|
||||
defp send_port_desc_stats_request(datapath_id) do
|
||||
PortDesc.Request.new
|
||||
|> send_message(datapath_id)
|
||||
end
|
||||
|
||||
defp handle_flow_stats_reply(desc, datapath_id) do
|
||||
info("[#{__MODULE__}] Switch #{length(desc.flows)} installed on #{datapath_id}")
|
||||
end
|
||||
|
||||
defp handle_desc_stats_reply(desc, datapath_id) do
|
||||
info(
|
||||
"[#{__MODULE__}] Switch Desc: "
|
||||
<> "mfr = #{desc.mfr_desc} "
|
||||
<> "hw = #{desc.hw_desc} "
|
||||
<> "sw = #{desc.sw_desc} "
|
||||
<> "for #{datapath_id}"
|
||||
)
|
||||
end
|
||||
|
||||
defp handle_port_desc_stats_reply(port_desc, datapath_id) do
|
||||
for port <- port_desc.ports do
|
||||
info(
|
||||
"[#{__MODULE__}] Switch has port: "
|
||||
<> "number = #{port.number} "
|
||||
<> "hw_addr = #{port.hw_addr} "
|
||||
<> "name = #{port.name} "
|
||||
<> "config = #{inspect(port.config)} "
|
||||
<> "current_speed = #{port.current_speed} "
|
||||
<> "on #{datapath_id}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
defmodule Tres.MessageHandler do
|
||||
use GenServer
|
||||
|
||||
defmodule State do
|
||||
defstruct [
|
||||
ip_addr: nil,
|
||||
port: nil,
|
||||
datapath_id: nil,
|
||||
conn_pid: nil,
|
||||
conn_ref: nil,
|
||||
handler_pid: nil,
|
||||
handler_ref: nil
|
||||
]
|
||||
end
|
||||
|
||||
alias Tres.MessageHandler.State
|
||||
|
||||
@process_flags [trap_exit: true]
|
||||
|
||||
def start_link({ip_addr, port}, conn_pid) do
|
||||
GenServer.start_link(__MODULE__, [{ip_addr, port}, conn_pid])
|
||||
end
|
||||
|
||||
def init([{ip_addr, port}, conn_pid]) do
|
||||
init_process()
|
||||
conn_ref = Process.monitor(conn_pid)
|
||||
state = %State{
|
||||
conn_pid: conn_pid,
|
||||
conn_ref: conn_ref,
|
||||
ip_addr: ip_addr,
|
||||
port: port
|
||||
}
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
def handle_info({:'EXIT', _pid, _reason}, state) do
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
def handle_info({:'DOWN', conn_ref, :process, _conn_pid, _reason}, %State{conn_ref: conn_ref} = state) do
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
|
||||
def terminate(_reason, state) do
|
||||
{:shutdown, state}
|
||||
end
|
||||
|
||||
## private functions
|
||||
|
||||
defp init_process do
|
||||
for {flag, value} <- @process_flags, do: Process.flag(flag, value)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
defmodule Tres.MessageHandlerSup do
|
||||
use Supervisor
|
||||
|
||||
def start_link do
|
||||
Supervisor.start_link(__MODULE__, [], name: __MODULE__)
|
||||
def start_link(cb_mod) do
|
||||
Supervisor.start_link(__MODULE__, [cb_mod], name: __MODULE__)
|
||||
end
|
||||
|
||||
def init(_) do
|
||||
children = [worker(Tres.MessageHandler, [], restart: :temporary)]
|
||||
def init([cb_mod]) do
|
||||
children = [worker(cb_mod, [], restart: :temporary, shutdown: 5000)]
|
||||
supervise(children, strategy: :simple_one_for_one)
|
||||
end
|
||||
|
||||
def start_child({ip_addr, port}) do
|
||||
Supervisor.start_child(__MODULE__, [{ip_addr, port}, self()])
|
||||
def start_child({dpid, aux_id}) do
|
||||
{_cb_mod, cb_args} = Tres.Utils.get_callback_module
|
||||
Supervisor.start_child(__MODULE__, [{dpid, aux_id}, cb_args])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
88
lib/tres/message_helper.ex
Normal file
88
lib/tres/message_helper.ex
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
defmodule Tres.MessageHelper do
|
||||
defmacro __using__(_) do
|
||||
quote location: :keep do
|
||||
defp send_flow_mod_add(datapath_id, options) do
|
||||
flow_mod = %Openflow.FlowMod{
|
||||
cookie: options[:cookie] || 0,
|
||||
priority: options[:priority] || 0,
|
||||
table_id: options[:table_id] || 0,
|
||||
command: :add,
|
||||
idle_timeout: options[:idle_timeout] || 0,
|
||||
hard_timeout: options[:hard_timeout] || 0,
|
||||
buffer_id: :no_buffer,
|
||||
out_port: :any,
|
||||
out_group: :any,
|
||||
flags: options[:flags] || [],
|
||||
match: options[:match] || Openflow.Match.new,
|
||||
instructions: options[:instructions] || [],
|
||||
}
|
||||
send_message(flow_mod, datapath_id)
|
||||
end
|
||||
|
||||
defp send_flow_mod_modify(datapath_id, options) do
|
||||
command = Tres.Utils.flow_command(options, :modify)
|
||||
flow_mod = %Openflow.FlowMod{
|
||||
cookie: options[:cookie] || 0,
|
||||
table_id: options[:table_id] || 0,
|
||||
command: command,
|
||||
idle_timeout: options[:idle_timeout] || 0,
|
||||
hard_timeout: options[:hard_timeout] || 0,
|
||||
out_port: :any,
|
||||
out_group: :any,
|
||||
match: options[:match] || Openflow.Match.new,
|
||||
instructions: options[:instructions] || [],
|
||||
}
|
||||
send_message(flow_mod, datapath_id)
|
||||
end
|
||||
|
||||
defp send_flow_mod_delete(datapath_id, options) do
|
||||
command = Tres.Utils.flow_command(options, :delete)
|
||||
flow_mod = %Openflow.FlowMod{
|
||||
cookie: options[:cookie] || 0,
|
||||
cookie_mask: options[:cookie_mask] || 0,
|
||||
table_id: options[:table_id] || :all,
|
||||
command: command,
|
||||
out_port: options[:out_port] || :any,
|
||||
out_group: options[:out_group] || :any,
|
||||
match: options[:match] || Openflow.Match.new
|
||||
}
|
||||
send_message(flow_mod, datapath_id)
|
||||
end
|
||||
|
||||
defp send_packet_out(datapath_id, options) do
|
||||
packet_out = %Openflow.PacketOut{
|
||||
buffer_id: options[:buffer_id] || :no_buffer,
|
||||
in_port: options[:in_port] || :controller,
|
||||
actions: options[:actions] || [],
|
||||
data: options[:data] || ""
|
||||
}
|
||||
send_message(packet_out, datapath_id)
|
||||
end
|
||||
|
||||
defp send_group_mod_add(datapath_id, options) do
|
||||
group_mod = Openflow.GroupMod.new(
|
||||
command: :add,
|
||||
type: options[:type] || :all,
|
||||
group_id: options[:group_id] || 0,
|
||||
buckets: options[:buckets] || []
|
||||
)
|
||||
send_message(group_mod, datapath_id)
|
||||
end
|
||||
|
||||
defp send_group_mod_delete(datapath_id, group_id) do
|
||||
group_mod = Openflow.GroupMod.new(command: :delete, group_id: group_id)
|
||||
send_message(group_mod, datapath_id)
|
||||
end
|
||||
|
||||
defp send_group_mod_modify(datapath_id, options) do
|
||||
group_mod = Openflow.GroupMod.new(
|
||||
command: :modify,
|
||||
type: options[:type] || :all,
|
||||
group_id: options[:group_id] || 0,
|
||||
buckets: options[:buckets] || []
|
||||
)
|
||||
send_message(group_mod, datapath_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
128
lib/tres/messages.ex
Normal file
128
lib/tres/messages.ex
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
defmodule Tres.Messages do
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
alias Openflow.ErrorMsg
|
||||
alias Openflow.Echo
|
||||
alias Openflow.Features
|
||||
alias Openflow.GetConfig
|
||||
alias Openflow.SetConfig
|
||||
alias Openflow.PacketIn
|
||||
alias Openflow.FlowRemoved
|
||||
alias Openflow.PortStatus
|
||||
alias Openflow.PacketOut
|
||||
alias Openflow.FlowMod
|
||||
alias Openflow.GroupMod
|
||||
alias Openflow.PortMod
|
||||
alias Openflow.TableMod
|
||||
alias Openflow.Multipart
|
||||
alias Openflow.Barrier
|
||||
alias Openflow.Role
|
||||
alias Openflow.GetAsync
|
||||
alias Openflow.SetAsync
|
||||
alias Openflow.MeterMod
|
||||
|
||||
alias Openflow.Match
|
||||
alias Openflow.Port
|
||||
|
||||
alias Openflow.NxSetPacketInFormat
|
||||
alias Openflow.NxSetControllerId
|
||||
alias Openflow.NxPacketIn2
|
||||
|
||||
alias Openflow.Multipart.Desc
|
||||
alias Openflow.Multipart.Flow
|
||||
alias Openflow.Multipart.Aggregate
|
||||
alias Openflow.Multipart.Table
|
||||
alias Openflow.Multipart.PortStats
|
||||
alias Openflow.Multipart.Queue
|
||||
alias Openflow.Multipart.Group
|
||||
alias Openflow.Multipart.GroupDesc
|
||||
alias Openflow.Multipart.GroupFeatures
|
||||
alias Openflow.Multipart.Meter
|
||||
alias Openflow.Multipart.MeterConfig
|
||||
alias Openflow.Multipart.MeterFeatures
|
||||
alias Openflow.Multipart.TableFeatures
|
||||
alias Openflow.Multipart.PortDesc
|
||||
|
||||
alias Openflow.Instruction.GotoTable
|
||||
alias Openflow.Instruction.WriteMetadata
|
||||
alias Openflow.Instruction.WriteActions
|
||||
alias Openflow.Instruction.ApplyActions
|
||||
alias Openflow.Instruction.ClearActions
|
||||
alias Openflow.Instruction.Meter
|
||||
|
||||
alias Openflow.Action.Output
|
||||
alias Openflow.Action.CopyTtlOut
|
||||
alias Openflow.Action.CopyTtlIn
|
||||
alias Openflow.Action.SetMplsTtl
|
||||
alias Openflow.Action.DecMplsTtl
|
||||
alias Openflow.Action.PushVlan
|
||||
alias Openflow.Action.PopVlan
|
||||
alias Openflow.Action.PushMpls
|
||||
alias Openflow.Action.PopMpls
|
||||
alias Openflow.Action.SetQueue
|
||||
alias Openflow.Action.Group
|
||||
alias Openflow.Action.SetNwTtl
|
||||
alias Openflow.Action.DecNwTtl
|
||||
alias Openflow.Action.SetField
|
||||
alias Openflow.Action.PushPbb
|
||||
alias Openflow.Action.PopPbb
|
||||
alias Openflow.Action.Encap
|
||||
alias Openflow.Action.Decap
|
||||
alias Openflow.Action.SetSequence
|
||||
alias Openflow.Action.ValidateSequence
|
||||
|
||||
alias Openflow.Action.NxResubmit
|
||||
alias Openflow.Action.NxSetTunnel
|
||||
alias Openflow.Action.NxSetQueue
|
||||
alias Openflow.Action.NxPopQueue
|
||||
alias Openflow.Action.NxRegMove
|
||||
alias Openflow.Action.NxRegLoad
|
||||
alias Openflow.Action.NxNote
|
||||
alias Openflow.Action.NxSetTunnel64
|
||||
alias Openflow.Action.NxMultipath
|
||||
alias Openflow.Action.NxBundle
|
||||
alias Openflow.Action.NxBundleLoad
|
||||
alias Openflow.Action.NxResubmitTable
|
||||
alias Openflow.Action.NxOutputReg
|
||||
alias Openflow.Action.NxLearn
|
||||
alias Openflow.Action.NxExit
|
||||
alias Openflow.Action.NxDecTtl
|
||||
alias Openflow.Action.NxFinTimeout
|
||||
alias Openflow.Action.NxController
|
||||
alias Openflow.Action.NxDecTtlCntIds
|
||||
alias Openflow.Action.NxWriteMetadata
|
||||
alias Openflow.Action.NxPushMpls
|
||||
alias Openflow.Action.NxPopMpls
|
||||
alias Openflow.Action.NxSetMplsTtl
|
||||
alias Openflow.Action.NxDecMplsTtl
|
||||
alias Openflow.Action.NxStackPush
|
||||
alias Openflow.Action.NxStackPop
|
||||
alias Openflow.Action.NxSample
|
||||
alias Openflow.Action.NxSetMplsLabel
|
||||
alias Openflow.Action.NxSetMplsTc
|
||||
alias Openflow.Action.NxOutputReg2
|
||||
alias Openflow.Action.NxRegLoad2
|
||||
alias Openflow.Action.NxConjunction
|
||||
alias Openflow.Action.NxConntrack
|
||||
alias Openflow.Action.NxNat
|
||||
alias Openflow.Action.NxController2
|
||||
alias Openflow.Action.NxSample2
|
||||
alias Openflow.Action.NxOutputTrunc
|
||||
alias Openflow.Action.NxGroup
|
||||
alias Openflow.Action.NxSample3
|
||||
alias Openflow.Action.NxClone
|
||||
alias Openflow.Action.NxCtClear
|
||||
alias Openflow.Action.NxResubmitTableCt
|
||||
alias Openflow.Action.NxLearn2
|
||||
alias Openflow.Action.NxEncap
|
||||
alias Openflow.Action.NxDecap
|
||||
alias Openflow.Action.NxDebugRecirc
|
||||
|
||||
alias Openflow.Action.NxFlowSpecMatch
|
||||
alias Openflow.Action.NxFlowSpecLoad
|
||||
alias Openflow.Action.NxFlowSpecOutput
|
||||
|
||||
alias Tres.SwitchRegistry
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,7 @@ defmodule Tres.SecureChannel do
|
|||
|
||||
import Logger
|
||||
|
||||
alias :tres_xact_kv, as: XACT_KV
|
||||
alias Tres.SecureChannelState
|
||||
alias Tres.SwitchRegistry
|
||||
alias Tres.MessageHandlerSup
|
||||
|
|
@ -32,12 +33,11 @@ defmodule Tres.SecureChannel do
|
|||
end
|
||||
|
||||
def init([ref, socket, transport, _opts]) do
|
||||
state_data =
|
||||
ref
|
||||
|> init_secure_channel(socket, transport)
|
||||
|> init_handler
|
||||
info("[#{__MODULE__}] TCP connected to Switch on #{state_data.ip_addr}:#{state_data.port} on #{inspect(self())}")
|
||||
:gen_statem.enter_loop(__MODULE__, [debug: []], :INIT, state_data, [])
|
||||
state_data = init_secure_channel(ref, socket, transport)
|
||||
debug("[#{__MODULE__}] TCP connected to Switch on"
|
||||
<> " #{state_data.ip_addr}:#{state_data.port}"
|
||||
<> " on #{inspect(self())}")
|
||||
:gen_statem.enter_loop(__MODULE__, [debug: [:debug]], :INIT, state_data, [])
|
||||
end
|
||||
|
||||
# TCP handler
|
||||
|
|
@ -73,8 +73,9 @@ defmodule Tres.SecureChannel do
|
|||
handle_WATING(type, message, state_data)
|
||||
end
|
||||
|
||||
def terminate(reason, state, %SecureChannelState{datapath_id: datapath_id, aux_id: aux_id}) do
|
||||
def terminate(reason, state, %SecureChannelState{datapath_id: datapath_id, aux_id: aux_id, xact_kv_ref: kv_ref}) do
|
||||
warn("[#{__MODULE__}] termiate: #{inspect(reason)} state = #{inspect(state)}")
|
||||
true = XACT_KV.drop(kv_ref)
|
||||
:ok = SwitchRegistry.unregister({datapath_id, aux_id})
|
||||
end
|
||||
|
||||
|
|
@ -83,7 +84,8 @@ defmodule Tres.SecureChannel do
|
|||
defp init_secure_channel(ref, socket, transport) do
|
||||
init_process(ref)
|
||||
:ok = transport.setopts(socket, [active: :once])
|
||||
SecureChannelState.new(ref: ref, socket: socket, transport: transport)
|
||||
kv_ref = XACT_KV.create
|
||||
SecureChannelState.new(ref: ref, socket: socket, transport: transport, xact_kv_ref: kv_ref)
|
||||
end
|
||||
|
||||
defp init_process(ref) do
|
||||
|
|
@ -93,8 +95,8 @@ defmodule Tres.SecureChannel do
|
|||
end
|
||||
|
||||
defp init_handler(state_data) do
|
||||
%SecureChannelState{ip_addr: ip_addr, port: port} = state_data
|
||||
{:ok, pid} = MessageHandlerSup.start_child({ip_addr, port})
|
||||
%SecureChannelState{datapath_id: dpid, aux_id: aux_id} = state_data
|
||||
{:ok, pid} = MessageHandlerSup.start_child({dpid, aux_id})
|
||||
ref = Process.monitor(pid)
|
||||
%{state_data|handler_pid: pid, handler_ref: ref}
|
||||
end
|
||||
|
|
@ -128,8 +130,7 @@ defmodule Tres.SecureChannel do
|
|||
close_connection(:features_handshake_timeout, state_data)
|
||||
end
|
||||
defp handle_CONNECTING(:internal, {:openflow, %Openflow.Features.Reply{} = features}, state_data) do
|
||||
# TODO: Send to handler
|
||||
info("[#{__MODULE__}] Switch connected datapath_id: #{features.datapath_id} auxiliary_id: #{features.aux_id}")
|
||||
debug("[#{__MODULE__}] Switch connected datapath_id: #{features.datapath_id} auxiliary_id: #{features.aux_id}")
|
||||
_ = maybe_cancel_timer(state_data.timer_ref)
|
||||
handle_features_handshake(features, state_data)
|
||||
end
|
||||
|
|
@ -142,9 +143,10 @@ defmodule Tres.SecureChannel do
|
|||
end
|
||||
|
||||
# CONNECTED state
|
||||
defp handle_CONNECTED(:enter, :CONNECTING, _state_data) do
|
||||
defp handle_CONNECTED(:enter, :CONNECTING, state_data) do
|
||||
new_state_data = init_handler(state_data)
|
||||
start_periodic_idle_check()
|
||||
:keep_state_and_data
|
||||
{:keep_state, new_state_data}
|
||||
end
|
||||
defp handle_CONNECTED(:info, :idle_check, state_data) do
|
||||
start_periodic_idle_check()
|
||||
|
|
@ -162,19 +164,48 @@ defmodule Tres.SecureChannel do
|
|||
send_echo_reply(echo.xid, echo.data, state_data)
|
||||
:keep_state_and_data
|
||||
end
|
||||
defp handle_CONNECTED(:internal, {:openflow, _message}, _state_data) do
|
||||
# TODO: Send to handler
|
||||
defp handle_CONNECTED(:internal, {:openflow, %Openflow.Barrier.Reply{xid: xid}}, state_data) do
|
||||
for {:xact_entry, _xid, message, _orig} <- XACT_KV.get(state_data.xact_kv_ref, xid) do
|
||||
unless is_nil(message) do
|
||||
send(state_data.handler_pid, message)
|
||||
XACT_KV.delete(state_data.xact_kv_ref, message.xid)
|
||||
end
|
||||
end
|
||||
:keep_state_and_data
|
||||
end
|
||||
defp handle_CONNECTED(:internal, {:openflow, message}, state_data) do
|
||||
%SecureChannelState{datapath_id: dpid, aux_id: aux_id} = state_data
|
||||
new_message = %{message|datapath_id: dpid, aux_id: aux_id}
|
||||
state_data.xact_kv_ref
|
||||
|> XACT_KV.is_exists(message.xid)
|
||||
|> handle_message(new_message, state_data)
|
||||
:keep_state_and_data
|
||||
end
|
||||
defp handle_CONNECTED(:cast, {:send_message, message}, state_data) do
|
||||
message
|
||||
|> send_message(state_data)
|
||||
xid = SecureChannelState.increment_transaction_id(state_data.xid)
|
||||
messages = [
|
||||
%{message|xid: xid},
|
||||
%{Openflow.Barrier.Request.new|xid: xid}
|
||||
]
|
||||
XACT_KV.insert(state_data.xact_kv_ref, xid, message)
|
||||
send_message(messages, state_data)
|
||||
:keep_state_and_data
|
||||
end
|
||||
|
||||
defp handle_message(_in_xact = true, message, state_data) do
|
||||
[{:xact_entry, _xid, prev_message, _orig}|_] = XACT_KV.get(state_data.xact_kv_ref, message.xid)
|
||||
new_message = Openflow.append_body(prev_message, message)
|
||||
XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message)
|
||||
end
|
||||
defp handle_message(_in_xact = false, message, %SecureChannelState{handler_pid: handler_pid}) do
|
||||
send(handler_pid, message)
|
||||
end
|
||||
|
||||
# WATING state
|
||||
defp handle_WATING(:enter, :CONNECTING, state_data) do
|
||||
warn("[#{__MODULE__}] Possible HANG Detected on datapath_id: #{state_data.datapath_id} !")
|
||||
%SecureChannelState{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data
|
||||
send(handler_pid, {:switch_hang, {dpid, aux_id}})
|
||||
start_periodic_idle_check()
|
||||
:keep_state_and_data
|
||||
end
|
||||
|
|
@ -186,8 +217,9 @@ defmodule Tres.SecureChannel do
|
|||
defp handle_WATING(:info, :ping_timeout, state_data) do
|
||||
handle_ping_timeout(state_data, :WAITING)
|
||||
end
|
||||
defp handle_WATING(:internal, {:openflow, _message}, state_data) do
|
||||
# TODO: Send to handler
|
||||
defp handle_WATING(:internal, {:openflow, message}, state_data) do
|
||||
%SecureChannelState{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data
|
||||
send(handler_pid, %{message|datapath_id: dpid, aux_id: aux_id})
|
||||
{:next_state, :CONNECTING, state_data}
|
||||
end
|
||||
defp handle_WATING(type, message, state_data)
|
||||
|
|
@ -249,12 +281,10 @@ defmodule Tres.SecureChannel do
|
|||
{:next_state, :CONNECTED, new_state_data}
|
||||
end
|
||||
|
||||
defp monitor_connection(datapath_id, aux_id) when aux_id > 0 do
|
||||
datapath_id
|
||||
|> SwitchRegistry.lookup_pid
|
||||
|> Process.monitor
|
||||
end
|
||||
defp monitor_connection(_datapath_id, _aux_id), do: nil
|
||||
defp monitor_connection(datapath_id, aux_id) when aux_id > 0,
|
||||
do: SwitchRegistry.monitor(datapath_id)
|
||||
defp monitor_connection(_datapath_id, _aux_id),
|
||||
do: nil
|
||||
|
||||
defp send_hello(state_data) do
|
||||
@supported_version
|
||||
|
|
@ -320,7 +350,7 @@ defmodule Tres.SecureChannel do
|
|||
end
|
||||
|
||||
defp send_message(message, %SecureChannelState{socket: socket, transport: transport}) do
|
||||
debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})")
|
||||
#debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})")
|
||||
Tres.Utils.send_message(message, socket, transport)
|
||||
end
|
||||
|
||||
|
|
@ -371,7 +401,7 @@ defmodule Tres.SecureChannel do
|
|||
{:stop, :normal, %{state_data|socket: nil}}
|
||||
end
|
||||
defp close_connection({:trap_detected, reason}, state_data) do
|
||||
warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}")
|
||||
warn("[#{__MODULE__}] connection terminated: Trapped by #{reason}")
|
||||
{:stop, :normal, %{state_data|socket: nil}}
|
||||
end
|
||||
defp close_connection(:tcp_closed, state_data) do
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@ defmodule Tres.SecureChannelState do
|
|||
ping_xid: 0,
|
||||
ping_timer_ref: nil,
|
||||
ping_fail_count: 0,
|
||||
last_received: 0
|
||||
last_received: 0,
|
||||
xact_kv_ref: nil
|
||||
)
|
||||
|
||||
alias __MODULE__
|
||||
alias :tres_xact_kv, as: XACT_KV
|
||||
|
||||
def new(options) do
|
||||
ref = Keyword.get(options, :ref)
|
||||
|
|
@ -27,13 +29,15 @@ defmodule Tres.SecureChannelState do
|
|||
transport = Keyword.get(options, :transport)
|
||||
{:ok, {ip_addr, port}} = :inet.peername(socket)
|
||||
{:ok, xid_agent} = Agent.start_link(fn -> 0 end)
|
||||
kv_ref = XACT_KV.create
|
||||
%SecureChannelState{
|
||||
ref: ref,
|
||||
socket: socket,
|
||||
transport: transport,
|
||||
ip_addr: :inet.ntoa(ip_addr),
|
||||
port: port,
|
||||
xid: xid_agent
|
||||
xid: xid_agent,
|
||||
xact_kv_ref: kv_ref
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ defmodule Tres.SwitchRegistry do
|
|||
send_message(message, {dpid, 0})
|
||||
end
|
||||
|
||||
def monitor(datapath_id) do
|
||||
datapath_id
|
||||
|> lookup_pid
|
||||
|> Process.monitor
|
||||
end
|
||||
|
||||
# private function
|
||||
|
||||
defp dispatch(entries, message) do
|
||||
|
|
|
|||
|
|
@ -6,10 +6,16 @@ defmodule Tres.Utils do
|
|||
@default_num_acceptors 10
|
||||
@default_openflow_port 6633
|
||||
|
||||
def get_callback_module do
|
||||
cb_mod = get_config(:callback_module, Tres.ExampleHandler)
|
||||
cb_args = get_config(:callback_args, [])
|
||||
{cb_mod, cb_args}
|
||||
end
|
||||
|
||||
def start_openflow_listener do
|
||||
max_connections = Application.get_env(:tres, :max_connections, @default_max_connections)
|
||||
num_acceptors = Application.get_env(:tres, :num_acceptors, @default_num_acceptors)
|
||||
port = Application.get_env(:tres, :port, @default_openflow_port)
|
||||
max_connections = get_config(:max_connections, @default_max_connections)
|
||||
num_acceptors = get_config(:num_acceptors, @default_num_acceptors)
|
||||
port = get_config(:port, @default_openflow_port)
|
||||
options = [max_connections: max_connections, num_acceptors: num_acceptors, port: port]
|
||||
:ranch.start_listener(Tres, :ranch_tcp, options, @connection_manager, [])
|
||||
end
|
||||
|
|
@ -23,4 +29,31 @@ defmodule Tres.Utils do
|
|||
error("[#{__MODULE__}] Unencodable error: #{inspect(message)}")
|
||||
end
|
||||
end
|
||||
|
||||
def is_multipart?(message) do
|
||||
message.__struct__
|
||||
|> Module.split
|
||||
|> Enum.at(1)
|
||||
|> String.match?(~r/Multipart/)
|
||||
end
|
||||
|
||||
def flow_command(:delete, options) do
|
||||
if Keyword.get(options, :strict, false) do
|
||||
:delete_strict
|
||||
else
|
||||
:delete
|
||||
end
|
||||
end
|
||||
def flow_command(:modify, options) do
|
||||
if Keyword.get(options, :strict, false) do
|
||||
:modify_strict
|
||||
else
|
||||
:modify
|
||||
end
|
||||
end
|
||||
|
||||
# private functions
|
||||
defp get_config(item, default) do
|
||||
Application.get_env(:tres, item, default)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue