make openflow framework work in clusters.

This commit is contained in:
Johanna Amann 2015-05-26 13:55:16 -07:00
parent 0a49b8cdf6
commit f2be226a5a
12 changed files with 243 additions and 35 deletions

View file

@ -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

View file

@ -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);
}

View file

@ -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,24 +103,16 @@ export {
##
## Returns: The cookie group id.
global generate_cookie: function(cookie: count &default=0): count;
}
# 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 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);
}
function match_conn(id: conn_id, reverse: bool &default=F): ofp_match

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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,

View file

@ -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() };
}