diff --git a/scripts/base/frameworks/openflow/__load__.bro b/scripts/base/frameworks/openflow/__load__.bro index 64d6840c4d..bd9128b5aa 100644 --- a/scripts/base/frameworks/openflow/__load__.bro +++ b/scripts/base/frameworks/openflow/__load__.bro @@ -2,3 +2,12 @@ @load ./types @load ./main @load ./plugins + +# The cluster framework must be loaded first. +@load base/frameworks/cluster + +@if ( Cluster::is_enabled() ) +@load ./cluster +@else +@load ./non-cluster +@endif diff --git a/scripts/base/frameworks/openflow/cluster.bro b/scripts/base/frameworks/openflow/cluster.bro new file mode 100644 index 0000000000..6f08e6839a --- /dev/null +++ b/scripts/base/frameworks/openflow/cluster.bro @@ -0,0 +1,94 @@ +@load ./main +@load base/frameworks/cluster + +module OpenFlow; + +export { + ## This is the event used to transport flow_mod messages to the manager. + global cluster_flow_mod: event(name: string, match: ofp_match, flow_mod: ofp_flow_mod); + + ## This is the event used to transport flow_clear messages to the manager. + global cluster_flow_clear: event(name: string); +} + +## Workers need ability to forward commands to manager. +redef Cluster::worker2manager_events += /OpenFlow::cluster_flow_(mod|clear)/; + +global name_to_controller: table[string] of Controller; + +# the flow_mod function wrapper +function flow_mod(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool + { + if ( ! controller?$flow_mod ) + return F; + + if ( Cluster::local_node_type() == Cluster::MANAGER ) + return controller$flow_mod(controller$state, match, flow_mod); + else + event OpenFlow::cluster_flow_mod(controller$state$_name, match, flow_mod); + + return T; + } + +function flow_clear(controller: Controller): bool + { + if ( ! controller?$flow_clear ) + return F; + + if ( Cluster::local_node_type() == Cluster::MANAGER ) + return controller$flow_clear(controller$state); + else + event OpenFlow::cluster_flow_clear(controller$state$_name); + + return T; + } + +@if ( Cluster::local_node_type() == Cluster::MANAGER ) +event OpenFlow::cluster_flow_mod(name: string, match: ofp_match, flow_mod: ofp_flow_mod) + { + if ( name !in name_to_controller ) + { + Reporter::error(fmt("OpenFlow controller %s not found in mapping on master", name)); + return; + } + + local c = name_to_controller[name]; + if ( c?$flow_mod ) + c$flow_mod(c$state, match, flow_mod); + } + +event OpenFlow::cluster_flow_clear(name: string) + { + if ( name !in name_to_controller ) + { + Reporter::error(fmt("OpenFlow controller %s not found in mapping on master", name)); + return; + } + + local c = name_to_controller[name]; + + if ( c?$flow_clear ) + c$flow_clear(c$state); + } +@endif + +function register_controller(tpe: OpenFlow::Plugin, name: string, controller: Controller) + { + controller$state$_name = cat(tpe, name); + controller$state$_plugin = tpe; + + # we only run the init functions on the manager. + if ( Cluster::local_node_type() != Cluster::MANAGER ) + return; + + if ( controller$state$_name in name_to_controller ) + { + Reporter::error("OpenFlow Controller %s was already registered. Ignored duplicate registration"); + return; + } + + name_to_controller[controller$state$_name] = controller; + + if ( controller?$init ) + controller$init(controller$state); + } diff --git a/scripts/base/frameworks/openflow/main.bro b/scripts/base/frameworks/openflow/main.bro index 7158cf3812..139d932e0d 100644 --- a/scripts/base/frameworks/openflow/main.bro +++ b/scripts/base/frameworks/openflow/main.bro @@ -80,7 +80,7 @@ export { global match_conn: function(id: conn_id, reverse: bool &default=F): ofp_match; # ### - # ### Low-level functions for cookie handling. + # ### Low-level functions for cookie handling and plugin registration. # ### ## Function to get the unique id out of a given cookie. @@ -103,26 +103,18 @@ export { ## ## Returns: The cookie group id. global generate_cookie: function(cookie: count &default=0): count; + + ## Function to register a controller instance. This function + ## is called automatically by the plugin _new functions. + ## + ## tpe: type of this plugin + ## + ## name: unique name of this controller instance. + ## + ## controller: The controller to register + global register_controller: function(tpe: OpenFlow::Plugin, name: string, controller: Controller); } - -# the flow_mod function wrapper -function flow_mod(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool - { - if ( controller?$flow_mod ) - return controller$flow_mod(controller$state, match, flow_mod); - else - return F; - } - -function flow_clear(controller: Controller): bool - { - if ( controller?$flow_clear ) - return controller$flow_clear(controller$state); - else - return F; - } - function match_conn(id: conn_id, reverse: bool &default=F): ofp_match { local dl_type = ETH_IPv4; diff --git a/scripts/base/frameworks/openflow/non-cluster.bro b/scripts/base/frameworks/openflow/non-cluster.bro new file mode 100644 index 0000000000..efadbe56b6 --- /dev/null +++ b/scripts/base/frameworks/openflow/non-cluster.bro @@ -0,0 +1,29 @@ +@load ./main + +module OpenFlow; + +# the flow_mod function wrapper +function flow_mod(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool + { + if ( controller?$flow_mod ) + return controller$flow_mod(controller$state, match, flow_mod); + else + return F; + } + +function flow_clear(controller: Controller): bool + { + if ( controller?$flow_clear ) + return controller$flow_clear(controller$state); + else + return F; + } + +function register_controller(tpe: OpenFlow::Plugin, name: string, controller: Controller) + { + controller$state$_name = cat(tpe, name); + controller$state$_plugin = tpe; + + if ( controller?$init ) + controller$init(controller$state); + } diff --git a/scripts/base/frameworks/openflow/plugins/broker.bro b/scripts/base/frameworks/openflow/plugins/broker.bro index 3b721ad5a0..55505fd3eb 100644 --- a/scripts/base/frameworks/openflow/plugins/broker.bro +++ b/scripts/base/frameworks/openflow/plugins/broker.bro @@ -19,7 +19,7 @@ export { ## dpid: OpenFlow switch datapath id. ## ## Returns: OpenFlow::Controller record - global broker_new: function(host: addr, host_port: port, topic: string, dpid: count): OpenFlow::Controller; + global broker_new: function(name: string, host: addr, host_port: port, topic: string, dpid: count): OpenFlow::Controller; redef record ControllerState += { ## Controller ip. @@ -55,14 +55,21 @@ function broker_flow_clear_fun(state: OpenFlow::ControllerState): bool return T; } -# broker controller constructor -function broker_new(host: addr, host_port: port, topic: string, dpid: count): OpenFlow::Controller +function broker_init(state: OpenFlow::ControllerState) { BrokerComm::enable(); - BrokerComm::connect(cat(host), host_port, 1sec); - BrokerComm::subscribe_to_events(topic); # openflow success and failure events are directly sent back via the other plugin via broker. - - return [$state=[$broker_host=host, $broker_port=host_port, $broker_dpid=dpid, $broker_topic=topic, $_plugin=OpenFlow::BROKER], - $flow_mod=broker_flow_mod_fun, $flow_clear=broker_flow_clear_fun, $describe=broker_describe, $supports_flow_removed=T]; + BrokerComm::connect(cat(state$broker_host), state$broker_port, 1sec); + BrokerComm::subscribe_to_events(state$broker_topic); # openflow success and failure events are directly sent back via the other plugin via broker. + } + +# broker controller constructor +function broker_new(name: string, host: addr, host_port: port, topic: string, dpid: count): OpenFlow::Controller + { + local c = OpenFlow::Controller($state=OpenFlow::ControllerState($broker_host=host, $broker_port=host_port, $broker_dpid=dpid, $broker_topic=topic), + $flow_mod=broker_flow_mod_fun, $flow_clear=broker_flow_clear_fun, $describe=broker_describe, $supports_flow_removed=T, $init=broker_init); + + register_controller(OpenFlow::BROKER, name, c); + + return c; } diff --git a/scripts/base/frameworks/openflow/plugins/log.bro b/scripts/base/frameworks/openflow/plugins/log.bro index a7009e474a..3780dfd3c0 100644 --- a/scripts/base/frameworks/openflow/plugins/log.bro +++ b/scripts/base/frameworks/openflow/plugins/log.bro @@ -67,6 +67,10 @@ function log_describe(state: ControllerState): string function log_new(dpid: count, success_event: bool &default=T): OpenFlow::Controller { - return [$state=[$log_dpid=dpid, $log_success_event=success_event, $_plugin=OpenFlow::LOG], - $flow_mod=log_flow_mod, $flow_clear=ryu_flow_clear, $describe=log_describe, $supports_flow_removed=F]; + local c = OpenFlow::Controller($state=OpenFlow::ControllerState($log_dpid=dpid, $log_success_event=success_event), + $flow_mod=log_flow_mod, $flow_clear=ryu_flow_clear, $describe=log_describe, $supports_flow_removed=F); + + register_controller(OpenFlow::OFLOG, cat(dpid), c); + + return c; } diff --git a/scripts/base/frameworks/openflow/plugins/ryu.bro b/scripts/base/frameworks/openflow/plugins/ryu.bro index 490d10ac07..8d2d59e5e5 100644 --- a/scripts/base/frameworks/openflow/plugins/ryu.bro +++ b/scripts/base/frameworks/openflow/plugins/ryu.bro @@ -179,6 +179,10 @@ function ryu_describe(state: ControllerState): string # Ryu controller constructor function ryu_new(host: addr, host_port: count, dpid: count): OpenFlow::Controller { - return [$state=[$ryu_host=host, $ryu_port=host_port, $ryu_dpid=dpid, $_plugin=OpenFlow::RYU], - $flow_mod=ryu_flow_mod, $flow_clear=ryu_flow_clear, $describe=ryu_describe, $supports_flow_removed=F]; + local c = OpenFlow::Controller($state=OpenFlow::ControllerState($ryu_host=host, $ryu_port=host_port, $ryu_dpid=dpid), + $flow_mod=ryu_flow_mod, $flow_clear=ryu_flow_clear, $describe=ryu_describe, $supports_flow_removed=F); + + register_controller(OpenFlow::RYU, cat(host,host_port,dpid), c); + + return c; } diff --git a/scripts/base/frameworks/openflow/types.bro b/scripts/base/frameworks/openflow/types.bro index e35b675680..9faed67f9c 100644 --- a/scripts/base/frameworks/openflow/types.bro +++ b/scripts/base/frameworks/openflow/types.bro @@ -13,8 +13,10 @@ export { ## Can be redefined by plugins to ## add state. type ControllerState: record { - ## Internally set to the plugin used. - _plugin: Plugin; + ## Internally set to the type of plugin used. + _plugin: Plugin &optional; + ## Internally set to the unique name of the controller. + _name: string &optional; } &redef; ## Openflow match definition. @@ -143,6 +145,8 @@ export { supports_flow_removed: bool; ## function that describes the controller. Has to be implemented. describe: function(state: ControllerState): string; + ## one-time initialization function. + init: function (state: ControllerState) &optional; ## flow_mod function flow_mod: function(state: ControllerState, match: ofp_match, flow_mod: ofp_flow_mod): bool &optional; ## flow_clear function diff --git a/scripts/base/frameworks/pacf/plugin.bro b/scripts/base/frameworks/pacf/plugin.bro index cc1b1f0ee6..3e89ebd25d 100644 --- a/scripts/base/frameworks/pacf/plugin.bro +++ b/scripts/base/frameworks/pacf/plugin.bro @@ -39,7 +39,7 @@ export { ## framework will manage rule expiration. can_expire: bool; - # One-time initialization functionl called when plugin gets registered, and + # One-time initialization function called when plugin gets registered, and # before any other methods are called. init: function(state: PluginState) &optional; diff --git a/testing/btest/Baseline/scripts.base.frameworks.openflow.log-cluster/manager-1.openflow.log b/testing/btest/Baseline/scripts.base.frameworks.openflow.log-cluster/manager-1.openflow.log new file mode 100644 index 0000000000..9d5f2f39c8 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.openflow.log-cluster/manager-1.openflow.log @@ -0,0 +1,11 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path openflow +#open 2015-05-26-20-51-55 +#fields ts dpid match.in_port match.dl_src match.dl_dst match.dl_vlan match.dl_vlan_pcp match.dl_type match.nw_tos match.nw_proto match.nw_src match.nw_dst match.tp_src match.tp_dst flow_mod.cookie flow_mod.table_id flow_mod.command flow_mod.idle_timeout flow_mod.hard_timeout flow_mod.priority flow_mod.out_port flow_mod.out_group flow_mod.flags flow_mod.actions.out_ports flow_mod.actions.vlan_vid flow_mod.actions.vlan_pcp flow_mod.actions.vlan_strip flow_mod.actions.dl_src flow_mod.actions.dl_dst flow_mod.actions.nw_tos flow_mod.actions.nw_src flow_mod.actions.nw_dst flow_mod.actions.tp_src flow_mod.actions.tp_dst +#types time count count string string count count count count count subnet subnet count count count count enum count count count count count count vector[count] count count bool string string count addr addr count count +1432673515.912626 42 - - - - - 2048 - 6 10.10.1.4/32 74.53.140.153/32 1470 25 42 - OpenFlow::OFPFC_ADD 30 0 5 - - 0 (empty) - - F - - - - - - - +1432673515.912626 42 - - - - - 2048 - 6 74.53.140.153/32 10.10.1.4/32 25 25 42 - OpenFlow::OFPFC_ADD 30 0 5 - - 0 (empty) - - F - - - - - - - +#close 2015-05-26-20-52-05 diff --git a/testing/btest/scripts/base/frameworks/openflow/broker-basic.bro b/testing/btest/scripts/base/frameworks/openflow/broker-basic.bro index c8e5c03667..6f75f82081 100644 --- a/testing/btest/scripts/base/frameworks/openflow/broker-basic.bro +++ b/testing/btest/scripts/base/frameworks/openflow/broker-basic.bro @@ -20,7 +20,7 @@ global of_controller: OpenFlow::Controller; event bro_init() { suspend_processing(); - of_controller = OpenFlow::broker_new(127.0.0.1, broker_port, "bro/event/openflow", 42); + of_controller = OpenFlow::broker_new("broker1", 127.0.0.1, broker_port, "bro/event/openflow", 42); } event BrokerComm::outgoing_connection_established(peer_address: string, diff --git a/testing/btest/scripts/base/frameworks/openflow/log-cluster.bro b/testing/btest/scripts/base/frameworks/openflow/log-cluster.bro new file mode 100644 index 0000000000..f015e20875 --- /dev/null +++ b/testing/btest/scripts/base/frameworks/openflow/log-cluster.bro @@ -0,0 +1,54 @@ +# @TEST-SERIALIZE: comm +# +# @TEST-EXEC: btest-bg-run manager-1 "cp ../cluster-layout.bro . && CLUSTER_NODE=manager-1 bro %INPUT" +# @TEST-EXEC: sleep 1 +# @TEST-EXEC: btest-bg-run worker-1 "cp ../cluster-layout.bro . && CLUSTER_NODE=worker-1 bro --pseudo-realtime -C -r $TRACES/smtp.trace %INPUT" +# @TEST-EXEC: btest-bg-wait 20 +# @TEST-EXEC: btest-diff manager-1/openflow.log + +@TEST-START-FILE cluster-layout.bro +redef Cluster::nodes = { + ["manager-1"] = [$node_type=Cluster::MANAGER, $ip=127.0.0.1, $p=37757/tcp, $workers=set("worker-1", "worker-2")], + ["worker-1"] = [$node_type=Cluster::WORKER, $ip=127.0.0.1, $p=37760/tcp, $manager="manager-1", $interface="eth0"], +}; +@TEST-END-FILE + +redef Log::default_rotation_interval = 0secs; +#redef exit_only_after_terminate = T; + +@load base/protocols/conn +@load base/frameworks/openflow + +global of_controller: OpenFlow::Controller; + +event bro_init() + { + of_controller = OpenFlow::log_new(42); + #OpenFlow::flow_mod(of_controller, [], [$cookie=1, $command=OpenFlow::OFPFC_ADD, $actions=[$out_ports=vector(3, 7)]]); + } + +event connection_established(c: connection) + { + print "conn established"; + local match = OpenFlow::match_conn(c$id); + local match_rev = OpenFlow::match_conn(c$id, T); + + local flow_mod: OpenFlow::ofp_flow_mod = [ + $cookie=42, + $command=OpenFlow::OFPFC_ADD, + $idle_timeout=30, + $priority=5 + ]; + + OpenFlow::flow_mod(of_controller, match, flow_mod); + OpenFlow::flow_mod(of_controller, match_rev, flow_mod); + } + +event terminate_me() { + terminate(); +} + +event remote_connection_closed(p: event_peer) { + schedule 1sec { terminate_me() }; +} +