diff --git a/examples/heckle/config/config.exs b/examples/heckle/config/config.exs index abcf6ee..0b8ca46 100644 --- a/examples/heckle/config/config.exs +++ b/examples/heckle/config/config.exs @@ -5,15 +5,16 @@ use Mix.Config config :heckle, vlan_tagging: true, vlan_id: 123, - vlan_trunk: "1", - access_port1: "3", - access_port2: "4", - receiver_mac: "00000000000f", - receiver_ip: {8,8,8,8}, - sender_mac: "001122334455", - inside_local: {192,168,5,10}, - outside_local: {192,168,255,1}, - flow_pattern: :bum # :nat || :bum + 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, @@ -24,7 +25,7 @@ config :tres, callback_args: ["0002b05ada98d790"] config :logger, - level: :debug, + level: :info, format: "$date $time [$level] $metadata$message\n", metadata: [:application], handle_otp_reports: true diff --git a/examples/heckle/lib/heckle/controller.ex b/examples/heckle/lib/heckle/controller.ex index f4944e9..0eded9e 100644 --- a/examples/heckle/lib/heckle/controller.ex +++ b/examples/heckle/lib/heckle/controller.ex @@ -14,10 +14,12 @@ defmodule Heckle.Controller do conn_ref: nil, access_port1_name: nil, access_port2_name: nil, - trunk_port_name: nil, + trunk_port1_name: nil, + trunk_port2_name: nil, access_port1: nil, access_port2: nil, - trunk_port: nil, + trunk_port1: nil, + trunk_port2: nil, vlan_tagging: nil, vlan_id: nil, receiver_mac: nil, @@ -44,6 +46,8 @@ defmodule Heckle.Controller do end def handle_cast(:send_flows, state) do + :ok = print_flows(state) + state |> FlowPatterns.flows |> Enum.each(&send_flow_mod_add(state.dpid, &1)) @@ -67,13 +71,31 @@ defmodule Heckle.Controller do :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_port = Enum.find(ports, fn(port) -> port.name == state.trunk_port_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_port: trunk_port}} + {: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}):"<> @@ -83,11 +105,18 @@ defmodule Heckle.Controller do " 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 + def handle_info(info, state) do + :ok = info("Unhandled message: #{inspect(info)}") {:noreply, state} end @@ -101,7 +130,8 @@ defmodule Heckle.Controller do conn_ref: conn_ref, access_port1_name: config[:access_port1], access_port2_name: config[:access_port2], - trunk_port_name: config[:vlan_trunk], + 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], @@ -130,4 +160,42 @@ defmodule Heckle.Controller do 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 <>, do: integer_to_hex(int) + end + + defp integer_to_hex(int) do + case Integer.to_string(int, 16) do + <> -> <<48, d>> + dd -> dd + end + end end diff --git a/examples/heckle/lib/heckle/flow_patterns.ex b/examples/heckle/lib/heckle/flow_patterns.ex index 2a8cad2..1c2e036 100644 --- a/examples/heckle/lib/heckle/flow_patterns.ex +++ b/examples/heckle/lib/heckle/flow_patterns.ex @@ -19,7 +19,7 @@ defmodule Heckle.FlowPatterns do ApplyActions.new([ SetField.new({:eth_dst, state.receiver_mac}), SetField.new({:ipv4_src, state.outside_local}), - Output.new(state.trunk_port.number) + Output.new(state.trunk_port2.number) ])] ], [table_id: 1, @@ -29,12 +29,15 @@ defmodule Heckle.FlowPatterns do 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) ])] @@ -51,7 +54,7 @@ defmodule Heckle.FlowPatterns do ), instructions: [ ApplyActions.new([ - Openflow.Action.Output.new(state.trunk_port.number), + 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) @@ -67,23 +70,26 @@ defmodule Heckle.FlowPatterns do priority: 50, cookie: 0x1000000000000001, match: Match.new( - in_port: state.trunk_port.number, + in_port: state.trunk_port1.number, vlan_vid: state.vlan_id ), - instructions: [ - ApplyActions.new([ - PushVlan.new, - SetField.new({:vlan_vid, state.vlan_id}) - ]), - GotoTable.new(1) - ] + 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 + eth_src: state.sender_mac, ), instructions: [ ApplyActions.new([ @@ -98,7 +104,7 @@ defmodule Heckle.FlowPatterns do cookie: 0x1000000000000001, match: Match.new( in_port: state.access_port2.number, - eth_src: state.sender_mac + eth_src: state.sender_mac, ), instructions: [ ApplyActions.new([ @@ -108,6 +114,12 @@ defmodule Heckle.FlowPatterns do GotoTable.new(1) ] ], + [table_id: 0, + priority: 1, + match: Match.new, + cookie: 0x1000000000000001, + instructions: [ApplyActions.new(Output.new(:controller))] + ], ] end end diff --git a/examples/heckle/mix.exs b/examples/heckle/mix.exs index ad0528a..06e195a 100644 --- a/examples/heckle/mix.exs +++ b/examples/heckle/mix.exs @@ -17,6 +17,7 @@ defmodule Heckle.Mixfile do end defp deps do - [{:tres, path: @tres_path}] + [{:tres, path: @tres_path}, + {:pkt, github: "msantos/pkt", branch: "master"}] end end diff --git a/examples/heckle/mix.lock b/examples/heckle/mix.lock index eeade00..e337173 100644 --- a/examples/heckle/mix.lock +++ b/examples/heckle/mix.lock @@ -1,5 +1,6 @@ %{"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"]}}