Implement Openflow Protocol and Callback system

This commit is contained in:
Eishun Kondoh 2017-11-20 13:38:24 +09:00
parent fc02a678de
commit e52fe31b79
48 changed files with 937 additions and 244 deletions

View file

@ -1 +0,0 @@
shun159@shun159.5674:1510580208

View file

@ -14,15 +14,12 @@ defmodule Openflow.Action.NxBundle do
alias __MODULE__
def new(options) do
hash_field = Keyword.get(options, :hash_field, :eth_src)
basis = Keyword.get(options, :basis, 0)
alg = Keyword.get(options, :algorithm, :active_backup)
slaves = Keyword.get(options, :slaves, [])
%NxBundle{algorithm: alg,
hash_field: hash_field,
basis: basis,
n_slaves: length(slaves),
slaves: slaves}
slaves = options[:slaves] || []
%NxBundle{algorithm: options[:algorithm] || :active_backup,
hash_field: options[:hash_field] || :eth_src,
basis: options[:basis] || 0,
n_slaves: length(slaves),
slaves: slaves}
end
def to_binary(%NxBundle{algorithm: alg,

View file

@ -19,22 +19,17 @@ defmodule Openflow.Action.NxBundleLoad do
alias __MODULE__
def new(options) do
hash_field = Keyword.get(options, :hash_field, :eth_src)
basis = Keyword.get(options, :basis, 0)
alg = Keyword.get(options, :algorithm, :active_backup)
slaves = Keyword.get(options, :slaves, [])
dst_field = Keyword.get(options, :dst_field)
dst_field = options[:dst_field]
default_n_bits = Openflow.Match.Field.n_bits_of(dst_field)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
ofs = Keyword.get(options, :offset, 0)
%NxBundleLoad{algorithm: alg,
hash_field: hash_field,
basis: basis,
n_slaves: length(slaves),
slaves: slaves,
offset: ofs,
n_bits: n_bits,
dst_field: dst_field}
slaves = options[:slaves] || []
%NxBundleLoad{algorithm: options[:algorithm] || :active_backup,
hash_field: options[:hash_field] || :eth_src,
basis: options[:basis] || 0,
n_slaves: length(slaves),
slaves: slaves,
offset: options[:offset] || 0,
n_bits: options[:n_bits] || default_n_bits,
dst_field: options[:dst_field]}
end
def to_binary(%NxBundleLoad{algorithm: alg,

View file

@ -11,10 +11,9 @@ defmodule Openflow.Action.NxConjunction do
alias __MODULE__
def new(options) do
clause = Keyword.get(options, :clause, 0)
n_clauses = Keyword.get(options, :n_clauses, 0)
id = Keyword.get(options, :id, 0)
%NxConjunction{clause: clause, n_clauses: n_clauses, id: id}
%NxConjunction{clause: options[:clause] || 0,
n_clauses: options[:n_clauses] || 0,
id: options[:id] || 0}
end
def to_binary(%NxConjunction{clause: clause, n_clauses: n_clauses, id: id}) do

View file

@ -18,24 +18,14 @@ defmodule Openflow.Action.NxConntrack do
alias __MODULE__
def new(options \\ []) do
flags = Keyword.get(options, :flags, [])
zone_src = Keyword.get(options, :zone_src)
zone_ofs = Keyword.get(options, :zone_offset)
zone_n_bits = Keyword.get(options, :zone_n_bits)
zone_imm = Keyword.get(options, :zone_imm, 0)
recirc_table = Keyword.get(options, :recirc_table, 255)
alg = Keyword.get(options, :alg, 0)
exec = Keyword.get(options, :exec, [])
%NxConntrack{
flags: flags,
zone_src: zone_src,
zone_imm: zone_imm,
zone_offset: zone_ofs,
zone_n_bits: zone_n_bits,
recirc_table: recirc_table,
alg: alg,
exec: exec
}
%NxConntrack{flags: options[:flags] || [],
zone_src: options[:zone_src],
zone_imm: options[:zone_imm] || 0,
zone_offset: options[:zone_offset],
zone_n_bits: options[:zone_n_bits],
recirc_table: options[:recirc_table] || 255,
alg: options[:alg] || 0,
exec: options[:exec] || []}
end
def to_binary(%NxConntrack{

View file

@ -11,10 +11,9 @@ defmodule Openflow.Action.NxController do
alias __MODULE__
def new(options) do
max_len = Keyword.get(options, :max_len, :no_buffer)
controller_id = Keyword.get(options, :id, 0)
reason = Keyword.get(options, :reason, :action)
%NxController{max_len: max_len, id: controller_id, reason: reason}
%NxController{max_len: options[:max_len] || :no_buffer,
id: options[:id] || 0,
reason: options[:reason] || :action}
end
def to_binary(%NxController{max_len: max_len, id: controller_id, reason: reason}) do

View file

@ -21,16 +21,11 @@ defmodule Openflow.Action.NxController2 do
alias __MODULE__
def new(options) do
max_len = Keyword.get(options, :max_len, :no_buffer)
controller_id = Keyword.get(options, :id, 0)
reason = Keyword.get(options, :reason, :action)
userdata = Keyword.get(options, :userdata)
pause = Keyword.get(options, :pause, false)
%NxController2{max_len: max_len,
id: controller_id,
reason: reason,
userdata: userdata,
pause: pause}
%NxController2{max_len: options[:max_len] || :no_buffer,
id: options[:id] || 0,
reason: options[:reason] || :action,
userdata: options[:userdata],
pause: options[:pause] || false}
end
def to_binary(%NxController2{} = ctl) do

View file

@ -10,9 +10,8 @@ defmodule Openflow.Action.NxFinTimeout do
alias __MODULE__
def new(options) do
fin_idle = Keyword.get(options, :idle_timeout, 0)
fin_hard = Keyword.get(options, :hard_timeout, 0)
%NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard}
%NxFinTimeout{idle_timeout: options[:idle_timeout] || 0,
hard_timeout: options[:hard_timeout] || 0}
end
def to_binary(%NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard}) do

View file

@ -14,17 +14,13 @@ defmodule Openflow.Action.NxFlowSpecLoad do
alias __MODULE__
def new(options) do
src = Keyword.get(options, :src)
dst = Keyword.get(options, :dst)
src_ofs = Keyword.get(options, :src_offset, 0)
dst_ofs = Keyword.get(options, :dst_offset, 0)
default_n_bits = Openflow.Match.Field.n_bits_of(dst)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
%NxFlowSpecLoad{src: src,
dst: dst,
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs}
dst = options[:dst]
n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst)
%NxFlowSpecLoad{src: options[:src],
dst: dst,
n_bits: n_bits,
src_offset: options[:src_offset] || 0,
dst_offset: options[:dst_offset] || 0}
end
def to_binary(%NxFlowSpecLoad{} = fsm) do

View file

@ -14,17 +14,13 @@ defmodule Openflow.Action.NxFlowSpecMatch do
alias __MODULE__
def new(options) do
src = Keyword.get(options, :src)
dst = Keyword.get(options, :dst)
default_n_bits = Openflow.Match.Field.n_bits_of(dst)
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)
%NxFlowSpecMatch{src: src,
dst: dst,
n_bits: n_bits,
src_offset: src_ofs,
dst_offset: dst_ofs}
dst = options[:dst]
n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst)
%NxFlowSpecMatch{src: options[:src],
dst: dst,
n_bits: n_bits,
src_offset: options[:src_offset] || 0,
dst_offset: options[:dst_offset] || 0}
end
def to_binary(%NxFlowSpecMatch{} = fsm) do

View file

@ -11,13 +11,11 @@ defmodule Openflow.Action.NxFlowSpecOutput do
alias __MODULE__
def new(options) do
src = Keyword.get(options, :src)
src_ofs = Keyword.get(options, :src_offset, 0)
default_n_bits = Openflow.Match.Field.n_bits_of(src)
n_bits = Keyword.get(options, :n_bits, default_n_bits)
%NxFlowSpecOutput{n_bits: n_bits,
src: src,
src_offset: src_ofs}
src = options[:src]
n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(src)
%NxFlowSpecOutput{n_bits: n_bits,
src: src,
src_offset: options[:src_offset] || 0}
end
def to_binary(%NxFlowSpecOutput{n_bits: n_bits,

View file

@ -17,24 +17,15 @@ defmodule Openflow.Action.NxLearn do
alias __MODULE__
def new(options) do
idle = Keyword.get(options, :idle_timeout, 0)
hard = Keyword.get(options, :hard_timeout, 0)
prio = Keyword.get(options, :priority, 0)
cookie = Keyword.get(options, :cookie, 0)
flags = Keyword.get(options, :flags, [])
table_id = Keyword.get(options, :table_id, 0)
fin_idle = Keyword.get(options, :fin_idle_timeout, 0)
fin_hard = Keyword.get(options, :fin_hard_timeout, 0)
flow_specs = Keyword.get(options, :flow_specs, [])
%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}
%NxLearn{idle_timeout: options[:idle_timeout] || 0,
hard_timeout: options[:hard_timeout] || 0,
priority: options[:priority] || 0,
cookie: options[:cookie] || 0,
flags: options[:flags] || [],
table_id: options[:table_id] || 0xff,
fin_idle_timeout: options[:fin_idle_timeout] || 0,
fin_hard_timeout: options[:fin_hard_timeout] || 0,
flow_specs: options[:flow_specs] || []}
end
def to_binary(%NxLearn{idle_timeout: idle,
@ -52,7 +43,7 @@ defmodule Openflow.Action.NxLearn do
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)
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)>>

View file

@ -20,30 +20,18 @@ defmodule Openflow.Action.NxLearn2 do
alias __MODULE__
def new(options) do
idle = Keyword.get(options, :idle_timeout, 0)
hard = Keyword.get(options, :hard_timeout, 0)
prio = Keyword.get(options, :priority, 0)
cookie = Keyword.get(options, :cookie, 0)
flags = Keyword.get(options, :flags, [])
table_id = Keyword.get(options, :table_id, 0)
fin_idle = Keyword.get(options, :fin_idle_timeout, 0)
fin_hard = Keyword.get(options, :fin_hard_timeout, 0)
flow_specs = Keyword.get(options, :flow_specs, [])
limit = Keyword.get(options, :limit, 0)
result_dst_offset = Keyword.get(options, :result_dst_offset, 0)
result_dst = Keyword.get(options, :result_dst)
%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_offset,
result_dst: result_dst,
flow_specs: flow_specs}
%NxLearn2{idle_timeout: options[:idle_timeout] || 0,
hard_timeout: options[:hard_timeout] || 0,
priority: options[:priority] || 0,
cookie: options[:cookie] || 0,
flags: options[:flags] || [],
table_id: options[:table_id] || 0xff,
fin_idle_timeout: options[:fin_idle_timeout] || 0,
fin_hard_timeout: options[:fin_hard_timeout] || 0,
limit: options[:limit] || 0,
result_dst_offset: options[:result_dst_offset] || 0,
result_dst: options[:result_dst],
flow_specs: options[:flow_specs] || []}
end
def to_binary(%NxLearn2{idle_timeout: idle,

View file

@ -25,7 +25,7 @@ defmodule Openflow.Action.SetField do
end
def read(<<25::16, _length::16, match_field_bin::bytes>>) do
<<_class::16, _field::7, _hm::1, flen::8, _rest::bytes>>= match_field_bin
<<_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>>
{[field|_], _rest} = Openflow.Match.read(match_bin)

View file

@ -316,8 +316,9 @@ defmodule Openflow.Enums do
],
experimenter_oxm_vendors: [
nicira_ext_match: 0x00002320,
onf_ext_match: 0x4f4e4600
nicira_ext_match: 0x00002320,
hp_ext_match: 0x00002428,
onf_ext_match: 0x4f4e4600
],
match_type: [
@ -618,6 +619,24 @@ defmodule Openflow.Enums do
nsh_c4: 9
],
hp_ext_match: [
hp_udp_src_port_range: 0,
hp_udp_dst_port_range: 1,
hp_tcp_src_port_range: 2,
hp_tcp_dst_port_range: 3,
hp_tcp_flags: 4,
hp_custom_1: 5,
hp_custom_2: 6,
hp_custom_3: 7,
hp_custom_4: 8
],
hp_custom_match_type: [
l2_start: 1,
l3_start: 2,
l4_start: 3
],
onf_ext_match: [
onf_tcp_flags: 42,
onf_actset_output: 43,

View file

@ -18,7 +18,7 @@ defmodule Openflow.Match do
def read(binary) do
<<1::16, no_pad_len::16, binary1::binary>> = binary
padding_length = @match_size - rem(no_pad_len, 8)
padding_length = Openflow.Utils.pad_length(no_pad_len, 8)
match_field_len = no_pad_len - @header_size
<<match_fields::size(match_field_len)-binary, _::size(padding_length)-unit(8), rest::bitstring>> = binary1
{decode_fields(match_fields, []), rest}
@ -58,7 +58,7 @@ defmodule Openflow.Match do
def header_size(<<_oxm_class_int::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _::bytes>>),
do: 4
def header_size(<<0xffff::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _exp_int::32, _::bytes>>),
def header_size(<<0xffff::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _exp_int::32, _::bytes>>),
do: 8
# private functions

View file

@ -275,6 +275,17 @@ defmodule Openflow.Match.Field do
def vendor_of(:nsh_c3), do: :nicira_ext_match
def vendor_of(:nsh_c4), do: :nicira_ext_match
# HP Ext Match
def vendor_of(:hp_udp_src_port_range), do: :hp_ext_match
def vendor_of(:hp_udp_dst_port_range), do: :hp_ext_match
def vendor_of(:hp_tcp_src_port_range), do: :hp_ext_match
def vendor_of(:hp_tcp_dst_port_range), do: :hp_ext_match
def vendor_of(:hp_tcp_flags), do: :hp_ext_match
def vendor_of(:hp_custom_1), do: :hp_ext_match
def vendor_of(:hp_custom_2), do: :hp_ext_match
def vendor_of(:hp_custom_3), do: :hp_ext_match
def vendor_of(:hp_custom_4), do: :hp_ext_match
# ONF Ext Match
def vendor_of(:onf_tcp_flags), do: :onf_ext_match
def vendor_of(:onf_actset_output), do: :onf_ext_match
@ -518,6 +529,17 @@ defmodule Openflow.Match.Field do
def format_of(:nsh_c3), do: {:be32, :decimal}
def format_of(:nsh_c4), do: {:be32, :decimal}
# HP Ext Match
def format_of(:hp_udp_src_port_range), do: {:be32, :decimal}
def format_of(:hp_udp_dst_port_range), do: {:be32, :decimal}
def format_of(:hp_tcp_src_port_range), do: {:be32, :decimal}
def format_of(:hp_tcp_dst_port_range), do: {:be32, :decimal}
def format_of(:hp_tcp_flags), do: {:be16, :tcp_flags}
def format_of(:hp_custom_1), do: {:dynamic, :bytes}
def format_of(:hp_custom_2), do: {:dynamic, :bytes}
def format_of(:hp_custom_3), do: {:dynamic, :bytes}
def format_of(:hp_custom_4), do: {:dynamic, :bytes}
# ONF Ext Match
def format_of(:onf_tcp_flags), do: {:be16, :tcp_flags}
def format_of(:onf_actset_output), do: {:be32, :openflow13_port}

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Aggregate.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
packet_count: 0,
byte_count: 0,

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Desc.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
mfr_desc: "",
hw_desc: "",

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Flow.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
flows: []
)
@ -19,6 +20,16 @@ defmodule Openflow.Multipart.Flow.Reply do
flows = Openflow.Multipart.FlowStats.read(flows_bin)
%Reply{flows: flows}
end
def append_body(%Reply{flows: flows} = message, %Reply{flags: [:more], flows: continue}) do
%{message|flows: [continue|flows]}
end
def append_body(%Reply{flows: flows} = message, %Reply{flags: [], flows: continue}) do
new_flows = [continue|flows]
|> Enum.reverse
|> List.flatten
%{message|flows: new_flows}
end
end
defmodule Openflow.Multipart.FlowStats do

View file

@ -16,13 +16,13 @@ defmodule Openflow.Multipart.Flow.Request do
def ofp_type, do: 18
def new(options) do
def new(options \\ []) do
table_id = Keyword.get(options, :table_id, :all)
out_port = Keyword.get(options, :out_port, :any)
out_group = Keyword.get(options, :out_group, :any)
cookie = Keyword.get(options, :cookie, 0)
cookie_mask = Keyword.get(options, :cookie, 0)
match = Keyword.get(options, :match, [])
match = Keyword.get(options, :match, Openflow.Match.new)
%Request{table_id: table_id,
out_port: out_port,
out_group: out_group,

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Group.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
groups: []
)
@ -19,6 +20,16 @@ defmodule Openflow.Multipart.Group.Reply do
groups = Openflow.Multipart.Group.read(groups_bin)
%Reply{groups: groups}
end
def append_body(%Reply{groups: groups} = message, %Reply{flags: [:more], groups: continue}) do
%{message|groups: [continue|groups]}
end
def append_body(%Reply{groups: groups} = message, %Reply{flags: [], groups: continue}) do
new_groups = [continue|groups]
|> Enum.reverse
|> List.flatten
%{message|groups: new_groups}
end
end
defmodule Openflow.Multipart.Group do

View file

@ -19,6 +19,16 @@ defmodule Openflow.Multipart.GroupDesc.Reply do
groups = Openflow.Multipart.GroupDescStats.read(groups_bin)
%Reply{groups: groups}
end
def append_body(%Reply{groups: groups} = message, %Reply{flags: [:more], groups: continue}) do
%{message|groups: [continue|groups]}
end
def append_body(%Reply{groups: groups} = message, %Reply{flags: [], groups: continue}) do
new_groups = [continue|groups]
|> Enum.reverse
|> List.flatten
%{message|groups: new_groups}
end
end
defmodule Openflow.Multipart.GroupDescStats do

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.GroupFeatures.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
types: 0,
capabilities: [],

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Meter.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
meters: []
)
@ -15,6 +16,16 @@ defmodule Openflow.Multipart.Meter.Reply do
meters = Openflow.Multipart.Meter.read(meters_bin)
%Reply{meters: meters}
end
def append_body(%Reply{meters: meters} = message, %Reply{flags: [:more], meters: continue}) do
%{message|meters: [continue|meters]}
end
def append_body(%Reply{meters: meters} = message, %Reply{flags: [], meters: continue}) do
new_meters = [continue|meters]
|> Enum.reverse
|> List.flatten
%{message|meters: new_meters}
end
end
defmodule Openflow.Multipart.Meter do

View file

@ -0,0 +1,33 @@
defmodule Openflow.Multipart.PortDesc.Reply do
defstruct(
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
ports: []
)
alias __MODULE__
def ofp_type, do: 18
def new(ports \\ []) do
%Reply{ports: ports}
end
def read(<<ports_bin::bytes>>) do
ports = for (<<port_bin::64-bytes <- ports_bin>>), do: Openflow.Port.read(port_bin)
%Reply{ports: Enum.reverse(ports)}
end
def append_body(%Reply{ports: ports} = message, %Reply{flags: [:more], ports: continue}) do
%{message|ports: [continue|ports]}
end
def append_body(%Reply{ports: ports} = message, %Reply{flags: [], ports: continue}) do
new_ports = [continue|ports]
|> Enum.reverse
|> List.flatten
%{message|ports: new_ports}
end
end

View file

@ -0,0 +1,24 @@
defmodule Openflow.Multipart.PortDesc.Request do
defstruct(
version: 4,
xid: 0,
datapath_id: nil, # virtual field
flags: []
)
alias __MODULE__
def ofp_type, do: 18
def new do
%Request{}
end
def read("") do
%Request{}
end
def to_binary(%Request{} = msg) do
Openflow.Multipart.Request.header(msg)
end
end

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.PortStats.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
ports: []
)
@ -19,6 +20,16 @@ defmodule Openflow.Multipart.PortStats.Reply do
ports = Openflow.Multipart.PortStats.read(ports_bin)
%Reply{ports: ports}
end
def append_body(%Reply{ports: ports} = message, %Reply{flags: [:more], ports: continue}) do
%{message|ports: [continue|ports]}
end
def append_body(%Reply{ports: ports} = message, %Reply{flags: [], ports: continue}) do
new_ports = [continue|ports]
|> Enum.reverse
|> List.flatten
%{message|ports: new_ports}
end
end
defmodule Openflow.Multipart.PortStats do

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Queue.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
queues: []
)
@ -19,6 +20,16 @@ defmodule Openflow.Multipart.Queue.Reply do
queues = Openflow.Multipart.Queue.read(queues_bin)
%Reply{queues: queues}
end
def append_body(%Reply{queues: queues} = message, %Reply{flags: [:more], queues: continue}) do
%{message|queues: [continue|queues]}
end
def append_body(%Reply{queues: queues} = message, %Reply{flags: [], queues: continue}) do
new_queues = [continue|queues]
|> Enum.reverse
|> List.flatten
%{message|queues: new_queues}
end
end
defmodule Openflow.Multipart.Queue do

View file

@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Table.Reply do
version: 4,
xid: 0,
datapath_id: nil, # virtual field
aux_id: nil,
flags: [],
tables: []
)
@ -15,6 +16,16 @@ defmodule Openflow.Multipart.Table.Reply do
tables = Openflow.Multipart.TableStats.read(tables_bin)
%Reply{tables: tables}
end
def append_body(%Reply{tables: tables} = message, %Reply{flags: [:more], tables: continue}) do
%{message|tables: [continue|tables]}
end
def append_body(%Reply{tables: tables} = message, %Reply{flags: [], tables: continue}) do
new_tables = [continue|tables]
|> Enum.reverse
|> List.flatten
%{message|tables: new_tables}
end
end
defmodule Openflow.Multipart.TableStats do

View file

@ -13,14 +13,13 @@ defmodule Openflow.NxPacketIn2 do
reason: nil,
metadata: nil,
userdata: nil,
continuation: nil,
# continuation properties:
continuation_bridge: nil,
continuation_stack: nil,
continuation_conntracked: nil,
continuation_bridge: "",
continuation_stack: [],
continuation_conntracked: false,
continuation_table_id: nil,
continuation_cookie: nil,
continuation_actions: nil,
continuation_actions: [],
continuation_action_set: nil
)
@ -29,6 +28,138 @@ defmodule Openflow.NxPacketIn2 do
@experimenter 0x00002320
@nx_type 30
@packet 0
@full_len 1
@buffer_id 2
@table_id 3
@cookie 4
@reason 5
@metadata 6
@userdata 7
@continuation 8
@nxcpt_bridge 0x8000
@nxcpt_stack 0x8001
@nxcpt_mirrors 0x8002
@nxcpt_conntracked 0x8003
@nxcpt_table_id 0x8004
@nxcpt_cookie 0x8005
@nxcpt_actions 0x8006
@nxcpt_action_set 0x8007
@prop_header_length 4
def ofp_type, do: 4
def read(<<@experimenter::32, @nx_type::32, props_bin::bytes>>) do
%NxPacketIn2{}
|> decode_props(props_bin)
end
## private functions
defp decode_props(pktin, ""), do: pktin
defp decode_props(pktin, <<@packet::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
packet_length = length - @prop_header_length
<<packet::size(packet_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_props(%{pktin|packet: packet}, rest)
end
defp decode_props(pktin, <<@full_len::16, _length::16, full_len::32, rest::bytes>>) do
decode_props(%{pktin|full_len: full_len}, rest)
end
defp decode_props(pktin, <<@buffer_id::16, _length::16, buffer_id::32, rest::bytes>>) do
decode_props(%{pktin|buffer_id: buffer_id}, rest)
end
defp decode_props(pktin, <<@table_id::16, _length::16, table_id::8, _::24, rest::bytes>>) do
decode_props(%{pktin|table_id: table_id}, rest)
end
defp decode_props(pktin, <<@cookie::16, _length::16, _::32, cookie::64, rest::bytes>>) do
decode_props(%{pktin|cookie: cookie}, rest)
end
defp decode_props(pktin, <<@reason::16, _length::16, reason_int::8, _::24, rest::bytes>>) do
reason = Openflow.Enums.to_atom(reason_int, :packet_in_reason)
decode_props(%{pktin|reason: reason}, rest)
end
defp decode_props(pktin, <<@metadata::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
match_field_length = length - @prop_header_length
<<match_fields_bin::size(match_field_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
match_len = 4 + byte_size(match_fields_bin)
padding = Openflow.Utils.pad_length(match_len, 8)
match_bin = (<<1::16, match_len::16, match_fields_bin::bytes, 0::size(padding)-unit(8)>>)
{fields, _rest} = Openflow.Match.read(match_bin)
decode_props(%{pktin|metadata: fields}, rest)
end
defp decode_props(pktin, <<@userdata::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
userdata_length = length - @prop_header_length
<<userdata::size(userdata_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_props(%{pktin|userdata: userdata}, rest)
end
defp decode_props(pktin, <<@continuation::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
data_length = length - @prop_header_length - 4
<<_pad::32, data::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
pktin
|> decode_continuations(data)
|> decode_props(rest)
end
defp decode_props(pktin, <<_::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
data_length = length - @prop_header_length
<<_data::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_props(pktin, rest)
end
defp decode_continuations(pktin, ""), do: pktin
defp decode_continuations(pktin, <<@nxcpt_bridge::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
data_length = length - @prop_header_length
<<bridge::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_continuations(%{pktin|continuation_bridge: bridge}, rest)
end
defp decode_continuations(pktin, <<@nxcpt_stack::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
data_length = (length - @prop_header_length) * 8
<<stack::size(data_length), _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_continuations(%{pktin|continuation_stack: pktin.continuation_stack ++ [stack]}, rest)
end
defp decode_continuations(pktin, <<@nxcpt_mirrors::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
data_length = length - @prop_header_length
<<mirrors::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_continuations(%{pktin|continuation_mirrors: mirrors}, rest)
end
defp decode_continuations(pktin, <<@nxcpt_conntracked::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
data_length = length - @prop_header_length
<<_::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_continuations(%{pktin|continuation_conntracked: true}, rest)
end
defp decode_continuations(pktin, <<@nxcpt_table_id::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
<<table_id::8, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_continuations(%{pktin|continuation_table_id: table_id}, rest)
end
defp decode_continuations(pktin, <<@nxcpt_cookie::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
<<cookie::64, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_continuations(%{pktin|continuation_cookie: cookie}, rest)
end
defp decode_continuations(pktin, <<@nxcpt_actions::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
data_length = length - @prop_header_length - 4
<<_pad::32, actions::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_continuations(%{pktin|continuation_actions: Openflow.Action.read(actions)}, rest)
end
defp decode_continuations(pktin, <<@nxcpt_action_set::16, length::16, tail::bytes>>) do
pad_length = Openflow.Utils.pad_length(length, 8)
data_length = length - @prop_header_length
<<action_set::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail
decode_continuations(%{pktin|continuation_action_set: action_set}, rest)
end
defp decode_continuations(pktin, _) do
decode_continuations(pktin, "")
end
end